@devalok/shilp-sutra 0.40.1 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/AGENTS.md +23 -1
  2. package/BREAKING.json +66 -0
  3. package/BREAKING.schema.json +184 -0
  4. package/MIGRATION.md +16 -0
  5. package/docs/recipes/install-next-app-router.md +54 -14
  6. package/docs/recipes/upgrading.md +19 -0
  7. package/llms-full.txt +1 -1
  8. package/llms-quick.txt +3 -1
  9. package/llms.txt +1 -0
  10. package/make-kit/Guidelines.md +71 -0
  11. package/make-kit/components/badge.md +162 -0
  12. package/make-kit/components/button.md +125 -0
  13. package/make-kit/components/card.md +147 -0
  14. package/make-kit/components/dialog.md +167 -0
  15. package/make-kit/components/dropdown-menu.md +205 -0
  16. package/make-kit/components/form.md +189 -0
  17. package/make-kit/components/icon.md +152 -0
  18. package/make-kit/components/input.md +154 -0
  19. package/make-kit/components/overview.md +308 -0
  20. package/make-kit/components/popover.md +201 -0
  21. package/make-kit/components/select.md +148 -0
  22. package/make-kit/components/stack.md +165 -0
  23. package/make-kit/components/table.md +215 -0
  24. package/make-kit/components/tabs.md +162 -0
  25. package/make-kit/components/text.md +139 -0
  26. package/make-kit/components/toast.md +193 -0
  27. package/make-kit/foundations/color.md +128 -0
  28. package/make-kit/foundations/dark-mode.md +81 -0
  29. package/make-kit/foundations/icons.md +107 -0
  30. package/make-kit/foundations/motion.md +134 -0
  31. package/make-kit/foundations/radius.md +78 -0
  32. package/make-kit/foundations/spacing.md +110 -0
  33. package/make-kit/foundations/surfaces.md +121 -0
  34. package/make-kit/foundations/typography.md +120 -0
  35. package/make-kit/setup.md +130 -0
  36. package/package.json +9 -2
  37. package/skill/SKILL.md +3 -3
  38. package/skill/references/components-full.md +1 -1
  39. package/skill/references/components.md +1 -0
  40. package/skill/references/setup-next-app-router.md +54 -14
  41. package/skill/references/upgrading.md +19 -0
@@ -0,0 +1,71 @@
1
+ # shilp-sutra — Figma Make Kit Guidelines
2
+
3
+ > **Read order.** This is the entry point. Read `setup.md` next, then `foundations/overview.md`, then `components/overview.md`. Branch into specific token / component files on demand.
4
+
5
+ ## What this is
6
+
7
+ `@devalok/shilp-sutra` is the Devalok design system: React 18/19 components built on vendored Radix primitives, OKLCH design tokens, CSS-first Tailwind 4. Generated apps must use these components instead of raw HTML elements wherever a matching component exists.
8
+
9
+ ## Product character
10
+
11
+ | Trait | Value |
12
+ |---|---|
13
+ | Density | Mid. Default `md` size on controls. Reach for `sm` only inside dense toolbars, tables, filter bars. |
14
+ | Color philosophy | ~90% neutral surfaces, accent color used sparingly for primary action + status. Brand accent is OKLCH magenta by default, but the kit is themable — never hardcode hex. |
15
+ | Corner radius | Semantic roles (`--radius-control`, `--radius-surface`, `--radius-overlay`). Default preset = "slightly-rounded" (6/10/16 px). Never hand-pick radii. |
16
+ | Elevation | Five surface tiers (`surface-base`, `raised`, `sunken`, `overlay`, `inverted`). Cards sit on `raised`, dialogs on `overlay`. Never combine an explicit `border-*` with a `shadow-*` token — shadow tokens already contain a hairline ring layer. |
17
+ | Motion | framer-motion is a required peer. Wrap apps in `<MotionProvider reducedMotion="user">` once. Use motion primitives (`MotionFade`, `MotionCollapse`, `MotionSlide`) instead of CSS keyframes. |
18
+ | Dark mode | `.dark` class on `<html>` or `<body>` flips every token. Algorithmically derived from OKLCH curves — surfaces lighten with elevation. |
19
+
20
+ ## Mandatory rules
21
+
22
+ These are not preferences. Generated code that violates them is wrong.
23
+
24
+ 1. **Use design system components, not raw HTML.** `<Button>` not `<button>`. `<Input>` not `<input>`. `<Text>` not `<span>`/`<p>` (when typographic semantics matter). `<Stack>` not bare flex divs.
25
+ 2. **Use semantic tokens, never hex / rgb / hsl.** `bg-surface-raised` not `bg-white`. `text-fg` not `text-zinc-900`. `bg-accent-9` not `bg-pink-500`.
26
+ 3. **Spacing uses `ds-*` cadence.** `p-ds-05`, `gap-ds-03`. Never `p-4` / `p-6`. Default cadence is `ds-03 / ds-05 / ds-07` (related items / grouped sections / page sections). Do not reach for every adjacent token (`ds-04`, `ds-06`) — three tiers, not five.
27
+ 4. **Prefer `variant="soft"` over `variant="outline"` for non-primary actions.** Soft (tinted bg, no visible border) reads better in data-dense UIs. Outline only when on a colored bg or paired with a primary for explicit hierarchy.
28
+ 5. **Surface layering is strict.** Page = `surface-base`. Cards/panels/widgets = `surface-raised`. Dialogs/popovers/dropdowns/inputs = `surface-overlay`. Shell chrome (sidebar, topbar) = `surface-sunken`. Tooltips = `surface-inverted`. If you're unsure, read `foundations/surfaces.md`.
29
+ 6. **Never combine `border-*` + `shadow-*` tokens.** Shadow tokens already include a 1px ring layer. Adding an explicit border creates a 2-px edge.
30
+ 7. **Icons use `<Icon icon={...} />` from `@tabler/icons-react`.** Do not import lucide, heroicons, mui-icons. The icon system auto-sizes via `IconProvider` context.
31
+ 8. **Toasts mount once at app root.** `<Toaster />` (singleton). All triggering is imperative: `toast.success("...")`, `toast.error("...")`.
32
+ 9. **Forms wire via `<FormField>`.** It auto-binds `<Label htmlFor>` ↔ `<Input id>` via context. Do not hand-generate ids.
33
+ 10. **Dialog is responsive by default.** It auto-fullScreens on mobile (<768px). Sheet auto-bottom-drawer. Popover auto-drawer. Opt out with `responsive={false}` only when explicitly needed.
34
+
35
+ ## Workflow — before generating any screen
36
+
37
+ 1. **Identify the layout shell.** AppSidebar + TopBar for product pages. Container + Stack for marketing. Dialog/Sheet for modal flows.
38
+ 2. **Pick the surface for each region.** Read `foundations/surfaces.md`. Default page bg is `surface-base`; everything else stacks up from there.
39
+ 3. **Pick components from `components/overview.md`.** That file has decision trees for the common confusions (Button variant, Input vs Combobox vs Autocomplete, Dialog vs Sheet vs Popover).
40
+ 4. **Lay things out with `<Stack>` + `<Container>`.** Use `gap-ds-03/05/07`. Don't reach for arbitrary flex divs.
41
+ 5. **Apply state via component props, not classnames.** Loading? `loading` prop on Button. Error state on Input? `state="error"`. Disabled? `disabled`. Don't simulate states with className overrides.
42
+
43
+ ## Workflow — before using a component
44
+
45
+ 1. Read its component guideline in `components/`. Each lists variants, props, and at least 3 correct examples.
46
+ 2. Check the "Don't" section at the bottom of the file. Most common mistakes are listed there.
47
+ 3. If a prop you want isn't documented, it probably doesn't exist — don't invent one.
48
+
49
+ ## Guidelines map
50
+
51
+ | File | When to read |
52
+ |---|---|
53
+ | `setup.md` | Always. Required imports + provider setup. |
54
+ | `foundations/color.md` | Choosing background / text / border color. Decision tree for status colors. |
55
+ | `foundations/typography.md` | Picking text size / weight. Heading vs body hierarchy. |
56
+ | `foundations/spacing.md` | Picking gap / padding. The three-tier cadence rule. |
57
+ | `foundations/surfaces.md` | Picking which surface tier a region sits on. Includes the surface decision matrix. |
58
+ | `foundations/radius.md` | When to override a radius role. Shape presets (`sharp` / `slightly-rounded` / `rounded`). |
59
+ | `foundations/motion.md` | Adding transitions, list stagger, drawer slides. framer-motion primitives. |
60
+ | `foundations/dark-mode.md` | Wiring the `.dark` toggle. How tokens behave in dark mode. |
61
+ | `foundations/icons.md` | Using Tabler icons, sizing via IconProvider. |
62
+ | `components/overview.md` | Catalog of all major components with decision trees. Read before picking a component. |
63
+ | `components/{name}.md` | Deep guide per component. Variants, props, examples, rules. |
64
+
65
+ ## What this kit excludes
66
+
67
+ - Raw HTML form elements (use `<Input>`, `<Select>`, `<Combobox>`, `<Textarea>`, etc.)
68
+ - Tailwind utility colors (`text-zinc-*`, `bg-blue-*`) — use semantic tokens
69
+ - Custom icon libraries — Tabler only
70
+ - CSS-in-JS at runtime — utilities + tokens cover styling
71
+ - React Router / Next.js Link primitives — use `<Link>` from the kit; consumers wire their router via `LinkProvider`
@@ -0,0 +1,162 @@
1
+ # Badge
2
+
3
+ Small inline status / label / chip. Counts, statuses, tags, filter chips.
4
+
5
+ ```tsx
6
+ import { Badge } from '@devalok/shilp-sutra/ui/badge'
7
+ ```
8
+
9
+ ## When to use
10
+
11
+ - Status pill on a row (Active / Pending / Failed).
12
+ - Filter chip with optional dismiss X.
13
+ - Selectable tag (multi-select chips).
14
+ - Count indicator on an Avatar / Icon / Button — use `<Badge.Indicator>`.
15
+ - A horizontal cluster of tags — wrap in `<Badge.Group>` with optional `max`.
16
+
17
+ Not for in-flow text emphasis — use `<Text variant="label-*">` or `<Code>`.
18
+
19
+ ## Variants
20
+
21
+ | Variant | When |
22
+ |---|---|
23
+ | `subtle` (default) | Tinted bg, colored border. Most common. |
24
+ | `solid` | Filled background, on-color text. Use for high-emphasis statuses (e.g. red error). |
25
+ | `outline` | Border-only, transparent bg. Use on already-tinted backgrounds. |
26
+ | `soft` | Tinted bg, no border. Lighter than `subtle`. Use in dense lists. |
27
+
28
+ ## Colors
29
+
30
+ | Color | Use |
31
+ |---|---|
32
+ | `default` (default) | Neutral / generic. |
33
+ | `accent` | Brand-aligned tag. |
34
+ | `error` | Failed / blocked / critical. |
35
+ | `success` | Active / completed / healthy. |
36
+ | `warning` | Caution / overdue. |
37
+ | `info` | Informational / new. |
38
+ | `neutral` | Brand-agnostic muted tag. |
39
+ | `teal`, `amber`, `slate`, `indigo`, `cyan`, `orange`, `emerald` | Categorical (Kanban columns, project labels). Pick distinct hues per category. |
40
+ | `custom` | Drives off `--badge-color` (and `--badge-fg-color` for solid) inline CSS vars. |
41
+
42
+ ## Sizes
43
+
44
+ | Size | When |
45
+ |---|---|
46
+ | `xs` | Inline next to a 14 px text run. |
47
+ | `sm` | Filter chips, dense tables. |
48
+ | `md` (default) | Standard row pills. |
49
+ | `lg` | Hero / marketing badges. |
50
+
51
+ ## Props
52
+
53
+ | Prop | Type | Notes |
54
+ |---|---|---|
55
+ | `variant` | `'subtle'\|'solid'\|'outline'\|'soft'` | Default `subtle`. |
56
+ | `color` | See colors table | Default `default`. |
57
+ | `size` | `'xs'\|'sm'\|'md'\|'lg'` | Default `md`. |
58
+ | `startIcon` | `ReactElement` | Use `<Icon icon={IconX} />` — auto-sized. |
59
+ | `endIcon` | `ReactElement` | Same. |
60
+ | `dot` | `boolean` | Animated leading status dot. Use for live-state badges. |
61
+ | `onClick` | `() => void` | Renders as `<button>` — interactive badge. |
62
+ | `selected` | `boolean` | Toggle state — shows a check icon. Pair with `onClick`. |
63
+ | `disabled` | `boolean` | Reduced opacity + pointer-events-none. |
64
+ | `onDismiss` | `() => void` | Inline X button for filter chips. |
65
+ | `maxWidth` | `number` | Triggers truncation with `title` tooltip. |
66
+ | `truncate` | `boolean` | Ellipsis overflow without explicit maxWidth. |
67
+ | `circle` | `boolean` | Square aspect-ratio (icon-only / count badges). |
68
+ | `asChild` | `boolean` | Merge with a child (router Link, etc.). |
69
+
70
+ ## Interactive modes
71
+
72
+ | Combo | Renders as | Use |
73
+ |---|---|---|
74
+ | `onClick` only | `<button>` | Selectable tag, filter chip. |
75
+ | `onDismiss` only | `<span>` with inner X button | Read-only chip with dismiss. |
76
+ | `onClick` + `onDismiss` | `<div role="button">` | Selectable + dismissible (avoids nested buttons). |
77
+
78
+ ## Examples
79
+
80
+ **Status pill:**
81
+ ```tsx
82
+ <Badge variant="solid" color="success">Active</Badge>
83
+ <Badge color="warning">Overdue</Badge>
84
+ <Badge color="error" dot>Failed</Badge>
85
+ ```
86
+
87
+ **Dismissible filter chip:**
88
+ ```tsx
89
+ <Badge color="accent" onDismiss={() => removeFilter('react')}>
90
+ React
91
+ </Badge>
92
+ ```
93
+
94
+ **Selectable tag (toggle):**
95
+ ```tsx
96
+ <Badge
97
+ onClick={() => toggleTag('design')}
98
+ selected={selectedTags.has('design')}
99
+ color="accent"
100
+ >
101
+ Design
102
+ </Badge>
103
+ ```
104
+
105
+ **Categorical chip with leading icon:**
106
+ ```tsx
107
+ <Badge color="teal" startIcon={<Icon icon={IconUsers} />}>
108
+ Engineering
109
+ </Badge>
110
+ ```
111
+
112
+ **Count indicator on an Avatar:**
113
+ ```tsx
114
+ <Badge.Indicator content={3} color="error">
115
+ <Avatar src={user.avatar} alt={user.name} />
116
+ </Badge.Indicator>
117
+ ```
118
+
119
+ **Tag cluster with overflow:**
120
+ ```tsx
121
+ <Badge.Group max={3} size="sm" onOverflowClick={() => setShowAll(true)}>
122
+ <Badge>React</Badge>
123
+ <Badge>TypeScript</Badge>
124
+ <Badge>Tailwind</Badge>
125
+ <Badge>Vite</Badge>
126
+ <Badge>Vitest</Badge>
127
+ </Badge.Group>
128
+ {/* Renders: React, TypeScript, Tailwind, +2 */}
129
+ ```
130
+
131
+ **As a router link:**
132
+ ```tsx
133
+ <Badge asChild>
134
+ <Link href="/tags/react">React</Link>
135
+ </Badge>
136
+ ```
137
+
138
+ **Custom color:**
139
+ ```tsx
140
+ <Badge color="custom" style={{ '--badge-color': '#8b5cf6' }}>
141
+ Beta
142
+ </Badge>
143
+ ```
144
+
145
+ ## Composability
146
+
147
+ - **Badge.Group** lays out badges with consistent gap and an optional overflow indicator. Doesn't style children — they keep their own variant / color / size.
148
+ - **Badge.Indicator** overlays a count or dot onto any child (Icon, Avatar, Button). Positioning-only.
149
+ - **IconProvider:** `startIcon` / `endIcon` auto-size to badge size. Don't set explicit size on the nested Icon.
150
+ - **asChild:** Compose with router Link for nav-style badges.
151
+
152
+ See `foundations/color.md` for the color step system, `foundations/icons.md` for IconProvider.
153
+
154
+ ## Rules
155
+
156
+ - For destructive / failed states, use `variant="solid" color="error"`. There is no `variant="destructive"`.
157
+ - Use `Badge.Group` for any tag list — manual gap-handling drifts from the spacing cadence.
158
+ - For toggle-style selectable tags, use `onClick` + `selected`. Don't roll your own with `<Toggle>` styled to look like a Badge.
159
+ - Don't nest a Badge inside a Button — it produces invalid nested buttons. Use `Badge.Indicator` over the Button instead.
160
+ - Icons in `startIcon` / `endIcon` go through `<Icon icon={...} />`. Don't pass `size` on the Icon.
161
+ - For truncated badges, set `truncate` AND a `maxWidth` or container width. Both work together.
162
+ - The deprecated `Chip` component was merged into Badge — use `Badge` with `onClick` for interactive tags.
@@ -0,0 +1,125 @@
1
+ # Button
2
+
3
+ Primary action element. Use instead of `<button>` everywhere.
4
+
5
+ ```tsx
6
+ import { Button } from '@devalok/shilp-sutra/ui/button'
7
+ ```
8
+
9
+ ## When to use
10
+
11
+ - Any user-triggered action: submit a form, open a dialog, navigate (with `asChild`), trigger a flow.
12
+ - Icon-only? Use `<IconButton>` instead.
13
+ - Action plus dropdown? Use `<SplitButton>`.
14
+ - Inline text link (no button affordance)? Use `<Link>` or `<Button variant="link">`.
15
+
16
+ ## Variants — and which to pick
17
+
18
+ | Variant | When |
19
+ |---|---|
20
+ | `solid` (default) | Primary CTA. One per region. Heavy visual weight. |
21
+ | `soft` | Secondary action. **Preferred default for any non-primary action.** Tinted bg, colored text, no visible border — warmer than outline, brand-consistent. |
22
+ | `outline` | Secondary action on a colored / `surface-raised` bg where soft's tint disappears. In toolbars / icon-dense rows. Paired adjacent to a primary that needs explicit hierarchy. |
23
+ | `ghost` | Tertiary / dismissive — close, cancel, skip. Minimal weight. |
24
+ | `link` | Inline action that should read as a link but behave as a button. Use sparingly. |
25
+
26
+ **Rule:** for non-primary actions, default to `soft`. Reach for `outline` only when the three above conditions hit.
27
+
28
+ ## Colors
29
+
30
+ | Color | Use |
31
+ |---|---|
32
+ | `accent` (default) | Primary brand action. |
33
+ | `error` | Destructive action. Pair with confirmation. |
34
+ | `success` | Confirmation / completion action. |
35
+ | `warning` | Caution action. |
36
+ | `neutral` | Brand-agnostic action. Filing/utility buttons. |
37
+
38
+ There is **no** `color="destructive"` — use `variant="solid" color="error"`.
39
+ There is **no** `variant="destructive"` — same.
40
+
41
+ ## Sizes
42
+
43
+ | Size | Pixel height | When |
44
+ |---|---|---|
45
+ | `xs` | 28 | Filter chips, dense toolbars. |
46
+ | `sm` | 32 | Secondary actions in dense rows. |
47
+ | `md` (default) | 40 | Standard. |
48
+ | `lg` | 48 | Marketing CTAs, primary in spacious layouts. |
49
+ | `compact-xs/sm/md` | inline (height-less) | Inline buttons that flow with text. |
50
+ | `icon`, `icon-xs/sm/md/lg` | square | Use `<IconButton>` instead, with the same sizes. |
51
+
52
+ ## Common props
53
+
54
+ | Prop | Type | Notes |
55
+ |---|---|---|
56
+ | `variant` | `'solid'\|'soft'\|'outline'\|'ghost'\|'link'` | See above. |
57
+ | `color` | `'accent'\|'error'\|'success'\|'warning'\|'neutral'` | See above. |
58
+ | `size` | `'xs'\|'sm'\|'md'\|'lg'` + compact + icon variants | Default `md`. |
59
+ | `startIcon`, `endIcon` | `IconInput` | Use `<Icon icon={IconX} />` or a Tabler ref. |
60
+ | `loading` | `boolean` | Disables click + shows spinner. |
61
+ | `loadingPosition` | `'start'\|'end'\|'center'` | Default `'start'`. |
62
+ | `fullWidth` | `boolean` | Stretch to parent width. |
63
+ | `shape` | `'default'\|'pill'` | Pill = fully rounded. |
64
+ | `weight` | `'semibold'\|'normal'` | Default `semibold`. Use `normal` for soft/ghost in dense areas. |
65
+ | `onClickAsync` | `(e) => Promise<void>` | Auto-runs idle → loading → success/error → idle state machine. |
66
+ | `asyncFeedbackDuration` | `number` (ms) | Default 1500. |
67
+ | `processing` | `boolean \| 'ambient' \| 'working' \| 'urgent'` | Marching-ants border for long ops. Forces `soft` variant. |
68
+ | `asChild` | `boolean` | Style transfers to child (use with router Links). |
69
+
70
+ ## Examples
71
+
72
+ **Primary action:**
73
+ ```tsx
74
+ <Button>Save</Button>
75
+ ```
76
+
77
+ **Primary + secondary, sensible default:**
78
+ ```tsx
79
+ <Stack direction="row" gap="ds-03">
80
+ <Button>Save</Button>
81
+ <Button variant="soft">Cancel</Button>
82
+ </Stack>
83
+ ```
84
+
85
+ **Destructive with confirmation pattern:**
86
+ ```tsx
87
+ <Button variant="solid" color="error" startIcon={IconTrash}>
88
+ Delete project
89
+ </Button>
90
+ ```
91
+
92
+ **Async with built-in feedback:**
93
+ ```tsx
94
+ <Button onClickAsync={async () => { await api.save(form) }}>
95
+ Save changes
96
+ </Button>
97
+ ```
98
+
99
+ **Inside a ButtonGroup (toolbar):**
100
+ ```tsx
101
+ <ButtonGroup variant="outline" size="sm">
102
+ <Button startIcon={IconBold} />
103
+ <Button startIcon={IconItalic} />
104
+ <Button startIcon={IconUnderline} />
105
+ </ButtonGroup>
106
+ ```
107
+
108
+ **With a router link (Next.js):**
109
+ ```tsx
110
+ <Button asChild>
111
+ <Link href="/dashboard">Dashboard</Link>
112
+ </Button>
113
+ ```
114
+
115
+ ## Rules
116
+
117
+ - **Default to `variant="soft"`** for any non-primary action.
118
+ - **Never** `variant="destructive"` / `variant="secondary"` / `color="danger"` / `color="default"` — these don't exist.
119
+ - **Never** size `default` — use `md`.
120
+ - **Never** color-override via className (`className="bg-red-500"`) — use `color` prop.
121
+ - **Don't pair** explicit `loading` + `onClickAsync` — `onClickAsync` manages loading itself.
122
+ - **`processing` forces `variant="soft"`** so the marching ants are visible. Don't fight it.
123
+ - **Inside `<ButtonGroup>`**, omit `variant`/`color`/`size` on children unless intentionally overriding — they inherit from context.
124
+ - Icons in `startIcon` / `endIcon` auto-size via `IconProvider`. Don't pass `size` on the icon.
125
+ - For multi-line button content, use `<Stack>` inside; default Button is single-line.
@@ -0,0 +1,147 @@
1
+ # Card
2
+
3
+ The default container for any panel-style content that sits **on** the page.
4
+
5
+ ```tsx
6
+ import {
7
+ Card,
8
+ CardHeader,
9
+ CardTitle,
10
+ CardDescription,
11
+ CardContent,
12
+ CardFooter,
13
+ } from '@devalok/shilp-sutra/ui/card'
14
+ ```
15
+
16
+ ## When to use
17
+
18
+ - Any rectangular region that reads as a discrete unit on the page: dashboards widgets, list items, marketing feature blocks.
19
+ - Need built-in header / actions / footer slots? Use `<ContentCard>` (composed wrapper).
20
+ - Need just a tinted region with no card affordance? Use a `<div className="bg-surface-raised">` (rare; usually Card is right).
21
+
22
+ Card renders on `surface-raised` with `shadow-raised`. **Never** override its background or border.
23
+
24
+ ## Variants
25
+
26
+ | Variant | Use |
27
+ |---|---|
28
+ | `default` (default) | `surface-raised` + `shadow-raised`. Standard card. |
29
+ | `elevated` | Slightly stronger shadow. Use for hero / hovered emphasis. |
30
+ | `outline` | `surface-raised` + border-only (no shadow). Dense lists where 12 cards stacked together would feel too lifted. |
31
+ | `flat` | `surface-raised` + no shadow, no border. For cards inside an already-elevated container. |
32
+
33
+ ## Colors
34
+
35
+ | Color | What it tints |
36
+ |---|---|
37
+ | `default` (default) | Standard border. |
38
+ | `accent`, `error`, `success`, `warning`, `info`, `neutral` | Border picks up the semantic color at step-7. Use to signal status without filling the whole card. |
39
+
40
+ ## Sizes (padding cascade)
41
+
42
+ `size` on Card propagates to `CardHeader`, `CardContent`, `CardFooter` via React context:
43
+
44
+ | Size | Default padding |
45
+ |---|---|
46
+ | `sm` | `ds-04` (12 px) |
47
+ | `md` (default) | `ds-05` (16 px) |
48
+ | `lg` | `ds-07` (32 px) |
49
+
50
+ Override on a sub-component via `className` only if absolutely necessary — it breaks the cascade.
51
+
52
+ ## Compound shape
53
+
54
+ ```
55
+ Card (root)
56
+ CardHeader ← inherits size from Card
57
+ CardTitle
58
+ CardDescription
59
+ CardContent ← inherits size from Card
60
+ CardFooter ← inherits size from Card
61
+ ```
62
+
63
+ ## Other props
64
+
65
+ | Prop | Type | Notes |
66
+ |---|---|---|
67
+ | `interactive` | `boolean` | Adds hover lift + `cursor-pointer`. Make the whole card clickable. Provide `onClick` and `aria-label`. |
68
+ | `accent` | `'left'\|'top'\|'right'\|'bottom'` | Decorative colored bar on the named edge. |
69
+ | `accentColor` | `'default'\|'accent'\|'error'\|'success'\|'warning'\|'info'` | Color of the accent bar. |
70
+
71
+ ## Examples
72
+
73
+ **Standard:**
74
+ ```tsx
75
+ <Card>
76
+ <CardHeader>
77
+ <CardTitle>Q4 revenue</CardTitle>
78
+ <CardDescription>Last updated 2h ago</CardDescription>
79
+ </CardHeader>
80
+ <CardContent>
81
+ <Text variant="heading-lg">$2.4M</Text>
82
+ <Text variant="body-sm" className="text-fg-muted">+18% YoY</Text>
83
+ </CardContent>
84
+ </Card>
85
+ ```
86
+
87
+ **Interactive (whole card clickable):**
88
+ ```tsx
89
+ <Card interactive onClick={() => navigate(`/projects/${id}`)} aria-label={`Open ${name}`}>
90
+ <CardHeader>
91
+ <CardTitle>{name}</CardTitle>
92
+ <CardDescription>{owner} · {memberCount} members</CardDescription>
93
+ </CardHeader>
94
+ </Card>
95
+ ```
96
+
97
+ **Status-tinted border (warning):**
98
+ ```tsx
99
+ <Card color="warning">
100
+ <CardHeader>
101
+ <CardTitle>Action required</CardTitle>
102
+ <CardDescription>2 invoices are overdue.</CardDescription>
103
+ </CardHeader>
104
+ <CardFooter>
105
+ <Button variant="soft" color="warning">Review</Button>
106
+ </CardFooter>
107
+ </Card>
108
+ ```
109
+
110
+ **Accent bar — pipeline state:**
111
+ ```tsx
112
+ <Card accent="left" accentColor="success">
113
+ <CardHeader>
114
+ <CardTitle>Deploy succeeded</CardTitle>
115
+ <CardDescription>main · 2 min ago</CardDescription>
116
+ </CardHeader>
117
+ </Card>
118
+ ```
119
+
120
+ **Dense list — outline variant, no shadow accumulation:**
121
+ ```tsx
122
+ <Stack gap="ds-03">
123
+ {items.map((item) => (
124
+ <Card key={item.id} variant="outline" size="sm">
125
+ <CardHeader>
126
+ <CardTitle>{item.title}</CardTitle>
127
+ </CardHeader>
128
+ </Card>
129
+ ))}
130
+ </Stack>
131
+ ```
132
+
133
+ **Inside a colored region (flat):**
134
+ ```tsx
135
+ <div className="bg-surface-sunken p-ds-07">
136
+ <Card variant="flat">…</Card> {/* nested-feel, no double shadow */}
137
+ </div>
138
+ ```
139
+
140
+ ## Rules
141
+
142
+ - **Never** `bg-surface-base` on a Card — cards sit on `surface-raised`. The pre-publish audit rejects this.
143
+ - **Never** combine `border-*` + `shadow-*` on a Card. Pick one (Card already does — don't override).
144
+ - **Use `interactive` + `onClick` + `aria-label`** for clickable cards. Don't wrap a Card in a `<button>` — broken nesting.
145
+ - **`size` on Card** drives sub-component padding. Don't set padding on `CardContent` directly.
146
+ - **Don't stack `default` cards inside another `default` card.** The nested one should be `flat` or `outline`.
147
+ - For row-style content (avatar + title + meta + actions), prefer `<ContentCard>` from `/composed/content-card` — it bundles the slots.
@@ -0,0 +1,167 @@
1
+ # Dialog
2
+
3
+ Centered modal overlay for focused tasks that interrupt the page flow.
4
+
5
+ ```tsx
6
+ import {
7
+ Dialog,
8
+ DialogTrigger,
9
+ DialogContent,
10
+ DialogHeader,
11
+ DialogTitle,
12
+ DialogDescription,
13
+ DialogFooter,
14
+ DialogClose,
15
+ } from '@devalok/shilp-sutra/ui/dialog'
16
+ ```
17
+
18
+ ## When to use
19
+
20
+ - Confirmations (destructive actions, accept-terms).
21
+ - Short forms that need full attention (rename, share, invite).
22
+ - Critical announcements requiring acknowledgment.
23
+ - Side-anchored drawer (settings panel, mobile nav)? Use `<Sheet>`.
24
+ - Lightweight rich tooltip with no required interaction? Use `<HoverCard>`.
25
+ - Interactive panel anchored to a trigger (filter, picker)? Use `<Popover>`.
26
+
27
+ On mobile, Dialog auto-promotes to a full-screen sheet — don't rebuild this manually.
28
+
29
+ ## Compound shape
30
+
31
+ ```
32
+ Dialog (root — open, onOpenChange, defaultOpen, modal)
33
+ DialogTrigger ← uses asChild around a Button
34
+ DialogContent ← portalled, traps focus
35
+ DialogHeader
36
+ DialogTitle ← REQUIRED for a11y
37
+ DialogDescription
38
+ [body content]
39
+ DialogFooter
40
+ DialogClose ← uses asChild around a Button
41
+ ```
42
+
43
+ ## Root props (passthrough to Radix)
44
+
45
+ | Prop | Type | Notes |
46
+ |---|---|---|
47
+ | `open` | `boolean` | Controlled mode. |
48
+ | `onOpenChange` | `(open: boolean) => void` | Fires on every state change. |
49
+ | `defaultOpen` | `boolean` | Uncontrolled. |
50
+ | `modal` | `boolean` | Default `true`. Set `false` for non-blocking overlays (rare). |
51
+
52
+ Styling props live on `DialogContent`. Trigger / Close use `asChild` to merge with your Button.
53
+
54
+ ## Examples
55
+
56
+ **Confirmation:**
57
+ ```tsx
58
+ <Dialog>
59
+ <DialogTrigger asChild>
60
+ <Button variant="soft" color="error">Delete project</Button>
61
+ </DialogTrigger>
62
+ <DialogContent>
63
+ <DialogHeader>
64
+ <DialogTitle>Delete this project?</DialogTitle>
65
+ <DialogDescription>
66
+ This permanently deletes all tasks, files, and history. Cannot be undone.
67
+ </DialogDescription>
68
+ </DialogHeader>
69
+ <DialogFooter>
70
+ <DialogClose asChild>
71
+ <Button variant="soft">Cancel</Button>
72
+ </DialogClose>
73
+ <Button variant="solid" color="error" onClick={handleDelete}>
74
+ Delete
75
+ </Button>
76
+ </DialogFooter>
77
+ </DialogContent>
78
+ </Dialog>
79
+ ```
80
+
81
+ **Short form:**
82
+ ```tsx
83
+ <Dialog open={open} onOpenChange={setOpen}>
84
+ <DialogContent>
85
+ <DialogHeader>
86
+ <DialogTitle>Rename workspace</DialogTitle>
87
+ </DialogHeader>
88
+ <Stack gap="ds-04">
89
+ <FormField>
90
+ <Label htmlFor="ws-name">Name</Label>
91
+ <Input id="ws-name" value={name} onChange={(e) => setName(e.target.value)} />
92
+ </FormField>
93
+ </Stack>
94
+ <DialogFooter>
95
+ <DialogClose asChild>
96
+ <Button variant="soft">Cancel</Button>
97
+ </DialogClose>
98
+ <Button onClickAsync={async () => { await api.rename(name); setOpen(false) }}>
99
+ Save
100
+ </Button>
101
+ </DialogFooter>
102
+ </DialogContent>
103
+ </Dialog>
104
+ ```
105
+
106
+ **Visually-hidden title (a11y compliance without showing the heading):**
107
+ ```tsx
108
+ <DialogContent>
109
+ <VisuallyHidden>
110
+ <DialogTitle>Image preview</DialogTitle>
111
+ </VisuallyHidden>
112
+ <img src={src} alt={alt} />
113
+ </DialogContent>
114
+ ```
115
+
116
+ **Programmatic close from a deep child:**
117
+ ```tsx
118
+ <DialogContent>
119
+ <Stack>
120
+ <FancyForm onSubmit={handleSubmit} />
121
+ <DialogClose asChild>
122
+ <Button variant="ghost">Done</Button>
123
+ </DialogClose>
124
+ </Stack>
125
+ </DialogContent>
126
+ ```
127
+
128
+ **Nested Popover inside Dialog:**
129
+ ```tsx
130
+ <Dialog>
131
+ <DialogContent>
132
+ <Popover>
133
+ <PopoverTrigger asChild>
134
+ <Button variant="soft">Pick a date</Button>
135
+ </PopoverTrigger>
136
+ <PopoverContent>
137
+ <Calendar value={date} onChange={setDate} />
138
+ </PopoverContent>
139
+ </Popover>
140
+ </DialogContent>
141
+ </Dialog>
142
+ ```
143
+
144
+ Popover uses `z-popover` (1400) which is above `z-dialog` — nesting stacks correctly without z-index fights.
145
+
146
+ ## Mobile behavior
147
+
148
+ On viewports below the `md` breakpoint, DialogContent auto-fullscreens with a top-anchored close button. Layouts that work on desktop in centered modal usually work on mobile in fullscreen as-is — but verify long forms scroll inside the dialog body, not the page.
149
+
150
+ ## Composability
151
+
152
+ - **Portal rendering:** DialogContent portals to `document.body`. CSS `overflow: hidden`, `transform`, or stacking contexts on ancestors don't clip it.
153
+ - **Focus management:** Focus traps inside while open. First focusable element receives focus. Returns to trigger on close.
154
+ - **Imperative close:** Wrap your own button with `<DialogClose asChild>` — no prop drilling.
155
+ - **z-index:** `z-dialog`. Popovers, DropdownMenus, Tooltips inside stack above using `z-popover`.
156
+
157
+ See `foundations/surfaces.md` for the overlay surface tokens, `foundations/motion.md` for the spring entry animation.
158
+
159
+ ## Rules
160
+
161
+ - Always render a `<DialogTitle>` — screen readers depend on it. If the design hides it visually, wrap it in `<VisuallyHidden>`.
162
+ - Use `<DialogTrigger asChild>` with a Button — don't use Dialog's default injected trigger.
163
+ - For destructive actions, place the destructive Button on the right with `color="error"`. Cancel (`<DialogClose>`) sits left with `variant="soft"`.
164
+ - Don't manipulate `open` from inside the content tree without going through `onOpenChange` or `<DialogClose>` — focus restoration breaks.
165
+ - Don't nest Dialogs. Use a single Dialog with stepped state, or close the first before opening the second.
166
+ - For side-anchored drawers, switch to `<Sheet>`. Don't recreate Sheet behavior with custom Dialog styling.
167
+ - If your Dialog is mostly read-only content (image preview, log viewer), still provide a `<DialogTitle>` for a11y, hidden if needed.