From 4e36e800d494ecee0b4bc51efbb5ff3fdadc4506 Mon Sep 17 00:00:00 2001 From: Vladimir Eremin Date: Fri, 6 Mar 2026 21:19:41 +0300 Subject: [PATCH] feat: Add more thinned variants to Separator --- .../Separator/Separator.stories.tsx | 31 +++++++---- .../ui/src/components/Separator/Separator.tsx | 51 ++++++++++++++----- .../components/ThemeSwitch/ThemeSwitch.tsx | 2 +- .../layouts/IndexLayout/IndexLayout.tsx | 2 +- .../layouts/IndexLayout/IndexLayout.tsx | 2 +- 5 files changed, 61 insertions(+), 27 deletions(-) diff --git a/packages/ui/src/components/Separator/Separator.stories.tsx b/packages/ui/src/components/Separator/Separator.stories.tsx index 201339f..4c33640 100644 --- a/packages/ui/src/components/Separator/Separator.stories.tsx +++ b/packages/ui/src/components/Separator/Separator.stories.tsx @@ -11,7 +11,7 @@ import type { Meta, StoryObj } from '@storybook/react'; * - Single or double line variants inspired by newspaper layout traditions * - Multiple color options (black, outline, outline-light) with theme support * - Horizontal and vertical orientations - * - Optional thinned gradient fade effect at line ends (10% fade length) + * - Optional thinned gradient fade effect at line ends ('thinned', 'thinned-start', 'thinned-end', 'none') * - 2px stroke width following print design standards * - Semantic div elements with proper ARIA separator role for accessibility */ @@ -44,14 +44,15 @@ const meta: Meta = { description: 'Orientation of the separator', }, thinned: { - control: 'boolean', - description: 'Whether line ends should be thinned with gradient fade (10% fade length)', + control: 'select', + options: ['thinned', 'thinned-start', 'thinned-end', 'none'], + description: 'How line ends should be thinned with gradient fade', }, }, args: { type: 'single', direction: 'horizontal', - thinned: true, + thinned: 'thinned', }, }; @@ -191,7 +192,7 @@ export const VerticalDouble: Story = { */ export const NonThinned: Story = { args: { - thinned: false, + thinned: 'none', }, decorators: [ (Story) => ( @@ -251,19 +252,27 @@ export const AllTypes: Story = { }; /** - * Comparison between thinned and non-thinned effects. - * Demonstrates the 10% gradient fade at line ends. + * Comparison between different thinning effects. + * Demonstrates the gradient fade at line ends. */ export const ThinnedComparison: Story = { render: () => (
-

With Thinned Ends (Default)

- +

Full Thinned (Default)

+
-

Without Thinned Ends

- +

Thinned Start Only

+ +
+
+

Thinned End Only

+ +
+
+

No Fading (None)

+
), diff --git a/packages/ui/src/components/Separator/Separator.tsx b/packages/ui/src/components/Separator/Separator.tsx index f51927d..ccc0499 100644 --- a/packages/ui/src/components/Separator/Separator.tsx +++ b/packages/ui/src/components/Separator/Separator.tsx @@ -5,6 +5,11 @@ import { VStack } from '../VStack/VStack'; import type { ClassNameProps, TestIdProps } from '@ermnvldmr/stl'; +/** + * Options for thinning the separator line ends with a gradient fade. + */ +export type SeparatorThinnedType = 'thinned' | 'thinned-end' | 'thinned-start' | 'none'; + /** * Props for the Separator component. */ @@ -15,10 +20,32 @@ export interface SeparatorProps extends ClassNameProps, TestIdProps { color?: 'black' | 'outline' | 'outline-light'; /** Orientation of the separator */ direction?: 'horizontal' | 'vertical'; - /** Whether line ends should be thinned with gradient fade */ - thinned?: boolean; + /** + * How line ends should be thinned with gradient fade. + * - 'thinned': Both ends fade out (default) + * - 'thinned-start': Only the start of the line fades out + * - 'thinned-end': Only the end of the line fades out + * - 'none': No fading, sharp edges + */ + thinned?: SeparatorThinnedType; } +const THINNED_TYPE_CLASSES: Record< + 'horizontal' | 'vertical', + Record, string> +> = { + horizontal: { + thinned: '[mask-image:linear-gradient(to_right,transparent,black_10%,black_90%,transparent)]', + 'thinned-start': '[mask-image:linear-gradient(to_right,transparent,black_10%,black)]', + 'thinned-end': '[mask-image:linear-gradient(to_right,black,black_90%,transparent)]', + }, + vertical: { + thinned: '[mask-image:linear-gradient(to_bottom,transparent,black_10%,black_90%,transparent)]', + 'thinned-start': '[mask-image:linear-gradient(to_bottom,transparent,black_10%,black)]', + 'thinned-end': '[mask-image:linear-gradient(to_bottom,black,black_90%,transparent)]', + }, +}; + /** * A separator component for dividing content sections. * @@ -26,7 +53,7 @@ export interface SeparatorProps extends ClassNameProps, TestIdProps { * - Single or double line variants inspired by newspaper layout traditions * - Multiple color options (black, outline, outline-light) with theme support * - Horizontal and vertical orientations - * - Optional thinned gradient fade effect at line ends (10% fade length) + * - Optional thinned gradient fade effect at line ends ('thinned', 'thinned-start', 'thinned-end', 'none') * - 2px stroke width following print design standards * - Semantic HTML with proper ARIA attributes for accessibility * @@ -35,14 +62,17 @@ export interface SeparatorProps extends ClassNameProps, TestIdProps { * * @example * ```tsx - * // Basic horizontal separator + * // Basic horizontal separator (defaults to thinned) * * * // Double-line separator with black color * * * // Vertical separator without thinned ends - * + * + * + * // Separator with only the end thinned + * * * // Light separator for subtle content division * @@ -52,7 +82,7 @@ export const Separator = memo(function Separator({ type = 'single', color, direction = 'horizontal', - thinned = true, + thinned = 'thinned', className, 'data-testid': testId, }: SeparatorProps) { @@ -77,13 +107,8 @@ export const Separator = memo(function Separator({ ]; // Thinned effect using CSS mask - const thinnedClasses = thinned - ? [ - direction === 'horizontal' - ? '[mask-image:linear-gradient(to_right,transparent,black_10%,black_90%,transparent)]' - : '[mask-image:linear-gradient(to_bottom,transparent,black_10%,black_90%,transparent)]', - ] - : []; + const thinnedClasses = + thinned !== 'none' ? [THINNED_TYPE_CLASSES[direction][thinned]] : []; // Size classes - fill container by default const sizeClasses = diff --git a/packages/ui/src/components/ThemeSwitch/ThemeSwitch.tsx b/packages/ui/src/components/ThemeSwitch/ThemeSwitch.tsx index 188dd58..7078bbd 100644 --- a/packages/ui/src/components/ThemeSwitch/ThemeSwitch.tsx +++ b/packages/ui/src/components/ThemeSwitch/ThemeSwitch.tsx @@ -35,7 +35,7 @@ export function ThemeSwitch(): React.JSX.Element { /> - +