& {
+ icon?: ReactNode;
+ shortcut?: string;
+}) => (
+
+ {icon && {icon}}
+ {children}
+ {shortcut && {shortcut}}
+
+);
+
+export const MenuSeparator = () => (
+
+);
+
+export const MenuSurface = ({ children }: { children: ReactNode }) => (
+ {children}
+);
+
+export const MenuRow = ({
+ icon,
+ shortcut,
+ children,
+}: {
+ icon?: ReactNode;
+ shortcut?: string;
+ children: ReactNode;
+}) => (
+
+ {icon && {icon}}
+ {children}
+ {shortcut && {shortcut}}
+
+);
diff --git a/src/components/progress/index.tsx b/src/components/progress/index.tsx
new file mode 100644
index 0000000..ad6f07f
--- /dev/null
+++ b/src/components/progress/index.tsx
@@ -0,0 +1,16 @@
+import { type ComponentPropsWithoutRef } from 'react';
+import { Progress as RProgress } from 'radix-ui';
+import { cx } from '../utils';
+
+export const Progress = ({
+ value = 0,
+ className,
+ ...props
+}: ComponentPropsWithoutRef) => (
+
+
+
+);
diff --git a/src/components/scroll-area/index.tsx b/src/components/scroll-area/index.tsx
new file mode 100644
index 0000000..ef01ca5
--- /dev/null
+++ b/src/components/scroll-area/index.tsx
@@ -0,0 +1,22 @@
+import { type ComponentPropsWithoutRef } from 'react';
+import { ScrollArea as RScrollArea } from 'radix-ui';
+import { cx } from '../utils';
+
+export const ScrollArea = ({
+ children,
+ className,
+ ...props
+}: ComponentPropsWithoutRef) => (
+
+
+ {children}
+
+
+
+
+
+
+
+
+
+);
diff --git a/src/components/segmented-control/index.tsx b/src/components/segmented-control/index.tsx
new file mode 100644
index 0000000..8195804
--- /dev/null
+++ b/src/components/segmented-control/index.tsx
@@ -0,0 +1,39 @@
+import { type ComponentPropsWithoutRef } from 'react';
+import { ToggleGroup as RToggleGroup } from 'radix-ui';
+import { cx } from '../utils';
+
+type SegProps = Omit<
+ ComponentPropsWithoutRef,
+ 'type' | 'onValueChange' | 'defaultValue' | 'value'
+> & {
+ value: string;
+ defaultValue?: string;
+ onValueChange: (v: string) => void;
+ items: Array<{ value: string; label: string }>;
+};
+
+export const SegmentedControl = ({
+ value,
+ onValueChange,
+ items,
+ className,
+ ...props
+}: SegProps) => (
+ v && onValueChange(v)}
+ {...props}
+ >
+ {items.map((it) => (
+
+ {it.label}
+
+ ))}
+
+);
diff --git a/src/components/select/index.tsx b/src/components/select/index.tsx
new file mode 100644
index 0000000..a56d797
--- /dev/null
+++ b/src/components/select/index.tsx
@@ -0,0 +1,42 @@
+import { type ComponentPropsWithoutRef } from 'react';
+import { Select as RSelect } from 'radix-ui';
+import { Check, CaretDown } from '@phosphor-icons/react';
+
+type SelectProps = ComponentPropsWithoutRef & {
+ placeholder?: string;
+ items: Array<{ value: string; label: string }>;
+ 'aria-label'?: string;
+};
+
+export const Select = ({ placeholder, items, ...rest }: SelectProps) => (
+
+
+
+
+
+
+
+
+
+
+ {items.map((it) => (
+
+ {it.label}
+
+
+
+
+ ))}
+
+
+
+
+);
diff --git a/src/components/selection/index.tsx b/src/components/selection/index.tsx
new file mode 100644
index 0000000..c3fd38b
--- /dev/null
+++ b/src/components/selection/index.tsx
@@ -0,0 +1,53 @@
+import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
+import {
+ Switch as RSwitch,
+ Checkbox as RCheckbox,
+ RadioGroup as RRadioGroup,
+} from 'radix-ui';
+
+export const Switch = (props: ComponentPropsWithoutRef) => (
+
+
+
+);
+
+export const Checkbox = (props: ComponentPropsWithoutRef) => (
+
+
+
+
+
+);
+
+export const RadioGroup = RRadioGroup.Root;
+
+export const RadioItem = ({
+ value,
+ ...props
+}: ComponentPropsWithoutRef) => (
+
+
+
+);
+
+export const Control = ({
+ children,
+ control,
+}: {
+ children: ReactNode;
+ control: ReactNode;
+}) => (
+
+);
diff --git a/src/components/slider/index.tsx b/src/components/slider/index.tsx
new file mode 100644
index 0000000..0697090
--- /dev/null
+++ b/src/components/slider/index.tsx
@@ -0,0 +1,28 @@
+import { type ComponentPropsWithoutRef } from 'react';
+import { Slider as RSlider } from 'radix-ui';
+
+export const Slider = (props: ComponentPropsWithoutRef) => (
+
+
+
+
+
+
+);
+
+export const Stepper = ({
+ onDecrement,
+ onIncrement,
+}: {
+ onDecrement: () => void;
+ onIncrement: () => void;
+}) => (
+
+
+
+
+);
diff --git a/src/components/spinner/index.tsx b/src/components/spinner/index.tsx
new file mode 100644
index 0000000..684fb6f
--- /dev/null
+++ b/src/components/spinner/index.tsx
@@ -0,0 +1,45 @@
+import { useId, type ComponentPropsWithoutRef } from 'react';
+import { cx } from '../utils';
+
+export const Spinner = ({
+ size,
+ className,
+ ...props
+}: ComponentPropsWithoutRef<'span'> & { size?: 'sm' | 'lg' }) => {
+ const gid = `modern-sk-groove-${useId()}`;
+ return (
+
+
+
+ );
+};
diff --git a/src/components/table/index.tsx b/src/components/table/index.tsx
new file mode 100644
index 0000000..f5f7f9b
--- /dev/null
+++ b/src/components/table/index.tsx
@@ -0,0 +1,24 @@
+import { type ComponentPropsWithoutRef } from 'react';
+import { cx } from '../utils';
+
+export const Table = ({ children, ...props }: ComponentPropsWithoutRef<'table'>) => (
+
+);
+
+export const THead = (p: ComponentPropsWithoutRef<'thead'>) => ;
+export const TBody = (p: ComponentPropsWithoutRef<'tbody'>) => ;
+
+export const Tr = ({
+ selected,
+ className,
+ ...props
+}: ComponentPropsWithoutRef<'tr'> & { selected?: boolean }) => (
+
+);
+
+export const Th = (p: ComponentPropsWithoutRef<'th'>) => | ;
+export const Td = (p: ComponentPropsWithoutRef<'td'>) => | ;
diff --git a/src/components/tabs/index.tsx b/src/components/tabs/index.tsx
new file mode 100644
index 0000000..df63206
--- /dev/null
+++ b/src/components/tabs/index.tsx
@@ -0,0 +1,28 @@
+import { type ComponentPropsWithoutRef } from 'react';
+import { Tabs as RTabs } from 'radix-ui';
+import { cx } from '../utils';
+
+export const Tabs = RTabs.Root;
+
+export const TabsList = ({
+ items,
+ className,
+ ...props
+}: { items: Array<{ value: string; label: string }> } & Omit<
+ ComponentPropsWithoutRef,
+ 'children'
+>) => (
+
+ {items.map((it) => (
+
+ {it.label}
+
+ ))}
+
+);
+
+export const TabsContent = RTabs.Content;
diff --git a/src/components/text-field/index.tsx b/src/components/text-field/index.tsx
new file mode 100644
index 0000000..17666fb
--- /dev/null
+++ b/src/components/text-field/index.tsx
@@ -0,0 +1,26 @@
+import { forwardRef, type ComponentPropsWithoutRef, type ReactNode } from 'react';
+import { cx } from '../utils';
+
+export const TextField = forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ ),
+);
+TextField.displayName = 'TextField';
+
+export const TextArea = forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ ),
+);
+TextArea.displayName = 'TextArea';
+
+export const SearchField = ({
+ icon,
+ ...props
+}: ComponentPropsWithoutRef<'input'> & { icon: ReactNode }) => (
+
+ {icon}
+
+
+);
diff --git a/src/components/tooltip/index.tsx b/src/components/tooltip/index.tsx
new file mode 100644
index 0000000..1261fe9
--- /dev/null
+++ b/src/components/tooltip/index.tsx
@@ -0,0 +1,42 @@
+import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
+import { Tooltip as RTooltip } from 'radix-ui';
+import { cx } from '../utils';
+
+type TooltipProps = {
+ content: ReactNode;
+ children: ReactNode;
+ delayDuration?: number;
+ open?: boolean;
+ defaultOpen?: boolean;
+ onOpenChange?: (o: boolean) => void;
+} & Omit, 'children'>;
+
+export const Tooltip = ({
+ content,
+ children,
+ delayDuration,
+ open,
+ defaultOpen,
+ onOpenChange,
+ sideOffset = 6,
+ className,
+ ...contentProps
+}: TooltipProps) => (
+
+ {children}
+
+
+ {content}
+
+
+
+);
diff --git a/src/components/ui.tsx b/src/components/ui.tsx
index 021b4de..e9bbc53 100644
--- a/src/components/ui.tsx
+++ b/src/components/ui.tsx
@@ -1,679 +1,21 @@
-/* ============================================================
- ModernSK UI — Radix Primitives wrapped in the ModernSK look.
- Logic/accessibility from Radix; every pixel from the tokens in
- styles/tokens.css + styles/components.css.
- ============================================================ */
-import {
- forwardRef,
- useId,
- type ComponentPropsWithoutRef,
- type CSSProperties,
- type ReactNode,
-} from 'react';
-import {
- Switch as RSwitch,
- Checkbox as RCheckbox,
- RadioGroup as RRadioGroup,
- Tabs as RTabs,
- Slider as RSlider,
- DropdownMenu as RMenu,
- Tooltip as RTooltip,
- Progress as RProgress,
- Select as RSelect,
- ToggleGroup as RToggleGroup,
- Dialog as RDialog,
- AlertDialog as RAlertDialog,
- ScrollArea as RScrollArea,
-} from 'radix-ui';
-import { Check, CaretDown, X } from '@phosphor-icons/react';
-
-const cx = (...c: Array) =>
- c.filter(Boolean).join(' ');
-
-/* ---------- BUTTON ---------- */
-type BtnVariant = 'key' | 'primary' | 'ember' | 'ghost';
-type ButtonProps = ComponentPropsWithoutRef<'button'> & {
- variant?: BtnVariant;
- size?: 'sm';
- iconOnly?: boolean;
-};
-export const Button = forwardRef(
- ({ variant = 'key', size, iconOnly, className, ...props }, ref) => (
-
- ),
-);
-Button.displayName = 'Button';
-
-/* ---------- TEXT FIELD ---------- */
-export const TextField = forwardRef<
- HTMLInputElement,
- ComponentPropsWithoutRef<'input'>
->(({ className, ...props }, ref) => (
-
-));
-TextField.displayName = 'TextField';
-
-export const TextArea = forwardRef<
- HTMLTextAreaElement,
- ComponentPropsWithoutRef<'textarea'>
->(({ className, ...props }, ref) => (
-
-));
-TextArea.displayName = 'TextArea';
-
-export const SearchField = ({
- icon,
- ...props
-}: ComponentPropsWithoutRef<'input'> & { icon: ReactNode }) => (
-
- {icon}
-
-
-);
-
-/* ---------- SELECT (Radix) ---------- */
-type SelectProps = {
- value?: string;
- defaultValue?: string;
- onValueChange?: (v: string) => void;
- placeholder?: string;
- items: Array<{ value: string; label: string }>;
- 'aria-label'?: string;
-};
-export const Select = ({
- value,
- defaultValue,
- onValueChange,
- placeholder,
- items,
- ...rest
-}: SelectProps) => (
-
-
-
-
-
-
-
-
-
-
- {items.map((it) => (
-
- {it.label}
-
-
-
-
- ))}
-
-
-
-
-);
-
-/* ---------- SWITCH ---------- */
-export const Switch = (
- props: ComponentPropsWithoutRef,
-) => (
-
-
-
-);
-
-/* ---------- CHECKBOX ---------- */
-export const Checkbox = (
- props: ComponentPropsWithoutRef,
-) => (
-
-
-
-
-
-);
-
-/* ---------- RADIO GROUP ---------- */
-export const RadioGroup = RRadioGroup.Root;
-export const RadioItem = ({
- value,
- ...props
-}: ComponentPropsWithoutRef) => (
-
-
-
-);
-
-/* control + label row helper */
-export const Control = ({
- children,
- control,
-}: {
- children: ReactNode;
- control: ReactNode;
-}) => (
-
-);
-
-/* ---------- SEGMENTED CONTROL (ToggleGroup single) ---------- */
-type SegProps = {
- value: string;
- onValueChange: (v: string) => void;
- items: Array<{ value: string; label: string }>;
-};
-export const SegmentedControl = ({
- value,
- onValueChange,
- items,
-}: SegProps) => (
- v && onValueChange(v)}
- >
- {items.map((it) => (
-
- {it.label}
-
- ))}
-
-);
-
-/* ---------- SLIDER ---------- */
-export const Slider = (
- props: ComponentPropsWithoutRef,
-) => (
-
-
-
-
-
-
-);
-
-/* ---------- STEPPER ---------- */
-export const Stepper = ({
- onDecrement,
- onIncrement,
-}: {
- onDecrement: () => void;
- onIncrement: () => void;
-}) => (
-
-
-
-
-);
-
-/* ---------- TABS ---------- */
-export const Tabs = RTabs.Root;
-export const TabsList = ({
- items,
-}: {
- items: Array<{ value: string; label: string }>;
-}) => (
-
- {items.map((it) => (
-
- {it.label}
-
- ))}
-
-);
-export const TabsContent = RTabs.Content;
-
-/* ---------- PROGRESS ---------- */
-export const Progress = ({ value = 0 }: { value?: number }) => (
-
-
-
-);
-
-/* ---------- BADGE ---------- */
-type BadgeVariant = 'lime' | 'ember' | 'neutral' | 'outline';
-export const Badge = ({
- variant = 'neutral',
- dot,
- className,
- children,
- ...props
-}: ComponentPropsWithoutRef<'span'> & {
- variant?: BadgeVariant;
- dot?: boolean;
-}) => (
-
- {children}
-
-);
-
-/* ---------- CHIP ---------- */
-export const Chip = ({
- children,
- onRemove,
-}: {
- children: ReactNode;
- onRemove?: () => void;
-}) => (
-
- {children}
- {onRemove && (
-
- )}
-
-);
-
-/* ---------- CARD ---------- */
-export const Card = ({
- className,
- ...props
-}: ComponentPropsWithoutRef<'div'>) => (
-
-);
-
-/* ---------- LIST ---------- */
-export const List = ({
- className,
- ...props
-}: ComponentPropsWithoutRef<'div'>) => (
-
-);
-export const Row = ({
- selected,
- className,
- ...props
-}: ComponentPropsWithoutRef<'div'> & { selected?: boolean }) => (
-
-);
-
-/* ---------- DROPDOWN MENU (Radix) ---------- */
-export const Menu = RMenu.Root;
-export const MenuTrigger = RMenu.Trigger;
-export const MenuContent = ({
- children,
- ...props
-}: ComponentPropsWithoutRef) => (
-
-
- {children}
-
-
-);
-export const MenuItem = ({
- icon,
- shortcut,
- children,
- ...props
-}: ComponentPropsWithoutRef & {
- icon?: ReactNode;
- shortcut?: string;
-}) => (
-
- {icon && {icon}}
- {children}
- {shortcut && {shortcut}}
-
-);
-export const MenuSeparator = () => (
-
-);
-
-/* Static menu surface — for showcasing the menu without a trigger. */
-export const MenuSurface = ({ children }: { children: ReactNode }) => (
- {children}
-);
-export const MenuRow = ({
- icon,
- shortcut,
- children,
-}: {
- icon?: ReactNode;
- shortcut?: string;
- children: ReactNode;
-}) => (
-
- {icon && {icon}}
- {children}
- {shortcut && {shortcut}}
-
-);
-
-/* ---------- TOOLTIP (Radix) ---------- */
-export const Tooltip = ({
- content,
- children,
-}: {
- content: ReactNode;
- children: ReactNode;
-}) => (
-
- {children}
-
-
- {content}
-
-
-
-);
-
-/* ---------- ICON BUTTON ---------- */
-type IconButtonProps = ComponentPropsWithoutRef<'button'> & {
- variant?: BtnVariant;
- size?: 'sm' | 'lg';
-};
-export const IconButton = forwardRef(
- ({ variant = 'key', size, className, ...props }, ref) => (
-
- ),
-);
-IconButton.displayName = 'IconButton';
-
-/* ---------- SPINNER ----------
- Carved donut groove (sunk like the switch well, dark rim at top →
- light catch at the bottom) with a glossy lime arc spinning inside it. */
-export const Spinner = ({
- size,
- className,
- ...props
-}: ComponentPropsWithoutRef<'span'> & { size?: 'sm' | 'lg' }) => {
- const gid = `modern-sk-groove-${useId()}`;
- return (
-
-
-
- );
-};
-
-/* ---------- CALLOUT ---------- */
-type CalloutVariant = 'info' | 'success' | 'warning' | 'danger';
-export const Callout = ({
- variant = 'info',
- icon,
- children,
-}: {
- variant?: CalloutVariant;
- icon?: ReactNode;
- children: ReactNode;
-}) => (
-
- {icon &&
{icon}}
-
{children}
-
-);
-
-/* ---------- TABLE ---------- */
-export const Table = ({
- children,
- ...props
-}: ComponentPropsWithoutRef<'table'>) => (
-
-);
-export const THead = (p: ComponentPropsWithoutRef<'thead'>) => ;
-export const TBody = (p: ComponentPropsWithoutRef<'tbody'>) => ;
-export const Tr = ({
- selected,
- className,
- ...props
-}: ComponentPropsWithoutRef<'tr'> & { selected?: boolean }) => (
-
-);
-export const Th = (p: ComponentPropsWithoutRef<'th'>) => | ;
-export const Td = (p: ComponentPropsWithoutRef<'td'>) => | ;
-
-/* ---------- SCROLL AREA (Radix) ---------- */
-export const ScrollArea = ({
- children,
- className,
- style,
-}: {
- children: ReactNode;
- className?: string;
- style?: CSSProperties;
-}) => (
-
-
- {children}
-
-
-
-
-
-
-
-
-
-);
-
-/* ---------- DIALOG / MODAL (Radix Dialog) ---------- */
-export const Dialog = ({
- trigger,
- title,
- description,
- children,
- footer,
- open,
- onOpenChange,
-}: {
- trigger?: ReactNode;
- title: string;
- description?: ReactNode;
- children?: ReactNode;
- footer?: ReactNode;
- open?: boolean;
- onOpenChange?: (o: boolean) => void;
-}) => (
-
- {trigger && {trigger}}
-
-
-
- {title}
- {description && (
-
- {description}
-
- )}
- {children && {children}
}
- {footer && {footer}
}
-
-
-
-
-
-
-
-
-);
-/* Low-level Dialog parts (for custom compositions). */
-export const DialogClose = RDialog.Close;
-
-/* ---------- ALERT DIALOG (Radix AlertDialog) ---------- */
-export const AlertDialog = ({
- trigger,
- title,
- description,
- cancelLabel = 'Cancel',
- actionLabel = 'Confirm',
- destructive,
- onAction,
- open,
- onOpenChange,
-}: {
- trigger?: ReactNode;
- title: string;
- description?: ReactNode;
- cancelLabel?: string;
- actionLabel?: string;
- destructive?: boolean;
- onAction?: () => void;
- open?: boolean;
- onOpenChange?: (o: boolean) => void;
-}) => (
-
- {trigger && {trigger}}
-
-
-
-
- {title}
-
- {description && (
-
- {description}
-
- )}
-
-
-
-
-
-
-
-
-
-
-
-);
-
-/* ---------- WINDOW CHROME ---------- */
-export const Window = ({
- title,
- badge,
- children,
- ...props
-}: ComponentPropsWithoutRef<'div'> & {
- title: string;
- badge?: ReactNode;
-}) => (
-
-
-
-
-
-
{title}
- {badge && (
-
- {badge}
-
- )}
-
- {children}
-
-);
+export * from './button';
+export * from './icon-button';
+export * from './text-field';
+export * from './select';
+export * from './selection';
+export * from './segmented-control';
+export * from './slider';
+export * from './tabs';
+export * from './progress';
+export * from './badge';
+export * from './card';
+export * from './list';
+export * from './menu';
+export * from './tooltip';
+export * from './spinner';
+export * from './callout';
+export * from './table';
+export * from './scroll-area';
+export * from './dialog';
+export * from './alert-dialog';
+export * from './window';
diff --git a/src/components/utils.ts b/src/components/utils.ts
new file mode 100644
index 0000000..e672f9d
--- /dev/null
+++ b/src/components/utils.ts
@@ -0,0 +1,2 @@
+export const cx = (...c: Array) =>
+ c.filter(Boolean).join(' ');
diff --git a/src/components/window/index.tsx b/src/components/window/index.tsx
new file mode 100644
index 0000000..0478774
--- /dev/null
+++ b/src/components/window/index.tsx
@@ -0,0 +1,26 @@
+import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
+
+export const Window = ({
+ title,
+ badge,
+ children,
+ ...props
+}: ComponentPropsWithoutRef<'div'> & {
+ title: string;
+ badge?: ReactNode;
+}) => (
+
+
+
+
+
+
{title}
+ {badge && (
+
+ {badge}
+
+ )}
+
+ {children}
+
+);
diff --git a/src/stories/AlertDialog.stories.tsx b/src/stories/AlertDialog.stories.tsx
new file mode 100644
index 0000000..a818975
--- /dev/null
+++ b/src/stories/AlertDialog.stories.tsx
@@ -0,0 +1,64 @@
+import type { Meta, StoryObj } from 'storybook-react-rsbuild';
+import { AlertDialog, Button } from '../components/ui';
+
+const meta = {
+ title: 'Overlays/AlertDialog',
+ component: AlertDialog,
+ parameters: {
+ docs: {
+ description: {
+ component:
+ 'Confirmation dialog built on Radix AlertDialog. Use for destructive or irreversible actions — focus is trapped and the cancel button is always reachable. Set `destructive` to switch the action button to the ember (red) variant.',
+ },
+ },
+ },
+ argTypes: {
+ title: { control: 'text' },
+ description: { control: 'text' },
+ cancelLabel: { control: 'text' },
+ actionLabel: { control: 'text' },
+ destructive: { control: 'boolean' },
+ trigger: { control: false },
+ },
+ args: {
+ title: 'Delete this project?',
+ description: 'This action cannot be undone.',
+ actionLabel: 'Delete',
+ cancelLabel: 'Cancel',
+ destructive: true,
+ trigger: ,
+ onAction: () => {},
+ },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Playground: Story = {};
+
+export const Destructive: Story = {
+ render: () => (
+ Delete account}
+ title="Delete your account?"
+ description="All data will be permanently removed. This cannot be undone."
+ actionLabel="Delete account"
+ cancelLabel="Keep account"
+ destructive
+ onAction={() => {}}
+ />
+ ),
+};
+
+export const Confirm: Story = {
+ render: () => (
+ Publish}
+ title="Publish changes?"
+ description="This will make your changes visible to all users."
+ actionLabel="Publish"
+ cancelLabel="Cancel"
+ onAction={() => {}}
+ />
+ ),
+};
diff --git a/src/stories/Dialog.stories.tsx b/src/stories/Dialog.stories.tsx
new file mode 100644
index 0000000..a123f36
--- /dev/null
+++ b/src/stories/Dialog.stories.tsx
@@ -0,0 +1,54 @@
+import type { Meta, StoryObj } from 'storybook-react-rsbuild';
+import { Dialog, Button, TextField } from '../components/ui';
+
+const meta = {
+ title: 'Overlays/Dialog',
+ component: Dialog,
+ parameters: {
+ docs: {
+ description: {
+ component:
+ 'Modal dialog built on Radix Dialog. Pass a `trigger` to wire open/close automatically, or control it with `open` / `onOpenChange`. Title is required; description, body, and footer are optional slots.',
+ },
+ },
+ },
+ argTypes: {
+ title: { control: 'text' },
+ description: { control: 'text' },
+ trigger: { control: false },
+ children: { control: false },
+ footer: { control: false },
+ },
+ args: {
+ title: 'Rename project',
+ description: 'Choose a new name for this project.',
+ trigger: ,
+ },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Playground: Story = {};
+
+export const WithBody: Story = {
+ name: 'With body',
+ render: () => (
+
+ ),
+};
+
+export const NoDescription: Story = {
+ name: 'No description',
+ args: {
+ description: undefined,
+ title: 'Confirm action',
+ },
+};
diff --git a/src/styles/components.css b/src/styles/components.css
index a62661e..ef88cd2 100644
--- a/src/styles/components.css
+++ b/src/styles/components.css
@@ -310,7 +310,7 @@ textarea.modern-sk-field{ resize:vertical; min-height:64px; line-height:1.5; }
.modern-sk-overlay{ position:fixed; inset:0; z-index:80; background:rgba(8,9,6,.55); backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px); }
.modern-sk-overlay[data-state="open"]{ animation:modern-sk-fade-in var(--dur-base) var(--ease-out); }
.modern-sk-overlay[data-state="closed"]{ animation:modern-sk-fade-out var(--dur-quick) var(--ease-out); }
-.modern-sk-dialog{ position:fixed; z-index:81; top:50%; left:50%; transform:translate(-50%,-50%); width:min(92vw,460px); max-height:85vh; overflow:auto; border-radius:var(--r-xl); border:1px solid var(--hair-strong); background:var(--steel-800); box-shadow:var(--shadow-window); padding:22px 22px 20px; transform-origin:center; }
+.modern-sk-dialog{ position:fixed; z-index:81; top:50%; left:50%; translate:-50% -50%; width:min(92vw,460px); max-height:85vh; overflow:auto; border-radius:var(--r-xl); border:1px solid var(--hair-strong); background:var(--steel-800); box-shadow:var(--shadow-window); padding:22px 22px 20px; transform-origin:center; font-family:var(--font-sans); }
.modern-sk-dialog[data-state="open"]{ animation:modern-sk-scale-in var(--dur-base) var(--ease-out); }
.modern-sk-dialog[data-state="closed"]{ animation:modern-sk-scale-out var(--dur-quick) var(--ease-out); }
.modern-sk-dialog__title{ font-family:var(--font-sans); font-size:var(--text-lg); font-weight:600; color:var(--fg-1); letter-spacing:var(--track-snug); }