@hirobius/design-system 0.19.1 → 0.21.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 (175) hide show
  1. package/AGENTS.md +123 -0
  2. package/CONSUMING.md +11 -6
  3. package/DESIGN.md +34 -21
  4. package/NOTICE.md +1 -1
  5. package/README.md +48 -13
  6. package/codemods/hds-prefix.mjs +231 -0
  7. package/codemods/jsx-fold.mjs +515 -0
  8. package/codemods/not-found-pattern.mjs +71 -0
  9. package/codemods/patterns-subpath.mjs +242 -81
  10. package/codemods/patterns-subpath.names.json +41 -15
  11. package/codemods/removed-0.20.json +194 -0
  12. package/codemods/tile-grid.mjs +283 -0
  13. package/codemods/unrewritable.mjs +291 -0
  14. package/dist/chunks/DemoStateContext-Cz5TmrYu.js +32 -0
  15. package/dist/chunks/empty-state-DNoThgfV.js +1227 -0
  16. package/dist/chunks/form-By82qZcC.js +66 -0
  17. package/dist/contexts.js +157 -78
  18. package/dist/fonts/geist-mono-400.woff2 +0 -0
  19. package/dist/fonts/satoshi-400.woff2 +0 -0
  20. package/dist/fonts/satoshi-500.woff2 +0 -0
  21. package/dist/fonts/satoshi-700.woff2 +0 -0
  22. package/dist/fonts.css +53 -0
  23. package/dist/form.js +1 -1
  24. package/dist/hirobius-ui.js +1600 -5354
  25. package/dist/manifest.js +12 -13
  26. package/dist/patterns.js +796 -510
  27. package/dist/styles.css +2 -2
  28. package/dist/tokens.css +1 -1
  29. package/dist/tokens.js +165 -65
  30. package/dist/types/src/app/components/alert-dialog.d.ts +5 -0
  31. package/dist/types/src/app/components/asset-img.d.ts +1 -0
  32. package/dist/types/src/app/components/avatar-group.d.ts +2 -2
  33. package/dist/types/src/app/components/badge.d.ts +8 -3
  34. package/dist/types/src/app/components/blockquote.d.ts +2 -2
  35. package/dist/types/src/app/components/box-sx.d.ts +110 -13
  36. package/dist/types/src/app/components/box.d.ts +6 -3
  37. package/dist/types/src/app/components/breadcrumb.d.ts +0 -5
  38. package/dist/types/src/app/components/button.d.ts +35 -4
  39. package/dist/types/src/app/components/card.d.ts +10 -10
  40. package/dist/types/src/app/components/combobox.d.ts +47 -11
  41. package/dist/types/src/app/components/container.d.ts +2 -2
  42. package/dist/types/src/app/components/data-table-section.d.ts +2 -1
  43. package/dist/types/src/app/components/destructive-section.d.ts +1 -0
  44. package/dist/types/src/app/components/dialog.d.ts +8 -1
  45. package/dist/types/src/app/components/disclosure.d.ts +1 -5
  46. package/dist/types/src/app/components/error-pattern.d.ts +6 -1
  47. package/dist/types/src/app/components/field.d.ts +1 -2
  48. package/dist/types/src/app/components/form-actions.d.ts +1 -0
  49. package/dist/types/src/app/components/form-control.d.ts +10 -0
  50. package/dist/types/src/app/components/grid.d.ts +15 -6
  51. package/dist/types/src/app/components/hds-tooltip.d.ts +12 -2
  52. package/dist/types/src/app/components/icon.d.ts +1 -0
  53. package/dist/types/src/app/components/inline-code.d.ts +1 -2
  54. package/dist/types/src/app/components/input.d.ts +8 -5
  55. package/dist/types/src/app/components/kbd.d.ts +2 -2
  56. package/dist/types/src/app/components/metadata-list.d.ts +2 -2
  57. package/dist/types/src/app/components/metric-tiles.d.ts +1 -0
  58. package/dist/types/src/app/components/page-header.d.ts +0 -2
  59. package/dist/types/src/app/components/popover.d.ts +8 -0
  60. package/dist/types/src/app/components/progress.d.ts +33 -6
  61. package/dist/types/src/app/components/segmented-control.d.ts +2 -26
  62. package/dist/types/src/app/components/select.d.ts +10 -1
  63. package/dist/types/src/app/components/skeleton.d.ts +3 -4
  64. package/dist/types/src/app/components/spinner.d.ts +1 -2
  65. package/dist/types/src/app/components/stack.d.ts +4 -1
  66. package/dist/types/src/app/components/stat.d.ts +5 -5
  67. package/dist/types/src/app/components/status-list-item.d.ts +1 -2
  68. package/dist/types/src/app/components/status-tile.d.ts +3 -2
  69. package/dist/types/src/app/components/surface.d.ts +2 -3
  70. package/dist/types/src/app/components/switcher.d.ts +1 -1
  71. package/dist/types/src/app/components/table.d.ts +6 -15
  72. package/dist/types/src/app/components/tag.d.ts +0 -6
  73. package/dist/types/src/app/components/text.d.ts +54 -8
  74. package/dist/types/src/app/components/textarea.d.ts +1 -2
  75. package/dist/types/src/app/components/timestamp.d.ts +1 -0
  76. package/dist/types/src/app/components/toast.d.ts +1 -2
  77. package/dist/types/src/app/components/tooltip.d.ts +1 -1
  78. package/dist/types/src/app/context/DemoStateContext.d.ts +1 -1
  79. package/dist/types/src/app/context/RouterContext.d.ts +5 -5
  80. package/dist/types/src/app/data/manifest-types.d.ts +10 -5
  81. package/dist/types/src/app/design-system/generated-token-refs.d.ts +45 -28
  82. package/dist/types/src/app/design-system/generated-token-values.d.ts +4 -0
  83. package/dist/types/src/app/design-system/tokens.d.ts +139 -18
  84. package/dist/types/src/icons.d.ts +1 -1
  85. package/dist/types/src/index.d.ts +76 -164
  86. package/dist/types/src/patterns.d.ts +20 -25
  87. package/dist/variables.css +47 -33
  88. package/docs/CONSUMING.md +80 -48
  89. package/hirobius.tokens.json +125 -88
  90. package/llms.txt +166 -59
  91. package/mcp/catalog.mjs +366 -0
  92. package/mcp/guide.mjs +315 -0
  93. package/mcp/hds-mcp.mjs +49 -0
  94. package/mcp/server.mjs +94 -0
  95. package/package.json +48 -27
  96. package/public/hds-manifest.json +2667 -6705
  97. package/public/llms/components.txt +45 -426
  98. package/public/llms/layout.txt +5 -5
  99. package/public/llms-full.txt +245 -506
  100. package/public/llms.txt +166 -59
  101. package/scripts/eslint-plugin-hds/index.d.mts +29 -0
  102. package/scripts/eslint-plugin-hds/index.mjs +55 -0
  103. package/scripts/eslint-plugin-hds/package.json +30 -0
  104. package/scripts/eslint-plugin-hds/rules/no-raw-controls.mjs +47 -0
  105. package/scripts/eslint-plugin-hds/rules/no-raw-hex.mjs +70 -0
  106. package/scripts/eslint-plugin-hds/rules/no-raw-px-spacing.mjs +125 -0
  107. package/scripts/eslint-plugin-hds/rules/prefer-hds-layout-primitive.mjs +54 -0
  108. package/scripts/eslint-plugin-hds/rules/sx-token-first.mjs +111 -0
  109. package/scripts/eslint-plugin-hds/rules/utils.mjs +66 -0
  110. package/src/app/data/component-api.json +1082 -2913
  111. package/dist/chunks/LanguageContext-BxdCwiFg.js +0 -95
  112. package/dist/chunks/TokenDisplayContext-DKYJMVWk.js +0 -26
  113. package/dist/chunks/activity-feed-BGQ6v3wc.js +0 -22040
  114. package/dist/chunks/form-COP3vK9_.js +0 -66
  115. package/dist/types/src/app/components/activity-feed.d.ts +0 -73
  116. package/dist/types/src/app/components/app-shell.d.ts +0 -40
  117. package/dist/types/src/app/components/aspect-ratio.d.ts +0 -25
  118. package/dist/types/src/app/components/bleed.d.ts +0 -39
  119. package/dist/types/src/app/components/button-group.d.ts +0 -22
  120. package/dist/types/src/app/components/calendar.d.ts +0 -22
  121. package/dist/types/src/app/components/carousel.d.ts +0 -26
  122. package/dist/types/src/app/components/center.d.ts +0 -36
  123. package/dist/types/src/app/components/cinematic-link.d.ts +0 -18
  124. package/dist/types/src/app/components/circular-progress.d.ts +0 -28
  125. package/dist/types/src/app/components/cluster.d.ts +0 -39
  126. package/dist/types/src/app/components/command-palette.d.ts +0 -46
  127. package/dist/types/src/app/components/component-instance-matrix.d.ts +0 -27
  128. package/dist/types/src/app/components/context-menu.d.ts +0 -78
  129. package/dist/types/src/app/components/cover.d.ts +0 -43
  130. package/dist/types/src/app/components/date-input.d.ts +0 -31
  131. package/dist/types/src/app/components/date-range-input.d.ts +0 -34
  132. package/dist/types/src/app/components/date-time-input.d.ts +0 -30
  133. package/dist/types/src/app/components/doc-link-card.d.ts +0 -32
  134. package/dist/types/src/app/components/error-boundary.d.ts +0 -25
  135. package/dist/types/src/app/components/file-input.d.ts +0 -30
  136. package/dist/types/src/app/components/foundation-swatch.d.ts +0 -45
  137. package/dist/types/src/app/components/frame.d.ts +0 -38
  138. package/dist/types/src/app/components/heading-stack.d.ts +0 -52
  139. package/dist/types/src/app/components/history-card.d.ts +0 -12
  140. package/dist/types/src/app/components/hover-card.d.ts +0 -46
  141. package/dist/types/src/app/components/icon-button.d.ts +0 -22
  142. package/dist/types/src/app/components/image-lightbox.d.ts +0 -14
  143. package/dist/types/src/app/components/input-group.d.ts +0 -23
  144. package/dist/types/src/app/components/multi-selector.d.ts +0 -40
  145. package/dist/types/src/app/components/nav-group.d.ts +0 -29
  146. package/dist/types/src/app/components/nav-item.d.ts +0 -33
  147. package/dist/types/src/app/components/not-found-pattern.d.ts +0 -1
  148. package/dist/types/src/app/components/overflow-list.d.ts +0 -21
  149. package/dist/types/src/app/components/selectable-card.d.ts +0 -29
  150. package/dist/types/src/app/components/side-nav.d.ts +0 -23
  151. package/dist/types/src/app/components/sketch.d.ts +0 -19
  152. package/dist/types/src/app/components/stacked-card-rail.d.ts +0 -32
  153. package/dist/types/src/app/components/status-dot.d.ts +0 -25
  154. package/dist/types/src/app/components/stepper-field.d.ts +0 -12
  155. package/dist/types/src/app/components/stepper.d.ts +0 -33
  156. package/dist/types/src/app/components/text-lockup.d.ts +0 -38
  157. package/dist/types/src/app/components/tile-grid.d.ts +0 -17
  158. package/dist/types/src/app/components/time-input.d.ts +0 -24
  159. package/dist/types/src/app/components/toggle-button.d.ts +0 -32
  160. package/dist/types/src/app/components/token.d.ts +0 -90
  161. package/dist/types/src/app/components/tokenUtils.d.ts +0 -38
  162. package/dist/types/src/app/components/tokenizer.d.ts +0 -24
  163. package/dist/types/src/app/components/toolbar.d.ts +0 -36
  164. package/dist/types/src/app/components/top-nav.d.ts +0 -23
  165. package/dist/types/src/app/components/tree-list.d.ts +0 -30
  166. package/dist/types/src/app/data/nav-model.d.ts +0 -39
  167. package/dist/types/src/app/hooks/useFocusVisible.d.ts +0 -9
  168. package/dist/types/src/app/layouts/CaseStudyLayout.d.ts +0 -20
  169. package/dist/types/src/app/layouts/DocLayout.d.ts +0 -14
  170. package/dist/types/src/app/layouts/EmbeddedDocLayoutContext.d.ts +0 -6
  171. package/dist/types/src/app/layouts/HdsDocsShell.d.ts +0 -72
  172. package/dist/types/src/app/layouts/HdsSystemDocLayout.d.ts +0 -14
  173. package/dist/types/src/app/lib/hds-search.d.ts +0 -45
  174. package/dist/types/src/app/lib/nav-active.d.ts +0 -16
  175. package/dist/types/src/app/lib/navLevels.d.ts +0 -4
@@ -1,8 +1,67 @@
1
1
  # Hirobius Design System
2
2
 
3
- Generated: 2026-09-30T14:27:19.090Z
3
+ Generated: 2026-10-07T16:09:09.631Z
4
4
  Primary sources: `public/hds-manifest.json`, `src/app/data/component-api.json`, `hirobius.tokens.json`
5
5
 
6
+ ## Agents Using The Package: Start Here
7
+
8
+ - Read `AGENTS.md` (package root, `node_modules/@hirobius/design-system/AGENTS.md`) first: which component to use for each screen need, the imports, and the rules.
9
+ - MCP server `hds` ships in the package: run `npx hds-mcp` (stdio). Tools: `list_core`, `search_components`, `get_component`, `search_tokens`; each answer is under 2 KB.
10
+ - Lint before you finish: `import hds from '@hirobius/design-system/eslint-plugin'` and spread `hds.configs.recommended` into `eslint.config.mjs`.
11
+
12
+ ## Pick By Need
13
+
14
+ One answer per need. Use the components after the arrow; what follows "Not:" is what agents reached for instead, which is the wrong answer for that need even where it has a purpose of its own. Generated from `mcp/guide.mjs`, the same list as `AGENTS.md`.
15
+
16
+ - **Outermost wrapper of a screen** → `Page`, `Stack`. Not: `Container`, `Box`, a bare Stack as the root.
17
+ `<Page><Stack gap="spacious">…one child per section…</Stack></Page>`. `Container` is for a full-bleed surface that is not a page.
18
+ - **Screen title with breadcrumb, status and actions** → `PageHeader`, `Breadcrumb`, `Badge`. Not: Text as the page title, a Stack row of Text and Badge.
19
+ Exactly one per screen: `<PageHeader breadcrumb={<Breadcrumb items={[{ label, href }, { label }]} />} title="…" status={<Badge tone="success">Active</Badge>} />`.
20
+ - **Row of headline numbers (stats, KPIs, counts, totals)** → `MetricTiles`, `MetricTile`. Not: `Stat`, `StatusTile`, `CardMetric`, Card with Text.
21
+ `<MetricTiles><MetricTile label="Open projects" value="5" />…</MetricTiles>`; one MetricTile per number. `Stat` is for a number inside prose or a dense list, `Card.Metric` for one inside an existing `Card`, `StatusTile` for a state with notes (never a number).
22
+ - **Table of records with row actions** → `DataTableSection`, `Badge`, `Button`. Not: Table on its own, Card around a Table.
23
+ `<DataTableSection title="Projects" columns={[{ key, label }]} rows={[{ key, cells: [...], actions: <Stack direction="row" gap="tight">…</Stack> }]} />`; leave the actions column out of `columns`.
24
+ - **Switch between panels of related content** → `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`. Not: `SegmentedControl`, Button rows.
25
+ `<Tabs defaultValue="a"><TabsList><TabsTrigger value="a">A</TabsTrigger></TabsList><TabsContent value="a">…</TabsContent></Tabs>`.
26
+ - **Status label in a header or table cell** → `Badge`. Not: `Tag`, `Text`.
27
+ `<Badge tone="success|info|warning|danger|neutral|inProgress">…</Badge>`; tone is the only styling input.
28
+ - **Form with fields and a submit button** → `Form`, `Textarea`, `Input`, `FormActions`, `Button`. Not: a raw `<form>`, a Stack of Buttons as the footer.
29
+ `<Form onSubmit={…}><Textarea label="Notes" … /><FormActions primary={<Button type="submit">Save note</Button>} /></Form>`; every field takes its own `label`.
30
+ - **Confirm a destructive or irreversible action (delete, archive, remove)** → `AlertDialog`, `Button`. Not: `Dialog`, `window.confirm`.
31
+ Trigger, title, description, Cancel and Action parts: `<AlertDialog><AlertDialog.Trigger asChild><Button …>Archive</Button></AlertDialog.Trigger><AlertDialog.Content><AlertDialog.Header><AlertDialog.Title>…</AlertDialog.Title><AlertDialog.Description>…</AlertDialog.Description></AlertDialog.Header><AlertDialog.Footer><AlertDialog.Cancel asChild><Button variant="secondary">Cancel</Button></AlertDialog.Cancel><AlertDialog.Action asChild><Button tone="danger" onClick={…}>Archive</Button></AlertDialog.Action></AlertDialog.Footer></AlertDialog.Content></AlertDialog>`. Focus moves in and returns to the trigger for you.
32
+ - **Brief confirmation after an action (saved, archived, sent)** → `ToastProvider`, `useToast`. Not: `Badge`, `Alert`, `Text`.
33
+ Wrap the app root once in `<ToastProvider>`, then `toast({ title: "Note saved", tone: "success" })` from `useToast()`.
34
+ - **Message that stays next to its content (error, warning, info)** → `Alert`. Not: Text in a coloured Surface.
35
+ `<Alert tone="warning">…</Alert>`.
36
+ - **Button or row action** → `Button`. Not: a raw `<button>`, InlineLink for an action.
37
+ Row actions: `<Button variant="tertiary" size="sm" aria-label="View Brand refresh">View</Button>`; the destructive one adds `tone="danger"`.
38
+ - **Space between sections or items** → `Stack`, `Grid`. Not: margins on children, raw px gaps such as px8.
39
+ Stack `gap`: `spacious` between page sections, `normal` inside a section, `tight` between buttons. `Grid` for two-dimensional layout.
40
+ - **Padded background or a titled content object** → `Surface`, `Card`. Not: a Box with background and padding.
41
+ `Surface` for a plain padded background; `Card` only when the content has a title or can be pressed.
42
+ - **Progress bar or ring** → `Progress`. Not: a hand-built bar.
43
+ `<Progress value={60} label="Upload" />`; inside an existing `Card`, `CardProgress`.
44
+ - **Body copy** → `Text`. Not: Text as a section heading.
45
+ Section headings come from the pattern that owns the section (`PageHeader` `title`, `DataTableSection` `title`), not from extra Text.
46
+ - **Empty state for a list or section** → `EmptyState`. Not: Text saying "No items".
47
+ `DataTableSection` shows its own through `emptyTitle`; use `EmptyState` elsewhere.
48
+ - **The one irreversible action of a screen (delete the client, close the account)** → `DestructiveSection`. Not: a danger Button that acts without a confirm.
49
+ Last on the screen: `<DestructiveSection title="Delete client" description="…" actionLabel="Delete" onConfirm={…} />`; it opens its own AlertDialog. A per-row destructive action uses AlertDialog directly.
50
+ - **Read-only details as label and value pairs** → `MetadataList`, `Field`. Not: a Table with two columns, Text pairs in a Stack.
51
+ `<MetadataList items={[{ term: "Owner", description: "Ada" }]} />` for a set; `Field` for one pair.
52
+ - **A person, or a group of people** → `Avatar`, `AvatarGroup`. Not: an image in a rounded Box.
53
+ `<Avatar src alt initials />`; several in a row: `<AvatarGroup max={3}>…Avatars…</AvatarGroup>`.
54
+ - **A date or time** → `Timestamp`. Not: a formatted string in Text.
55
+ `<Timestamp date="2026-10-14" />` renders a `<time dateTime>`; a date in a table cell may stay plain text.
56
+ - **A keyboard key or shortcut hint** → `Kbd`. Not: `InlineCode`, Text in a Box.
57
+ `<Kbd>⌘K</Kbd>`.
58
+ - **A quotation with attribution** → `Blockquote`. Not: Callout for a quote, italic Text.
59
+ `<Blockquote attribution="Ada Lovelace">…</Blockquote>`.
60
+ - **A fixed-width side rail beside fluid content, or a row that stacks when narrow** → `Sidebar`, `Switcher`. Not: a Grid with media queries.
61
+ `Sidebar` (`side`, `sideWidth`) for rail + content; `Switcher` (`threshold`) for a row that flips to a column. Neither needs a media query.
62
+ - **Keep an element stuck while its region scrolls** → `Pin`. Not: position: sticky in style.
63
+ `<Pin top="…">…</Pin>` inside the scrolling region.
64
+
6
65
  ## System Architecture
7
66
 
8
67
  - Engine: React + TypeScript
@@ -36,57 +95,105 @@ This file intentionally does not embed prop tables or long-form API docs.
36
95
  - Detailed prop types, unions, defaults, and descriptions: read `src/app/data/component-api.json`.
37
96
  - Runtime behavior: read the actual component source under `src/app/components/` (and associated styles).
38
97
 
98
+ ## Core set
99
+
100
+ The 43 components of the ratified core set (hds#254, ADR-031): brand-neutral, composable, imported from the package root. Build a screen from these first. Each has `core: true` in `public/hds-manifest.json` and is marked `[core]` under "Which one when". Composed pattern modules (page shells, forms, code blocks, page sections) are imported from `@hirobius/design-system/patterns`.
101
+
102
+ - Button (Actions)
103
+ - Avatar (Display)
104
+ - Card (Display)
105
+ - EmptyState (Display)
106
+ - Field (Display)
107
+ - Icon (Display)
108
+ - Kbd (Display)
109
+ - Table (Display)
110
+ - Alert (Feedback)
111
+ - Badge (Feedback)
112
+ - Progress (Feedback)
113
+ - Skeleton (Feedback)
114
+ - Spinner (Feedback)
115
+ - ToastProvider (Feedback)
116
+ - Checkbox (Inputs)
117
+ - Combobox (Inputs)
118
+ - Input (Inputs)
119
+ - Radio (Inputs)
120
+ - SegmentedControl (Inputs)
121
+ - Select (Inputs)
122
+ - Slider (Inputs)
123
+ - Tag (Inputs)
124
+ - Textarea (Inputs)
125
+ - Toggle (Inputs)
126
+ - Box (Layout)
127
+ - Container (Layout)
128
+ - Disclosure (Layout)
129
+ - Divider (Layout)
130
+ - Grid (Layout)
131
+ - Stack (Layout)
132
+ - Surface (Layout)
133
+ - Breadcrumb (Navigation)
134
+ - InlineLink (Navigation)
135
+ - Pagination (Navigation)
136
+ - Tabs (Navigation)
137
+ - Dialog (Overlays)
138
+ - Menu (Overlays)
139
+ - Popover (Overlays)
140
+ - Tooltip (Overlays)
141
+ - HdsRouterProvider (Theming)
142
+ - HdsThemeProvider (Theming)
143
+ - Text (Typography)
144
+ - VisuallyHidden (Utility)
145
+
39
146
  ## Which one when
40
147
 
41
- One line per component that declares when to use it (`@usage` / `@useInstead` in its JSDoc). Full detail, including `usage.whenNot`, `keyboard` and `aiRules`, is in `public/hds-manifest.json` and `src/app/data/component-api.json`.
42
-
43
- Alert: Show a persistent inline message with a severity, next to the content it concerns. Use instead: ToastProvider, Badge
44
- Avatar: Show a person or entity as an image with an initials fallback. Use instead: Icon, Badge
45
- Badge: Label a status, state or count with a compact neutral or semantic chip. Use instead: Tag, Alert
46
- Box: Build one-off layout that no named layout primitive covers, using token keys through `sx`. Use instead: Stack, Grid, Container
47
- Breadcrumb: Show where the current page sits in a hierarchy and link back up it. Use instead: Tabs, Pagination
48
- Button: Trigger an action (submit, save, open a dialog) with a text label and optional icons; for an icon-only control pass iconOnly with iconLeft. Use instead: InlineLink
49
- ButtonGroup: Attach related Buttons into one segmented row or column that reads as a single control. Use instead: SegmentedControl, Stack
50
- Card: Group related content on a raised surface with header, body, footer and metric slots. Use instead: Surface, Card.Metric
51
- CardMetric: Show one headline figure (a KPI, a count, a total) with its label inside a Card.
52
- Checkbox: Let people tick one or more independent options, or a parent choice that is partly selected. Use instead: Toggle, Radio
53
- Combobox: Pick one value from a long list of options, with a search field that filters as you type. Use instead: Select, Menu
54
- Container: Constrain a page region to the content or maximum width and centre it. Use instead: Stack, Grid, Box
55
- ContextMenu: Offer actions for an item on right-click, or the equivalent keyboard gesture. Use instead: Menu
56
- Dialog: Interrupt the page with a modal that needs a decision or focused input before people continue. Use instead: HoverCard, Alert, ToastProvider
57
- Disclosure: Hide optional explanatory content behind a heading that people can expand. Use instead: Tabs
58
- Divider: Separate two regions of content with a thin semantic rule. Use instead: Stack, Surface
59
- EmptyState: Say that a list, grid or section has no data yet, with a title and an optional description. Use instead: Skeleton, Alert
60
- Field: Show a labelled read-only value, such as a name and its value in a detail view. Use instead: Input, Card.Metric
61
- Grid: Lay out content in responsive columns with token-governed gaps, or align nested content with subgrid. Use instead: Stack, Box
62
- HdsThemeProvider: Wrap an app or region once to set theme, density and other root attributes for everything inside it. Use instead: Surface
63
- HoverCard: Preview extra detail about a link or mention when it is hovered or focused, without navigating. Use instead: Dialog, Tooltip
64
- Icon: Draw a Lucide glyph at a size and colour from the design tokens. Use instead: Button, Avatar
65
- IconButton: Trigger an action from a compact icon-only control, always with an accessible label. Use instead: Button
66
- InlineLink: Link to a route or external URL from inside body copy. Use instead: Button, Text
67
- Input: Collect a single line of text, a number, an email or a password, with label, helper and error slots. Use instead: Textarea, Select, Combobox
68
- Kbd: Show a keyboard key or shortcut hint inline, as a native kbd element. Use instead: Text
69
- Menu: Offer a list of actions from a trigger, closed until opened. Use instead: Select
70
- Pagination: Move between pages of a long result set, with truncated page numbers. Use instead: Breadcrumb
71
- Progress: Show how far a task has got, as a bar that is determinate or indeterminate. Use instead: Spinner, Skeleton
72
- Radio: Choose exactly one option from a small set that should stay visible together. Use instead: Checkbox, Select, SegmentedControl
73
- SegmentedControl: Switch between two to five mutually exclusive views or modes in a compact row. Use instead: Radio, Tabs, Select
74
- Select: Choose one value from a short list of options in a form field. Use instead: Menu, Combobox
75
- Skeleton: Reserve the shape of content that is still loading, so the layout does not jump. Use instead: Progress, Spinner
76
- Slider: Pick a number within a known range by dragging, when the exact value matters less than the position. Use instead: Input
77
- Spinner: Show that something is loading when the wait is short or has no measurable end. Use instead: Progress, Skeleton
78
- Stack: Space a row or column of content with token gaps and simple flex alignment. Use instead: Grid, Surface
79
- Stat: Show one standalone headline figure with its label and an optional sub-line. Use instead: Card.Metric, StatusTile
80
- StatusTile: Show a titled status block with optional notes and a trailing badge or affordance. Use instead: Card.Metric, StatusListItem
81
- Surface: Wrap content in a padded, background-bearing inset surface with no header or footer anatomy. Use instead: Card, Stack
82
- Table: Show rows of structured data in aligned columns, with optional sorting. Use instead: Field, Stack
83
- Tabs: Switch between panels of related content in the same place, one visible at a time. Use instead: SegmentedControl, Disclosure
84
- Tag: Show an interactive filter or category chip that people can toggle. Use instead: Badge
85
- Text: Set any text on the type ramp, with the element chosen by `as`. Use instead: InlineLink, Kbd
86
- Textarea: Collect multi-line free text such as a message, note or description. Use instead: Input
87
- ToastProvider: Confirm an action with a brief message that appears and dismisses itself, through useToast. Use instead: Alert, Dialog
88
- Toggle: Switch one setting on or off, with the effect applying immediately. Use instead: Checkbox, Radio
89
- VisuallyHidden: Give assistive technology a label or description that sighted people do not need to see. Use instead: Text
148
+ One line per component that declares when to use it (`@usage` / `@useInstead` in its JSDoc); `[core]` marks the core set. Full detail, including `usage.whenNot`, `keyboard` and `aiRules`, is in `public/hds-manifest.json` and `src/app/data/component-api.json`.
149
+
150
+ Alert: [core] Show a persistent inline message with a severity, next to the content it concerns. Use instead: ToastProvider, Badge
151
+ AlertDialog: Confirm a destructive or irreversible action (delete, archive, remove) before it runs, with Cancel and Action buttons. Use instead: Dialog, ToastProvider
152
+ Avatar: [core] Show a person or entity as an image with an initials fallback. Use instead: Icon, Badge
153
+ Badge: [core] Label a status, state or count with a compact neutral or semantic chip, or a dot. Use instead: Tag, Alert
154
+ Box: [core] Build one-off layout that no named layout primitive covers, using token keys through `sx`. Use instead: Stack, Grid, Container
155
+ Breadcrumb: [core] Show where the current page sits in a hierarchy and link back up it. Use instead: Tabs, Pagination
156
+ Button: [core] Trigger an action (submit, save, open a dialog) with a text label and optional icons; for an icon-only control pass iconOnly with iconLeft and label; for an on/off toggle pass pressed. Use instead: InlineLink
157
+ Card: [core] Group related content on a raised surface with header, body, footer and metric slots, or as one selectable option. Use instead: Surface, Card.Metric
158
+ CardMetric: Show one headline figure (a KPI, a count, a total) with its label inside a Card. Use instead: MetricTiles
159
+ Checkbox: [core] Let people tick one or more independent options, or a parent choice that is partly selected. Use instead: Toggle, Radio
160
+ Combobox: [core] Pick one value, or several with `multiple`, from a long list of options, with a search field that filters as you type. Use instead: Select, Menu
161
+ Container: [core] Constrain a page region to the content or maximum width and centre it. Use instead: Stack, Grid, Box
162
+ Dialog: [core] Interrupt the page with a modal that needs a decision or focused input before people continue. Use instead: Tooltip, Alert, ToastProvider, AlertDialog
163
+ Disclosure: [core] Hide optional explanatory content behind a heading that people can expand. Use instead: Tabs
164
+ Divider: [core] Separate two regions of content with a thin semantic rule. Use instead: Stack, Surface
165
+ EmptyState: [core] Say that a list, grid or section has no data yet, with a title and an optional description. Use instead: Skeleton, Alert
166
+ Field: [core] Show a labelled read-only value, such as a name and its value in a detail view. Use instead: Input, Card.Metric
167
+ Grid: [core] Lay out content in responsive columns with token-governed gaps, or align nested content with subgrid. Use instead: Stack, Box
168
+ HdsRouterProvider: [core] Wrap the app root once so HDS links and navigation use your client router; with no provider they stay plain anchors.
169
+ HdsThemeProvider: [core] Wrap an app or region once to set theme, density and other root attributes for everything inside it. Use instead: Surface
170
+ Icon: [core] Draw a Lucide glyph at a size and colour from the design tokens. Use instead: Button, Avatar
171
+ InlineLink: [core] Link to a route or external URL from inside body copy. Use instead: Button, Text
172
+ Input: [core] Collect a single line of text, a number, an email, a password, a date or a time, with label, helper and error slots. Use instead: Textarea, Select, Combobox
173
+ Kbd: [core] Show a keyboard key or shortcut hint inline, as a native kbd element. Use instead: Text
174
+ Menu: [core] Offer a list of actions from a trigger, closed until opened. Use instead: Select
175
+ Pagination: [core] Move between pages of a long result set, with truncated page numbers. Use instead: Breadcrumb
176
+ Popover: [core] Anchor interactive content, such as a short form, a filter set or a picker, to a trigger people click to open. Use instead: Tooltip, Menu, Dialog
177
+ Progress: [core] Show how far a task has got, as a bar or ring that is determinate or indeterminate. Use instead: Spinner, Skeleton
178
+ Radio: [core] Choose exactly one option from a small set that should stay visible together. Use instead: Checkbox, Select, SegmentedControl
179
+ SegmentedControl: [core] Switch between two to five mutually exclusive views or modes in a compact row. Use instead: Radio, Tabs, Select
180
+ Select: [core] Choose one value from a short list of options in a form field. Use instead: Menu, Combobox
181
+ Skeleton: [core] Reserve the shape of content that is still loading, so the layout does not jump. Use instead: Progress, Spinner
182
+ Slider: [core] Pick a number within a known range by dragging, when the exact value matters less than the position. Use instead: Input
183
+ Spinner: [core] Show that something is loading when the wait is short or has no measurable end. Use instead: Progress, Skeleton
184
+ Stack: [core] Space a row or column of content with token gaps and simple flex alignment. Use instead: Grid, Surface
185
+ Stat: Show one headline figure inline, inside prose or a dense list, with its label and an optional sub-line. Use instead: MetricTiles, Card.Metric, StatusTile
186
+ StatusTile: Show a titled status block with optional notes and a trailing badge or affordance. Use instead: MetricTiles, Card.Metric, StatusListItem
187
+ Surface: [core] Wrap content in a padded, background-bearing inset surface with no header or footer anatomy. Use instead: Card, Stack
188
+ Table: [core] Show rows of structured data in aligned columns, with optional sorting. Use instead: Field, Stack
189
+ Tabs: [core] Switch between panels of related content in the same place, one visible at a time. Use instead: SegmentedControl, Disclosure
190
+ Tag: [core] Show an interactive filter or category chip that people can toggle. Use instead: Badge
191
+ Text: [core] Set any text on the type ramp, with the element chosen by `as`. Use instead: InlineLink, Kbd
192
+ Textarea: [core] Collect multi-line free text such as a message, note or description. Use instead: Input
193
+ ToastProvider: [core] Confirm an action with a brief message that appears and dismisses itself, through useToast. Use instead: Alert, Dialog
194
+ Toggle: [core] Switch one setting on or off, with the effect applying immediately. Use instead: Checkbox, Radio
195
+ Tooltip: [core] Name an icon-only control, or add a short hint that appears on hover and keyboard focus. Use instead: Popover, Text
196
+ VisuallyHidden: [core] Give assistive technology a label or description that sighted people do not need to see. Use instead: Text
90
197
 
91
198
  ## Tokens (Primary Source Of Truth)
92
199
 
@@ -111,16 +218,16 @@ VisuallyHidden: Give assistive technology a label or description that sighted pe
111
218
 
112
219
  Canonical skeleton, outermost to innermost:
113
220
 
114
- 1. `Page` (or `Container`/`Center` for a full-bleed, non-page surface) for the outermost width constraint. Never import `Container` directly inside `src/app/pages/**` — use `Page`, which wraps it and owns vertical rhythm.
221
+ 1. `Page` (from `@hirobius/design-system/patterns`; or `Container` for a full-bleed, non-page surface, with a `Box` inside it for a horizontal gutter) for the outermost width constraint. Never import `Container` directly inside `src/app/pages/**` — use `Page`, which wraps it and owns vertical rhythm.
115
222
  2. `Stack` (vertical rhythm between sections) or `Grid` (two-dimensional/column layout) for the structural skeleton. One section = one Section/Stack — never add a second wrapper to fake a section boundary.
116
- 3. Reach for a named every-layout primitive before hand-rolling flex/grid math for a common intent: `Cluster` (wrapping row of same-ish things), `Center` (centered max-width column with optional gutter), `Sidebar` (fixed-width rail + fluid content, no media query), `Switcher` (row that flips to a column below a threshold, no media query), `Cover` (full-height shell with a centered main region), `Frame` (aspect-ratio-locked clipped media box), `Bleed` (controlled negative margin to escape a parent padding).
223
+ 3. Reach for a named layout primitive before hand-rolling flex/grid math for a common intent: `Stack direction="row" wrap="wrap"` (wrapping row of same-ish things), `Sidebar` (fixed-width rail + fluid content, no media query), `Switcher` (row that flips to a column below a threshold, no media query). A full-height shell with a centered main region, an aspect-ratio-locked media box or a negative margin that escapes a parent padding is a `Box` with `style` (not `sx`, which applies on the client only).
117
224
  4. `Surface` for any background-bearing, padded wrapper (card, panel, inset). Never a raw element with backgroundColor + padding hand-rolled inline.
118
- 5. Use the screen patterns (`@hirobius/design-system/patterns`) for the parts every screen repeats: `PageHeader` once at the top (breadcrumb, `heading2` title, status, actions), `MetricTiles` for any row of headline numbers, `FormActions` for a form footer (primary right-most and last in DOM order, destructive on the far left), `DataTableSection` for a titled table (toolbar slot, consumer row actions, empty state, scrolls on narrow widths without a caller `minWidth`), and `DestructiveSection` last for an irreversible action (danger button, then an `AlertDialog` confirm). Pick between `MetricTiles`, `Stat`, `Card.Metric` and `StatusTile`, and between `Card` and `Surface`, with the "Which one, when" table in `DESIGN.md`.
225
+ 5. Use the screen patterns (`@hirobius/design-system/patterns`) for the parts every screen repeats: `PageHeader` once at the top (breadcrumb, `heading2` title, status, actions), `MetricTiles` for any row of headline numbers, `FormActions` for a form footer (primary right-most and last in DOM order, destructive on the far left), `DataTableSection` for a titled table (toolbar slot, consumer row actions, empty state, scrolls on narrow widths without a caller `minWidth`), and `DestructiveSection` last for an irreversible action (danger button, then an `AlertDialog` confirm). A row of headline numbers is always `MetricTiles`; `Stat`, `Card.Metric` and `StatusTile` answer other needs (see "Pick By Need" in `llms.txt`). Pick between `Card` and `Surface` with the "Which one, when" table in `DESIGN.md`.
119
226
  6. `Box` `sx` LAST — only for genuinely one-off layout that no named primitive covers. `sx` spacing/color keys MUST be HDS token keys, never raw hex/px.
120
227
 
121
228
  Negative rules (apply everywhere, checked by `scripts/audit-tokens.mjs --full`, `check-hardcoded-spacing.mjs`, `check-hardcoded-colors.mjs`):
122
229
 
123
- - No inline margins on children to fake spacing between siblings — gap/spacing on the parent (Stack/Grid/Cluster/Switcher/Sidebar/Cover) owns rhythm, not margin on the child.
230
+ - No inline margins on children to fake spacing between siblings — gap/spacing on the parent (Stack/Grid/Switcher/Sidebar) owns rhythm, not margin on the child.
124
231
  - No raw px or hex values in any layout or color prop — every spacing value comes from the semantic gap scale (`tight | normal | inset | spacious`, or a component/subgrid step) and every color comes from a `semantic.color.*` token.
125
232
  - No repeated outlined cards as the default structure for roadmap/status/process/overview UI — use open bands, dividers, rails, disclosures, and whitespace instead.
126
233
 
@@ -175,7 +282,7 @@ When building any card component or card-like surface, ALL of the following rule
175
282
 
176
283
  Cards default to `elevation.flat`. Popovers/tooltips/dropdowns use `elevation.floating`. Dialogs/sheets use `elevation.overlay`. Interactive cards lift to `elevation.raised` on hover. Never combine `raised` with a border — depth is one mechanism (border OR shadow), not both stacked.
177
284
 
178
- Overlays (Dialog, AlertDialog, Menu, ContextMenu, Popover, Select, HoverCard, Tooltip) portal into the nearest `data-hds` scope, so they inherit its theme (for example `<div data-hds data-theme="dark">`); pass `container` on the Content part to override.
285
+ Overlays (Dialog, AlertDialog, Menu, Popover, Select, Tooltip) portal into the nearest `data-hds` scope, so they inherit its theme (for example `<div data-hds data-theme="dark">`); pass `container` on the Content part to override.
179
286
 
180
287
  ## Slices And Full Bundle
181
288
 
@@ -219,19 +326,19 @@ On-demand only (load only if explicitly requested or the task clearly requires i
219
326
  Icons come from the curated subpath `@hirobius/design-system/icons`; nothing extra to install.
220
327
 
221
328
  ```tsx
222
- import { IconButton } from '@hirobius/design-system';
329
+ import { Button, Icon } from '@hirobius/design-system';
223
330
  import { Ellipsis } from '@hirobius/design-system/icons';
224
331
 
225
- <IconButton icon={Ellipsis} label="Row actions" />
332
+ <Button iconOnly label="Row actions" iconLeft={<Icon icon={Ellipsis} />} />
226
333
  ```
227
334
 
228
- Rule: icon-only actions (row menus, close, edit) use `IconButton`; do not hand-roll a button with a glyph or text "...".
335
+ Rule: icon-only actions (row menus, close, edit) use `Button iconOnly` with a `label` and an `Icon` in `iconLeft`; do not hand-roll a button with a glyph or text "...".
229
336
 
230
337
  Names: Archive, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Bell, Bookmark, Calendar, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, CircleAlert, CircleCheck, CircleHelp, Clock, Copy, Download, Ellipsis, EllipsisVertical, ExternalLink, Eye, EyeOff, FileText, Funnel, Heart, House, Image, Info, Link, Lock, LogIn, LogOut, Mail, Menu, Minus, Pencil, Plus, RefreshCw, Save, Search, Settings, Share, Star, Trash2, TriangleAlert, Upload, User, X
231
338
 
232
339
  Legacy names map to canonical ones: MoreHorizontal -> Ellipsis, MoreVertical -> EllipsisVertical, AlertTriangle -> TriangleAlert, Home -> House, Filter -> Funnel.
233
340
 
234
- Name collisions: `Calendar` and `Menu` share names with HDS components; alias the icon: `import { Calendar as CalendarIcon, Menu as MenuIcon } from '@hirobius/design-system/icons'`. For an icon outside the set, install `lucide-react@0.487.0` (same version keeps the `LucideIcon` type identical).
341
+ Name collisions: `Menu` shares a name with an HDS component; alias the icon: `import { Menu as MenuIcon } from '@hirobius/design-system/icons'`. For an icon outside the set, install `lucide-react@0.487.0` (same version keeps the `LucideIcon` type identical).
235
342
 
236
343
  ## Token Rules
237
344
 
@@ -294,28 +401,29 @@ Feedback hues are never decorative; do not use them as accents. Light and dark m
294
401
  <!-- auto:start:typography -->
295
402
  HDS ships three typefaces — each with a distinct and exclusive role:
296
403
 
297
- - **Display / Heading font**: Satoshi. Bound exclusively to `display`, `h1`, `h2`, and `h3` styles. Never used for body copy or UI labels.
404
+ - **Display / Heading font**: Satoshi. Bound exclusively to the `display` and `title` styles. Never used for body copy or UI labels.
298
405
  - **Body / UI font**: Satoshi. All prose, labels, small text, captions, and UI copy.
299
406
  - **Mono font**: Geist Mono. Reserved for tokens, code, technical callouts, and metric readouts.
300
407
 
301
- Weights declared: `400` regular, `500` medium, `700` bold. Heading styles (display · h1 · h2 · h3) use `700` bold; body, UI, and caption use `500` medium. Declared but unused by any semantic role: `400` regular.
408
+ Weights declared: `400` regular, `500` medium, `700` bold. Heading styles (display · title) use `700` bold; body, UI, and caption use `400` regular / `500` medium.
302
409
 
303
410
  ### Type ramp
304
411
 
305
412
  | Role | Size (desktop max) | Weight | Use |
306
413
  | --- | --- | --- | --- |
307
- | `semantic.typography.display` | 60px | 700 | Display headline |
308
- | `semantic.typography.h1` | 48px | 700 | Primary section headings (h1) |
309
- | `semantic.typography.h2` | 30px | 700 | Secondary section headings (h2) |
310
- | `semantic.typography.h3` | 20px | 700 | Component and card headers (h3) |
311
- | `semantic.typography.body` | 16px | 500 | Body prose |
312
- | `semantic.typography.ui` | 14px | 500 | UI text for nav, labels, captions |
313
- | `semantic.typography.caption` | 12px | 500 | Caption / helper text |
314
- | `semantic.typography.eyebrow` | 12px | 500 | Eyebrow / kicker label |
315
- | `semantic.typography.mono` | 14px | 400 | Monospace for code, token names, shortcuts |
414
+ | `semantic.typography.display` | 48px | 700 | One per page, hero only |
415
+ | `semantic.typography.title` | 24px | 700 | Section and card headings |
416
+ | `semantic.typography.body` | 16px | 400 | Prose |
417
+ | `semantic.typography.ui` | 14px | 500 | Controls, nav, table text, values |
418
+ | `semantic.typography.caption` | 12px | 500 | Metadata, help text and labels, in sentence case |
419
+ | `semantic.typography.mono` | 13px | 400 | Code and ids |
420
+ | `semantic.typography.h1` | 24px | 700 | deprecated: use semantic.typography.title |
421
+ | `semantic.typography.h2` | 24px | 700 | deprecated: use semantic.typography.title |
422
+ | `semantic.typography.h3` | 24px | 700 | deprecated: use semantic.typography.title |
423
+ | `semantic.typography.eyebrow` | 12px | 500 | deprecated: use semantic.typography.caption |
316
424
  | `semantic.typography.lineHeight.none` | | | Semantic alias for leading-none (1) |
317
425
 
318
- > Responsive `clamp()` overrides live in `src/styles/theme.css`; tokens store the desktop-max static value.
426
+ > The ramp is static: tokens store the value every surface renders. `h1`, `h2`, `h3` and `eyebrow` are deprecated aliases and go in 1.0.0.
319
427
  <!-- auto:end:typography -->
320
428
 
321
429
  ## Spacing
@@ -325,9 +433,19 @@ Weights declared: `400` regular, `500` medium, `700` bold. Heading styles (displ
325
433
 
326
434
  Scale: `0px` (`primitive.space.0`) · `1px` (`primitive.space.px1`) · `2px` (`primitive.space.px2`) · `4px` (`primitive.space.1`) · `6px` (`primitive.space.px6`) · `8px` (`primitive.space.2`) · `10px` (`primitive.space.px10`) · `12px` (`primitive.space.3`) · `16px` (`primitive.space.4`) · `20px` (`primitive.space.5`) · `24px` (`primitive.space.6`) · `28px` (`primitive.space.7`) · `32px` (`primitive.space.8`) · `40px` (`primitive.space.10`) · `48px` (`primitive.space.12`) · `64px` (`primitive.space.16`) · `80px` (`primitive.space.20`) · `96px` (`primitive.space.24`) · `128px` (`primitive.space.32`)
327
435
 
328
- Use `primitive.space.*` for layout rhythm, padding, and gaps. Use `semantic.space.*` aliases (e.g. `semantic.space.surface.padding`) when the purpose is established. `data-density="compact"` (on `<html>` or the `[data-hds]` scope element) remaps `semantic.space.scale.*`, `semantic.space.surface.padding` and `semantic.space.region.gutter` one step down the scale, and `Table` follows it. The `--hds-space-{xs…4xl}` vars are a legacy bridge that no component reads.
436
+ Layout rhythm, padding, and gaps use the `semantic.space.scale.{xs…xl}` steps (and `none`), never a raw `primitive.space.*` value or a numeric Tailwind class; `primitive.space.*` is the ramp behind the scale. Use `semantic.space.*` aliases (e.g. `semantic.space.surface.padding`) when the purpose is established. `data-density="compact"` (on `<html>` or the `[data-hds]` scope element) remaps `semantic.space.scale.*`, `semantic.space.surface.padding` and `semantic.space.region.gutter` one step down the scale, and `Table` follows it. The `--hds-space-{xs…4xl}` vars are a legacy bridge that no component reads.
329
437
  <!-- auto:end:spacing -->
330
438
 
439
+ ## Layout contract (v1)
440
+
441
+ Approved 2026-10-07. Data: `docs/guardrails/layout-contract.json`; gates: `pnpm check:layout-contract` and `pnpm check:spacing-scale` (warn mode; baseline in `docs/audits/layout-contract-baseline.md`).
442
+
443
+ 1. **Sizing.** Every component declares `fill` or `hug` on each axis. Leaves hug. Input, Textarea, Select, Combobox, Table, Progress and Divider fill width. One override prop, `width="hug|fill"`, on every component. Height always hugs; stretching is the parent's job (`align="stretch"`). No component sets `h-full`, `height: 100%` or `self-*` on its root.
444
+ 2. **Padding.** Only containers pad: Card, Surface, Alert, Callout, Dialog, Page. Compound parts (`Card.Header`, `Body`, `Footer`, `Metric`) have zero padding and the container's `gap` spaces them. Padding is `none | sm | md`, default `md`, density-aware. A padded container directly inside a padded container is an error unless the inner one is `padding="none"`.
445
+ 3. **Spacing.** One scale, `xs…xl` (plus `none`), for every `gap` and `padding` prop. Every gap defaults to `md`. No outer margins on any component; space between siblings is the parent's `gap`.
446
+ 4. **Primitives.** Stack, Grid, Page (absorbs Container) and Surface compose layouts. Box and Pin are escape hatches. Sidebar and Switcher fold into Stack and Grid.
447
+ 5. **Forms.** A form control in a form context has a max width (`40rem` by default) and siblings share one width.
448
+
331
449
  ## Corner-Radius Policy
332
450
 
333
451
  Shape is one knob. `semantic.radius.action` is the system's single shape value — interactive controls (buttons, inputs, badges, alerts, segmented items) all resolve to it, and Tailwind's `rounded-md` maps to it exactly so the utility and the token cannot disagree. Containers sit one step above at `rounded-lg` (`action + 4px`); `rounded-sm` (`action - 2px`) is for chrome nested inside a control. `full` is reserved for pills and circular forms, and `0px` only for intentional outer canvas or substrate boundaries, never everyday UI controls. Reshaping the whole system means overriding `role.radius` in one tenant overlay — that is how `brutalist-demo` goes square in a single value.
@@ -356,7 +474,7 @@ Depth is communicated through 4 elevation roles bundled by `semantic.elevation.*
356
474
 
357
475
  Cards default to `flat`. They lift to `raised` only on interactive hover or when explicitly elevated above siblings. Never combine `raised` with a border — depth is one mechanism (border OR shadow), not both stacked.
358
476
 
359
- Overlays (Dialog, AlertDialog, Menu, ContextMenu, Popover, Select, HoverCard, Tooltip) portal into the nearest `data-hds` scope, so they inherit its theme (for example `<div data-hds data-theme="dark">`); pass `container` on the Content part to override.
477
+ Overlays (Dialog, AlertDialog, Menu, Popover, Select, Tooltip) portal into the nearest `data-hds` scope, so they inherit its theme (for example `<div data-hds data-theme="dark">`); pass `container` on the Content part to override.
360
478
 
361
479
  ## Motion
362
480
 
@@ -387,7 +505,7 @@ Default most interactive feedback to `productive` (150ms, decelerate). Reserve `
387
505
 
388
506
  | Token | Value | Purpose |
389
507
  | --- | --- | --- |
390
- | `semantic.motion.distance` | `24px` | Travel distance for a scroll-reveal translateY offset. Coincides in value with semantic.space.scale.md today, but names a motion travel distance rather than a spacing tier (hds#242). |
508
+ | `semantic.motion.distance` | `24px` | Scroll-reveal translateY travel distance. Equals semantic.space.scale.md today, but names motion travel, not a spacing tier (hds#242). |
391
509
  <!-- auto:end:motion -->
392
510
 
393
511
  ## Components
@@ -395,7 +513,7 @@ Default most interactive feedback to `productive` (150ms, decelerate). Reserve `
395
513
  <!-- auto:start:components -->
396
514
  | Component | Radius | States | Guidance |
397
515
  | --- | --- | --- | --- |
398
- | **Buttons** (`Button`) | `8px` (`semantic.radius.action`) | default · hover · focus · active · disabled · loading | Three variants: primary (accent-filled), secondary (outline), tertiary (ghost). Primary uses `semantic.accent.*` ramp per state. Icon buttons (`IconButton`) follow the same token surface. Icon-only actions use `IconButton` with an icon from `@hirobius/design-system/icons`. |
516
+ | **Buttons** (`Button`) | `8px` (`semantic.radius.action`) | default · hover · focus · active · disabled · loading | Three variants: primary (accent-filled), secondary (outline), tertiary (ghost). Primary uses `semantic.accent.*` ramp per state. Pressed is a 5% `semantic.color.state.pressed.overlay` wash over the fill (`role.pressed-overlay`: black in light, white in dark), not a brightness filter. Icon-only buttons (`iconOnly`) follow the same token surface. Icon-only actions use `Button iconOnly` with a `label` and an `Icon` from `@hirobius/design-system/icons` in `iconLeft`. |
399
517
  | **Inputs** (`Input`) | `8px` (`semantic.radius.action`) | default · focus · filled · error · disabled · loading | Border-driven treatment; no filled background by default. Focus uses `semantic.color.border.accent` plus a 2px outline offset. Error swaps to `component.input.borderError`. |
400
518
  | **Cards** (`Card`) | `12px` (`rounded-lg`, role radius + 4px) | default · hover (optional parallax) · pressed (when interactive) | Cards default to `elevation.flat` (1px border `border.subtle`, no shadow). Interactive cards lift to `elevation.raised` (shadow.subtle, no border) on hover. Bind via `semantic.elevation.{role}` — never raw box-shadow values. Radius: `rounded-lg` (role radius + 4 px, follows the tenant knob) — never a hard-coded value. Padding: `var(--semantic-space-surface-padding)`. Title: `heading3`. Meta: `caption` + `var(--semantic-color-content-secondary)`. Hover (interactive): `scale(1.02)` transform + lift to raised. Never: gradients, glow, frosted glass, tinted surfaces, decorative overlays, or inner shadows. |
401
519
  | **Badges** (`Badge`) | `8px` (`primitive.radius.4`) | neutral · accent · feedback (error/success/warning/info) | Single-line status markers. Feedback colors come from `semantic.color.feedback.*`. Never used as decorative chrome. |
@@ -418,7 +536,7 @@ Every HDS card surface must conform to this anatomy exactly. No creative interpr
418
536
  | Border radius | `rounded-lg` (`role.radius` + 4 px: 12 px by default, follows the tenant) | `var(--component-card-radius)`, `rounded-full`, or any hard-coded value |
419
537
  | Padding | `var(--semantic-space-component-padding)` or `<HdsSurface padding="component">` | Raw pixel values or ad hoc insets |
420
538
  | Shadow | Resting cards: none (`elevation.flat`). Interactive lifted state: `shadow.subtle` via `elevation.raised` | Raw `box-shadow` values, `drop-shadow`, glow, or any depth effect not bound to a role token |
421
- | Title | `hds.typeStyles.heading3` / `<HdsText variant="heading3">` | Any other type style for the primary card heading |
539
+ | Title | `hds.typeStyles.title` / `<Text variant="title">` | Any other type style for the primary card heading |
422
540
  | Subtitle / meta | `hds.typeStyles.caption` + `var(--semantic-color-content-secondary)` | Primary content color or body size for secondary text |
423
541
  | Hover (interactive only) | `transform: scale(1.02)` | Background fill change, border color shift, or opacity fade on hover |
424
542
 
@@ -430,7 +548,7 @@ Five patterns sit above the primitives so that every screen is composed the same
430
548
 
431
549
  ### Page title rule
432
550
 
433
- Every screen has exactly one `PageHeader`. The page title is `heading2` (30px), a fixed size with no size prop; `level` changes only the DOM heading element. `display` and `h1` are reserved for marketing and landing surfaces and are never a screen title. Breadcrumb goes in the `breadcrumb` slot, one status `Badge` in `status`, screen-level actions in `actions`.
551
+ Every screen has exactly one `PageHeader`. The page title is `title` (24px), a fixed size with no size prop; `level` changes only the DOM heading element. `display` is reserved for marketing and landing surfaces and is never a screen title. Breadcrumb goes in the `breadcrumb` slot, one status `Badge` in `status`, screen-level actions in `actions`.
434
552
 
435
553
  ### Which one, when
436
554
 
@@ -442,14 +560,16 @@ Pick by what the thing is. The first four rows are for a headline number or a st
442
560
  | `Stat` | An inline number inside prose or a dense list. | `MetricTiles` when the number stands alone in a row of its own. |
443
561
  | `Card.Metric` | A metric inside an existing `Card` only. | `MetricTiles` when there is no enclosing `Card`. |
444
562
  | `StatusTile` | State with notes and a trailing badge, never a number. | `MetricTiles` when the tile is a number; `StatusTile` never carries a value. |
445
- | `Card` | A titled or interactive content object (header, body, footer, or selectable). | `Surface` when there is no title and nothing to press. |
563
+ | `Card` | A titled or interactive content object (header, body, footer, `selectable`). | `Surface` when there is no title and nothing to press. |
446
564
  | `Surface` | A plain padded background around content that has no header of its own. | `Card` when the content is titled or interactive. |
447
565
  | `DestructiveSection` | The one irreversible action on a screen, with an explanation and a confirm. | `FormActions` `destructive` slot for a delete beside a form's own buttons. |
448
566
  | `DataTableSection` | A titled table with a toolbar, row actions and an empty state. | `Table` alone when there is no heading, toolbar or empty state to show. |
449
567
 
568
+ Three more needs are props on an existing component, not components of their own. A card people pick from a set is a `Card` with `selectable`, `selected` and `onSelectedChange`, announced as a checkbox. A wrapping row of equal tiles that are not headline numbers is a `Grid` with `layout="auto-fill"` and `minItemWidth`. A state shown only as a colored marker, with no notes, is a `Badge` with `dot` and a `label` that names the state.
569
+
450
570
  `Card` and `Surface` share one container radius, `rounded-lg` (`role.radius` + 4 px: 12 px by default, 4 px under `brutalist-demo`). Dialogs and alert dialogs use it too. Never read `--component-card-radius` for a container; it ignores the tenant.
451
571
 
452
- `MetricTiles` has `min(tiles, 4)` columns, so a row never leaves an empty column, and every tile has one fixed min-height with the value in `heading2`, the label as an eyebrow and the sub line in `caption`. Its tone comes from the fixed feedback vocabulary: `neutral | success | warning | danger | info`.
572
+ `MetricTiles` has `min(tiles, 4)` columns, so a row never leaves an empty column, and every tile has one fixed min-height with the value in `title`, the label in `caption` and the sub line in `caption`. Its tone comes from the fixed feedback vocabulary: `neutral | success | warning | danger | info`.
453
573
 
454
574
  ### Destructive and table sections
455
575
 
@@ -481,7 +601,7 @@ Unresolved rules that the live repo cannot yet answer confidently are tracked in
481
601
  <!-- auto:start:build-meta -->
482
602
  ---
483
603
 
484
- > Generated from `hirobius.tokens.json` (381 tokens) and `public/hds-manifest.json` by `scripts/build-design-md.mjs`.
604
+ > Generated from `hirobius.tokens.json` (389 tokens) and `public/hds-manifest.json` by `scripts/build-design-md.mjs`.
485
605
  > Hand-edit `DESIGN.source.md`; this file (`DESIGN.md`) is overwritten by `pnpm tokens`.
486
606
  <!-- auto:end:build-meta -->
487
607
 
@@ -491,10 +611,6 @@ Unresolved rules that the live repo cannot yet answer confidently are tracked in
491
611
 
492
612
  Compact: name, first line of description, then prop: type = default. Full detail in `src/app/data/component-api.json`.
493
613
 
494
- ### ActivityFeed
495
- ActivityFeed — chronological system event log. Renders a list of activity events using white space as the sole separator. No dividers, no borders between items…
496
- - events: ActivityEvent[] = [ { id: 'evt-001', title: 'File Uploade…
497
-
498
614
  ### Alert
499
615
  Alert - compact feedback surface with contextual severity.
500
616
  - children: ReactNode
@@ -508,11 +624,6 @@ AlertDialog root + parts. Controlled via `open`/`onOpenChange`, or uncontrolled
508
624
  - onOpenChange: ((open: boolean) => void)
509
625
  - open: boolean
510
626
 
511
- ### AspectRatio
512
- Locks its single child to the given width-to-height `ratio` so media reserves its box up front and never causes layout shift while it loads.
513
- - asChild: boolean
514
- - ratio: number = 1
515
-
516
627
  ### AssetImg
517
628
  AssetImg - responsive asset frame with fallback placeholder handling. Contexts: default, lightbox, and detail.
518
629
  - alt: string
@@ -550,16 +661,10 @@ Renders up to `max` overlapping avatars followed by a `+N` overflow chip.
550
661
  ### Badge
551
662
  Compact metadata/status chip. Tone is the only styling input.
552
663
  - as: ElementType<any, keyof IntrinsicElements> = span
553
- - tone: "neutral" | "danger" | "success" | "warning" | "info" | "inProgress" | null
554
-
555
- ### Bleed
556
- Bleed — controlled negative-margin full-bleed within a padded container. Every Layout reference: https://every-layout.dev/layouts/the-stack/ (bleed is the inve…
557
- - amount: 'tight' | 'normal' | 'inset' | 'spacious' = normal
558
- - as: ElementType<any, keyof IntrinsicElements> = div
559
- - axis: 'x' | 'y' | 'both' = x
560
- - children: ReactNode
561
- - className: string
562
- - style: CSSProperties
664
+ - dot: boolean = false
665
+ - label: string
666
+ - size: "sm" | "md" | "lg"
667
+ - tone: "success" | "danger" | "warning" | "info" | "neutral" | "inProgress" | null
563
668
 
564
669
  ### Blockquote
565
670
  A quoted passage; pass `cite` for the source URL and `attribution` for a byline.
@@ -580,23 +685,19 @@ Breadcrumb trail. Links route through the HDS router seam (plain anchors with no
580
685
  ### Button
581
686
  Triggers an action when activated.
582
687
  - asChild: boolean = false
688
+ - defaultPressed: boolean
583
689
  - disabled: boolean
584
690
  - iconLeft: ReactNode
585
691
  - iconOnly: boolean = false
586
692
  - iconRight: ReactNode
587
693
  - label: string
588
694
  - loading: boolean = false
695
+ - onPressedChange: ((pressed: boolean) => void)
696
+ - pressed: boolean
589
697
  - size: "sm" | "md" | "lg" | null
590
- - tone: "neutral" | "danger" | "success" | "warning" | "info" | null
698
+ - tone: "success" | "danger" | "warning" | "info" | "neutral" | null
591
699
  - variant: "primary" | "secondary" | "tertiary" | null
592
700
 
593
- ### ButtonGroup
594
- Groups Button (or button-like) children into a single attached segment.
595
- - orientation: "horizontal" | "vertical" | null
596
-
597
- ### Calendar
598
- Renders an accessible month grid; compose it inside a Popover for the date inputs.
599
-
600
701
  ### Callout
601
702
  Tone-driven side-rule callout for status, quotes, hypotheses.
602
703
  - italic: boolean = false
@@ -608,7 +709,10 @@ Tagged per-export, not on the file block: this module exports eight components a
608
709
  - bordered: boolean = false
609
710
  - gap: GapOption = tight
610
711
  - noPadding: boolean = false
712
+ - onSelectedChange: ((selected: boolean) => void)
611
713
  - padding: PaddingOption = component
714
+ - selectable: boolean = false
715
+ - selected: boolean = false
612
716
  - tone: 'neutral' | 'danger' | 'success' | 'warning' | 'info' = neutral
613
717
  - variant: 'default' | 'accent' = default
614
718
 
@@ -626,7 +730,7 @@ Card — surface container with slot anatomy (12d-card-anatomy). Slot anatomy (D
626
730
  - metadata: ReactNode
627
731
 
628
732
  ### CardMetric
629
- Card.Metric — one uppercase label, a large value and an optional sub-line inside a Card.
733
+ Card.Metric — one label, a large value and an optional sub-line inside a Card.
630
734
  - label: ReactNode
631
735
  - sub: ReactNode
632
736
  - tone: 'neutral' | 'danger' | 'success' | 'warning' | 'info' = neutral
@@ -642,21 +746,6 @@ Card — surface container with slot anatomy (12d-card-anatomy). Slot anatomy (D
642
746
  ### CardTitle
643
747
  Card — surface container with slot anatomy (12d-card-anatomy). Slot anatomy (DESIGN.md §"Card Anatomy"). Status, progress, and metadata are layout-bearing — th…
644
748
 
645
- ### Carousel
646
- Renders children as a snap-scrolling track with Prev/Next scroll buttons.
647
- - ariaLabel: string = Carousel
648
- - children: ReactNode
649
- - showControls: boolean = true
650
-
651
- ### Center
652
- Center — max-width column, auto-centered horizontally. Every Layout reference: https://every-layout.dev/layouts/center/
653
- - as: ElementType<any, keyof IntrinsicElements> = div
654
- - children: ReactNode
655
- - className: string
656
- - gutter: 'tight' | 'normal' | 'inset' | 'spacious'
657
- - maxWidth: 'content' | 'max' = content
658
- - style: CSSProperties
659
-
660
749
  ### Checkbox
661
750
  Checkbox — custom-drawn checkbox with check / indeterminate glyph.
662
751
  - checked: boolean
@@ -664,30 +753,6 @@ Checkbox — custom-drawn checkbox with check / indeterminate glyph.
664
753
  - label: string
665
754
  - onChange: (v: boolean) => void
666
755
 
667
- ### CinematicLink
668
- CinematicLink - cinematic editorial link treatment for portfolio surfaces.
669
- - children: string
670
- - href: string
671
-
672
- ### CircularProgress
673
- A circular progress ring; pass `value`/`max` or set `indeterminate`.
674
- - indeterminate: boolean = false
675
- - label: string
676
- - max: number = 100
677
- - size: "sm" | "md" | "lg" = md
678
- - tone: "neutral" | "danger" | "success" | "warning" | "info" | null
679
- - value: number = 0
680
-
681
- ### Cluster
682
- Cluster — wrapping horizontal group with even gaps. Every Layout reference: https://every-layout.dev/layouts/cluster/
683
- - align: 'start' | 'center' | 'end' | 'stretch' = center
684
- - as: ElementType<any, keyof IntrinsicElements> = div
685
- - children: ReactNode
686
- - className: string
687
- - gap: 'tight' | 'normal' | 'inset' | 'spacious' = tight
688
- - justify: 'start' | 'center' | 'end' | 'space-between' = start
689
- - style: CSSProperties
690
-
691
756
  ### CodeBlock
692
757
  CodeBlock - code display with copy button and optional collapsible toggle.
693
758
  - className: string
@@ -700,32 +765,20 @@ CodeBlock - code display with copy button and optional collapsible toggle.
700
765
  - variant: "inline" | "block" = block
701
766
 
702
767
  ### Combobox
703
- Combobox — searchable single-select (Popover + filtered listbox). A select-with-search built on the HDS Popover. The trigger shows the current selection; openi…
768
+ Combobox — searchable single- or multi-select (Popover + filtered listbox). A select-with-search built on the HDS Popover. The trigger shows the current select…
769
+ - aria-describedby: string
770
+ - aria-invalid: boolean
704
771
  - aria-label: string
705
772
  - className: string
706
773
  - disabled: boolean = false
707
774
  - emptyMessage: string = No results
708
- - onChange: (value: string) => void
775
+ - id: string
776
+ - multiple: boolean
777
+ - onChange: ((value: string) => void) | ((value: string[]) => void)
709
778
  - options: ComboboxOption[]
710
779
  - placeholder: string = Select…
711
780
  - searchPlaceholder: string = Search…
712
- - value: string | null
713
-
714
- ### CommandPalette
715
- Mountable trigger + dialog. Drop into header slot; component manages its own open state and Cmd-K / Ctrl-K global keybinding.
716
- - className: string
717
-
718
- ### ComponentInstanceMatrix
719
- ComponentInstanceMatrix — responsive specimen matrix for variant and state parity.
720
- - componentName: string
721
- - dimensionX: string
722
- - dimensionY: string
723
- - freezeRowState: boolean = false
724
- - isMobile: boolean
725
- - renderInstance: (rowKey: string, columnKey: string) => ReactNode
726
- - rowLabelTone: "primary" | "secondary" = primary
727
- - rowLabelWidth: number = 120
728
- - title: string
781
+ - value: string | readonly string[] | null
729
782
 
730
783
  ### Container
731
784
  Container — semantic width-constrained layout. Centers content horizontally and applies semantic max-width constraints. - maxWidth options: 'content' (760px pr…
@@ -735,67 +788,17 @@ Container — semantic width-constrained layout. Centers content horizontally an
735
788
  - padding: string = var(--semantic-space-surface-padding)
736
789
  - style: CSSProperties
737
790
 
738
- ### ContextMenu
739
- ContextMenu root + parts. Wrap the right-clickable area in `ContextMenu.Trigger`. Controlled via `onOpenChange`.
740
- - children: React.ReactNode
741
- - dir: ("ltr" | "rtl")
742
- - modal: boolean
743
- - onOpenChange: ((open: boolean) => void)
744
- - open: boolean
745
-
746
- ### Cover
747
- Cover — vertical shell with an auto-centered main region. Every Layout reference: https://every-layout.dev/layouts/cover/
748
- - as: ElementType<any, keyof IntrinsicElements> = div
749
- - centerSlot: ReactNode
750
- - children: ReactNode
751
- - className: string
752
- - footer: ReactNode
753
- - gap: 'tight' | 'normal' | 'inset' | 'spacious' = normal
754
- - header: ReactNode
755
- - minHeight: string = 100svh
756
- - style: CSSProperties
757
-
758
791
  ### DataTableSection
759
792
  Section heading and toolbar over a `Table`. Row actions are consumer nodes in a trailing column, the table scrolls horizontally on narrow widths without a `min…
760
793
  - actionsLabel: string = Actions
761
794
  - columns: TableColumn[]
762
795
  - emptyDescription: ReactNode
763
796
  - emptyTitle: ReactNode = Nothing here yet
764
- - level: 2 | 3 | 4 | 5 | 6 = 2
797
+ - level: 6 | 3 | 4 | 2 | 5 = 2
765
798
  - rows: DataTableSectionRow[]
766
799
  - title: ReactNode
767
800
  - toolbar: ReactNode
768
801
 
769
- ### DateInput
770
- Text field + calendar popover for picking a single date.
771
- - aria-label: string
772
- - disabled: boolean = false
773
- - displayFormat: string = yyyy-MM-dd
774
- - id: string
775
- - onChange: ((date: Date) => void) | undefined
776
- - placeholder: string
777
- - value: Date
778
-
779
- ### DateRangeInput
780
- DateRangeInput — date-range field composing the existing Popover with Calendar in range mode, so picking a start/end date reads as one overlay pattern with Com…
781
- - aria-label: string
782
- - disabled: boolean = false
783
- - displayFormat: string = yyyy-MM-dd
784
- - id: string
785
- - numberOfMonths: number = 2
786
- - onChange: ((range: DateRange) => void) | undefined
787
- - placeholder: string = Select date range
788
- - value: DateRange
789
-
790
- ### DateTimeInput
791
- Text date field + calendar popover, paired with a native time field, producing one Date.
792
- - aria-label: string
793
- - dateFormat: string = yyyy-MM-dd
794
- - disabled: boolean = false
795
- - id: string
796
- - onChange: ((date: Date) => void) | undefined
797
- - value: Date
798
-
799
802
  ### DestructiveSection
800
803
  Titled danger zone: a title, an explanation, and one danger `Button` that opens an `AlertDialog`. Pressing the button never runs the action; only the dialog's …
801
804
  - actionLabel: ReactNode
@@ -803,7 +806,7 @@ Titled danger zone: a title, an explanation, and one danger `Button` that opens
803
806
  - confirmBody: ReactNode
804
807
  - confirmLabel: string
805
808
  - description: ReactNode
806
- - level: 2 | 3 | 4 | 5 | 6 = 2
809
+ - level: 6 | 3 | 4 | 2 | 5 = 2
807
810
  - onConfirm: () => void
808
811
  - title: ReactNode
809
812
 
@@ -881,19 +884,6 @@ Divider — semantic separator between content regions.
881
884
  - strong: boolean
882
885
  - variant: "strong" | "default" | null
883
886
 
884
- ### DocLinkCard
885
- DocLinkCard - navigation card for editorial and documentation cross-links.
886
- - accent: boolean = false
887
- - affordance: "up-right" | "right" | "left" = up-right
888
- - description: string
889
- - disabled: boolean = false
890
- - href: string
891
- - icon: LucideIcon
892
- - meta: string
893
- - metaStyle: "caption" | "ui" = caption
894
- - title: string
895
- - variant: "feature" | "pager" = feature
896
-
897
887
  ### EmptyState
898
888
  EmptyState — consistent no-data placeholder for lists, grids, and sections.
899
889
  - className: string
@@ -905,17 +895,9 @@ Caption label paired with a value — used in metadata grids and read-only forms
905
895
  - children: React.ReactNode
906
896
  - label: string
907
897
  - mono: boolean | null
908
- - tone: "neutral" | "danger" | "success" | "warning" | "info" | null
898
+ - tone: "success" | "danger" | "warning" | "info" | "neutral" | null
909
899
  - value: ReactNode
910
900
 
911
- ### FileInput
912
- A dashed dropzone that opens the native file picker or accepts a drag-and-drop.
913
- - accept: string
914
- - disabled: boolean
915
- - label: ReactNode = Click to upload or drag and drop
916
- - multiple: boolean
917
- - onFiles: ((files: File[]) => void)
918
-
919
901
  ### Form
920
902
  Styled `<form>` with consistent vertical field rhythm. Tagged per-export: FormField and FormFieldShell share this module and have no Figma node, so a file-leve…
921
903
 
@@ -946,50 +928,23 @@ Presentational label + control slot + helper/error markup. Renders `children` ve
946
928
  - label: string
947
929
  - required: boolean = false
948
930
 
949
- ### FoundationSwatch
950
- FoundationSwatch - governed foundation specimen for color and semantic role previews.
951
- - background: string
952
- - bordered: boolean = false
953
- - details: string[]
954
- - foreground: string = var(--semantic-color-content-primary)
955
- - hidePreviewLabel: boolean = false
956
- - label: string
957
- - note: ReactNode
958
- - previewPosition: 'bottom-left' | 'top-left' | 'center' = center
959
- - specimen: ReactNode
960
- - swatchVar: string
961
- - tokenDisplayPreset: 'depth1' | 'depth2' | 'full' = depth2
962
- - tokenPath: string
963
- - value: string
964
-
965
- ### Frame
966
- Frame — aspect-ratio-locked, token-clipped media box. Every Layout reference: https://every-layout.dev/layouts/frame/
967
- - as: ElementType<any, keyof IntrinsicElements> = div
968
- - children: ReactNode
969
- - className: string
970
- - radius: 'none' | 'sm' | 'md' | 'full' = md
971
- - ratio: number = 16 / 9
972
- - style: CSSProperties
973
-
974
931
  ### Grid
975
932
  Grid — responsive grid composition primitive. Enforces semantic gap and column values. No arbitrary CSS grid. - layout='fixed': responsive base (collapses via …
933
+ - align: "center" | "end" | "start" | "stretch" = start
976
934
  - as: ElementType<any, keyof IntrinsicElements> = div
977
935
  - children: ReactNode
978
936
  - className: string
979
937
  - columns: number = 12
980
- - gap: 'tight' | 'normal' | 'inset' | 'spacious' = inset
981
- - layout: 'fixed' | 'auto-fit' = fixed
938
+ - gap: 'medium' | 'tight' | 'normal' | 'inset' | 'spacious' = inset
939
+ - layout: 'fixed' | 'auto-fit' | 'auto-fill' = fixed
940
+ - minItemWidth: string
982
941
  - style: CSSProperties
983
942
  - subgrid: boolean = false
984
943
 
985
- ### HdsDocsShell
986
- HdsDocsShell — three-region docs shell (left nav rail, content, right TOC rail), driven by grid columns that default to 0 and are set per breakpoint. hds#280 s…
944
+ ### HdsRouterProvider
945
+ Provide a router adapter to all HDS components below. Wrap your app root once.
946
+ - adapter: HdsRouterAdapter
987
947
  - children: ReactNode
988
- - className: string
989
- - leftRail: ReactNode
990
- - rightRail: ReactNode
991
- - style: CSSProperties
992
- - topOffset: string = 0px
993
948
 
994
949
  ### HdsThemeProvider
995
950
  Renders a `data-hds` scope wrapper that carries the four theming dials as root attributes + CSS vars, and exposes them to `useHdsTheme()`.
@@ -1003,25 +958,6 @@ Renders a `data-hds` scope wrapper that carries the four theming dials as root a
1003
958
  - style: CSSProperties
1004
959
  - theme: 'light' | 'dark'
1005
960
 
1006
- ### HeadingStack
1007
- HeadingStack — enforced vertical rhythm for heading + subheading pairs. Automatically handles gap and secondary color, preventing manual stacking. - Heading le…
1008
- - as: ElementType = div
1009
- - gap: HdsHeadingGap = px8
1010
- - heading: string
1011
- - headingAs: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
1012
- - level: HdsHeadingLevel
1013
- - subheading: string
1014
- - subheadingAs: 'p' | 'div' | 'span' = p
1015
-
1016
- ### HoverCard
1017
- HoverCard root + parts. Controlled via `open`/`onOpenChange`, or uncontrolled with `defaultOpen`; tune timing with `openDelay`/`closeDelay`.
1018
- - children: React.ReactNode
1019
- - closeDelay: number
1020
- - defaultOpen: boolean
1021
- - onOpenChange: ((open: boolean) => void)
1022
- - open: boolean
1023
- - openDelay: number
1024
-
1025
961
  ### Icon
1026
962
  Icon — semantic icon wrapper for Lucide icons.
1027
963
  - aria-hidden: boolean | "true" | "false" = true
@@ -1032,22 +968,6 @@ Icon — semantic icon wrapper for Lucide icons.
1032
968
  - style: CSSProperties
1033
969
  - weight: "fill" | "bold" | "light" | "thin" | "regular" | "duotone"
1034
970
 
1035
- ### IconButton
1036
- IconButton ” icon-only action trigger built on the shared Button primitive.
1037
- - aria-label: string
1038
- - asChild: boolean
1039
- - disabled: boolean
1040
- - icon: LucideIcon
1041
- - iconColor: string
1042
- - iconLeft: ReactNode
1043
- - iconRight: ReactNode
1044
- - iconSize: IconSize
1045
- - label: string
1046
- - loading: boolean
1047
- - size: "sm" | "md" | "lg"
1048
- - tone: "neutral" | "danger" | "success" | "warning" | "info" | null
1049
- - variant: "primary" | "secondary" | "tertiary" | null
1050
-
1051
971
  ### InlineCode
1052
972
  InlineCode — inline code chip for token paths, file paths, and code-adjacent prose.
1053
973
  - children: ReactNode
@@ -1073,34 +993,17 @@ Input — text field primitive with label, helper, and error slots.
1073
993
  - label: string
1074
994
  - leadingVisual: ReactNode
1075
995
  - loading: boolean = false
996
+ - prefix: ReactNode
1076
997
  - size: InputSize = md
998
+ - suffix: ReactNode
1077
999
  - textStyle: "body" | "mono" = body
1078
1000
  - trailingVisual: ReactNode
1079
- - type: 'text' | 'email' | 'password' | 'search' | 'tel' | 'url' | 'number' = text
1080
-
1081
- ### InputGroup
1082
- A single-line input with optional `leading`/`trailing` adornments.
1083
- - className: string
1084
- - leading: ReactNode
1085
- - size: "sm" | "md" | "lg" | null
1086
- - trailing: ReactNode
1001
+ - type: 'text' | 'email' | 'password' | 'search' | 'tel' | 'url' | 'number' | 'date' | … = text
1087
1002
 
1088
1003
  ### Kbd
1089
1004
  Renders a keyboard key or shortcut token, e.g. `<Kbd>⌘K</Kbd>`.
1090
1005
  - size: "sm" | "md" | "lg" | null
1091
1006
 
1092
- ### Lightbox
1093
- Lightbox — full-bleed image viewer built on Radix Dialog. Full-bleed image viewer built on Radix Dialog (raw primitives, not the card `Dialog` wrapper — a ligh…
1094
- - alt: string
1095
- - caption: string
1096
- - captionHref: string
1097
- - captionLabel: string
1098
- - naturalHeight: number
1099
- - naturalWidth: number
1100
- - onClose: () => void
1101
- - open: boolean
1102
- - src: string
1103
-
1104
1007
  ### Menu
1105
1008
  Menu root + parts. Controlled via `open`/`onOpenChange`, or uncontrolled with `defaultOpen`.
1106
1009
  - children: React.ReactNode
@@ -1128,44 +1031,6 @@ One metric tile: eyebrow label, `heading2` value, caption sub line, on a raised
1128
1031
  A row of `MetricTile`s. The grid has `min(tiles, 4)` columns, so three tiles fill three columns and never leave an empty fourth. The default for any row of hea…
1129
1032
  - children: ReactNode
1130
1033
 
1131
- ### MultiSelector
1132
- MultiSelector — multi-select dropdown composing the existing Popover with a checkbox option list, so choosing several values from a fixed set reads as one over…
1133
- - className: string
1134
- - disabled: boolean = false
1135
- - onChange: (next: string[]) => void
1136
- - options: MultiSelectorOption[]
1137
- - placeholder: string = Select…
1138
- - value: string[]
1139
-
1140
- ### NavGroup
1141
- NavGroup - labeled navigation group for stacks of nav items.
1142
- - children: ReactNode
1143
- - className: string
1144
- - collapsible: boolean = false
1145
- - contentStyle: CSSProperties
1146
- - getExact: ((item: NavItem) => boolean)
1147
- - items: NavItem[]
1148
- - label: string
1149
- - level: NavLevel = section
1150
- - onNavigate: (() => void)
1151
- - style: CSSProperties
1152
- - variant: 'side' | 'toc' = side
1153
-
1154
- ### NavItem
1155
- NavItem - navigation row primitive for sidebars, table of contents, and list navigation.
1156
- - active: boolean = false
1157
- - disabled: boolean = false
1158
- - label: string
1159
- - level: NavLevel = root
1160
- - onNavigate: ((event: MouseEvent<HTMLAnchorElement, MouseEvent>) => void)
1161
- - variant: NavVariant = side
1162
-
1163
- ### OverflowList
1164
- Renders up to `max` children followed by a `+N` overflow chip (or custom node).
1165
- - children: ReactNode
1166
- - max: number
1167
- - renderOverflow: ((count: number) => ReactNode)
1168
-
1169
1034
  ### Page
1170
1035
  Page — standard page shell. Wraps page content in Container (width constraint) and applies the canonical vertical padding so content breathes against the viewp…
1171
1036
  - children: ReactNode
@@ -1207,10 +1072,13 @@ Popover root + parts. Controlled via `open`/`onOpenChange`, or uncontrolled with
1207
1072
  - open: boolean
1208
1073
 
1209
1074
  ### Progress
1210
- Linear progress. Determinate when `value` is a number, else indeterminate.
1075
+ Linear bar or circular ring. Determinate when `value` is a number, else indeterminate.
1211
1076
  - label: string = Progress
1077
+ - max: number = 100
1212
1078
  - size: "sm" | "md" | "lg" | null
1079
+ - tone: "success" | "danger" | "warning" | "info" | "neutral" = neutral
1213
1080
  - value: number | null = null
1081
+ - variant: "linear" | "circular" = linear
1214
1082
 
1215
1083
  ### Radio
1216
1084
  Radio — radio button with animated selection indicator.
@@ -1241,30 +1109,16 @@ SegmentedControl " segmented selection input for compact mutually-exclusive choi
1241
1109
 
1242
1110
  ### Select
1243
1111
  Select — dropdown selector built on Radix Select (ADR-001 Radix convention). Radix owns the listbox a11y contract: managed focus + active-descendant, typeahead…
1112
+ - aria-describedby: string
1113
+ - aria-invalid: boolean
1244
1114
  - container: HTMLElement | null
1115
+ - id: string
1245
1116
  - label: string
1246
1117
  - onChange: (v: string) => void
1247
1118
  - options: { value: string; label: string; }[]
1248
1119
  - showLabel: boolean = true
1249
1120
  - value: string
1250
1121
 
1251
- ### SelectableCard
1252
- A card-shaped single checkbox — click or activate to toggle selection.
1253
- - disabled: boolean
1254
- - onSelectedChange: ((selected: boolean) => void)
1255
- - selected: boolean = false
1256
-
1257
- ### SideNav
1258
- SideNav - sidebar navigation row primitive. Two levels: root — top-level section link. Full-width row using the shared nav label type style. nested — child pag…
1259
- - active: boolean = false
1260
- - disabled: boolean = false
1261
- - href: string
1262
- - indent: number | boolean = 0
1263
- - label: string
1264
- - level: 'root' | 'nested' = nested
1265
- - onNavigate: ((event: MouseEvent<HTMLAnchorElement, MouseEvent>) => void)
1266
- - titleLabel: boolean = false
1267
-
1268
1122
  ### Sidebar
1269
1123
  Sidebar — fixed-width rail beside fluid content, no media query. Every Layout reference: https://every-layout.dev/layouts/sidebar/
1270
1124
  - as: ElementType<any, keyof IntrinsicElements> = div
@@ -1279,15 +1133,9 @@ Sidebar — fixed-width rail beside fluid content, no media query. Every Layout
1279
1133
  ### Skeleton
1280
1134
  Decorative loading placeholder. Marked aria-hidden — announce the loading state on the surrounding region (e.g. aria-busy) rather than per skeleton.
1281
1135
  - height: string | number
1282
- - variant: "text" | "rectangular" | "circular" | null = rectangular
1136
+ - variant: "text" | "circular" | "rectangular" | null = rectangular
1283
1137
  - width: string | number
1284
1138
 
1285
- ### Sketch
1286
- Sketch — shared shell for generative canvases and WebGL sketches.
1287
- - children: ReactNode
1288
- - controls: ReactNode
1289
- - title: string
1290
-
1291
1139
  ### Slider
1292
1140
  Slider — range slider with label and value display.
1293
1141
  - label: string
@@ -1314,28 +1162,18 @@ Stack — one-dimensional layout primitive.
1314
1162
  - style: CSSProperties
1315
1163
  - wrap: FlexWrap
1316
1164
 
1317
- ### StackedCardRail
1318
- StackedCardRail — horizontally-scrolling stacked card carousel. Scroll mechanism: vertical scroll on an absolutely-positioned inner container is mapped to hori…
1319
- - cards: StackedCardRailCard[]
1320
-
1321
1165
  ### Stat
1322
- Headline metric — large value, uppercase caption label, optional sub-line.
1166
+ Headline metric — large value, caption label, optional sub-line.
1323
1167
  - label: string
1324
1168
  - sub: ReactNode
1325
- - tone: "neutral" | "danger" | "success" | "warning" | "info" | null
1169
+ - tone: "success" | "danger" | "warning" | "info" | "neutral" | null
1326
1170
  - value: ReactNode
1327
1171
 
1328
- ### StatusDot
1329
- A small solid dot conveying a semantic status via `tone`.
1330
- - label: string
1331
- - size: "sm" | "md" | "lg" | null
1332
- - tone: "neutral" | "danger" | "success" | "warning" | "info" | "inProgress" | null
1333
-
1334
1172
  ### StatusListItem
1335
1173
  Status dot + title row with optional muted notes and trailing slot (e.g. badge).
1336
1174
  - notes: ReactNode[]
1337
1175
  - title: ReactNode
1338
- - tone: "neutral" | "danger" | "success" | "warning" | "info" | null
1176
+ - tone: "success" | "danger" | "warning" | "info" | "neutral" | null
1339
1177
  - trailing: ReactNode
1340
1178
 
1341
1179
  ### StatusTile
@@ -1345,23 +1183,6 @@ Block-layout sibling of StatusListItem. Renders a raised surface tile with title
1345
1183
  - tone: 'success' | 'warning' | 'danger' | 'info' | 'neutral' = neutral
1346
1184
  - trailing: ReactNode
1347
1185
 
1348
- ### Stepper
1349
- Step indicator for multi-step flows. Purely presentational — no navigation.
1350
- - activeStep: number
1351
- - orientation: "horizontal" | "vertical" = horizontal
1352
- - steps: Step[]
1353
-
1354
- ### StepperField
1355
- StepperField ” numeric input with decrement/increment controls. Classification baseline: Material Design, Chakra UI, and Ant Design treat steppers as numeric i…
1356
- - inputStep: number
1357
- - label: string
1358
- - max: number
1359
- - min: number
1360
- - onChange: (value: number) => void
1361
- - precision: number
1362
- - step: number
1363
- - value: number
1364
-
1365
1186
  ### Surface
1366
1187
  Surface — governed inset surface primitive. Enforces the "Surface Inset" rule: backgrounds MUST have internal padding. - Default padding: px24 (cards) | px16 (…
1367
1188
  - as: ElementType<any, keyof IntrinsicElements> = div
@@ -1390,7 +1211,7 @@ Table - structured data table primitive for documentation and compact UI matrice
1390
1211
  - columns: TableColumn[]
1391
1212
  - density: 'compact' | 'comfortable'
1392
1213
  - description: ReactNode
1393
- - flush: boolean = false
1214
+ - flush: boolean = true
1394
1215
  - labelledBy: string
1395
1216
  - minWidth: string | number
1396
1217
  - rows: TableRow[]
@@ -1429,24 +1250,12 @@ Tag — interactive filter and category chip. The outer <button> carries the acc
1429
1250
 
1430
1251
  ### Text
1431
1252
  Text — polymorphic typography primitive spanning the full HDS type ramp.
1432
- - as: "symbol" | "object" | "map" | "filter" | "title" | "a" | "button" | "div" | "fo… = defaultTagMap[variant]
1253
+ - as: "symbol" | "object" | "title" | "a" | "button" | "div" | "form" | "h2" | "h3" |… = defaultTagMap[variant]
1433
1254
  - children: ReactNode
1434
1255
  - className: string
1435
1256
  - style: CSSProperties
1436
1257
  - variant: TextVariant
1437
1258
 
1438
- ### TextLockup
1439
- TextLockup - governed title-and-description pairing primitive.
1440
- - align: 'left' | 'center' = left
1441
- - as: ElementType = div
1442
- - description: ReactNode
1443
- - descriptionAs: 'div' | 'p' | 'span' = div
1444
- - eyebrow: string
1445
- - id: string
1446
- - size: 'hero' | 'heroXl' | 'section' | 'metric' | 'detail' | 'numbered'
1447
- - title: string
1448
- - titleAs: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span'
1449
-
1450
1259
  ### Textarea
1451
1260
  Textarea — multi-line text field primitive with label, helper, and error slots. Mirrors Input's shell + token skin as a native `<textarea>`. Figma parity: the …
1452
1261
  - className: string
@@ -1458,15 +1267,6 @@ Textarea — multi-line text field primitive with label, helper, and error slots
1458
1267
  - resize: TextareaResize = vertical
1459
1268
  - textareaClassName: string
1460
1269
 
1461
- ### TileGrid
1462
- Auto-fill responsive grid for status/micro tiles. Wraps tile children in a one-line grid declaration so pages don't repeat the auto-fill minmax recipe.
1463
- - gap: "sm" | "md" | "xs" = sm
1464
- - minTileWidth: string = 260px
1465
-
1466
- ### TimeInput
1467
- A native time field skinned with HDS input chrome. Value is a `HH:mm` string.
1468
- - size: "sm" | "md" | "lg" | null
1469
-
1470
1270
  ### Timestamp
1471
1271
  Presents a date/time with a machine-readable `dateTime` for accessibility.
1472
1272
  - date: string | number | Date
@@ -1486,54 +1286,6 @@ Toggle — boolean on/off toggle with animated thumb.
1486
1286
  - label: string
1487
1287
  - onChange: (v: boolean) => void
1488
1288
 
1489
- ### ToggleButton
1490
- A single two-state toggle button exposing a native `aria-pressed` contract — use for one independent on/off control such as bold, mute, or pin.
1491
- - asChild: boolean
1492
- - defaultPressed: boolean
1493
- - onPressedChange: ((pressed: boolean) => void)
1494
- - pressed: boolean
1495
- - size: "sm" | "md" | "lg" | null
1496
- - variant: "secondary" | "ghost" | null
1497
-
1498
- ### Token
1499
- Token - reflective token specimen for unified node-based token views. The `variant` axis (node | diagram — diagram is a deprecated alias that resolves to the s…
1500
- - accentColor: string
1501
- - aria-label: string
1502
- - className: string
1503
- - description: ReactNode
1504
- - fullWidth: boolean
1505
- - isSelected: boolean
1506
- - isSourceNode: boolean
1507
- - label: string
1508
- - leadingSlot: ReactNode
1509
- - nodeRef: ((el: HTMLElement | null) => void)
1510
- - nowrap: boolean
1511
- - onClick: (() => void) | (() => void)
1512
- - pathDisplayDepth: number
1513
- - pathDisplayLeadingDot: boolean
1514
- - pathDisplayMode: "full" | "compressed"
1515
- - rawValue: ReactNode
1516
- - swatchVar: string
1517
- - tokenPath: string
1518
- - truncateFromStart: boolean
1519
- - value: string
1520
- - variant: "node" | "diagram"
1521
-
1522
- ### Tokenizer
1523
- A bordered field of Tag chips followed by a text input for adding more.
1524
- - className: string
1525
- - disabled: boolean
1526
- - onChange: (next: string[]) => void
1527
- - placeholder: string
1528
- - value: string[]
1529
-
1530
- ### Toolbar
1531
- Toolbar root. Groups related controls with a single roving tab-stop. Supports the native `orientation` prop (`horizontal` | `vertical`).
1532
- - asChild: boolean
1533
- - dir: ("ltr" | "rtl")
1534
- - loop: boolean
1535
- - orientation: "horizontal" | "vertical"
1536
-
1537
1289
  ### Tooltip
1538
1290
  Tooltip root. Bakes in the Radix Provider so a single `<Tooltip>` is self-contained — no app-level provider required. Controlled via `open`/`onOpenChange`, or …
1539
1291
  - children: React.ReactNode
@@ -1543,19 +1295,6 @@ Tooltip root. Bakes in the Radix Provider so a single `<Tooltip>` is self-contai
1543
1295
  - onOpenChange: ((open: boolean) => void)
1544
1296
  - open: boolean
1545
1297
 
1546
- ### TopNav
1547
- Top navigation bar with brand, nav, and trailing action slots.
1548
- - brand: ReactNode
1549
- - children: ReactNode
1550
- - sticky: boolean
1551
- - trailing: ReactNode
1552
-
1553
- ### TreeList
1554
- Renders a hierarchical `role="tree"` list; nodes with children get an expand/collapse toggle.
1555
- - defaultExpandedIds: string[]
1556
- - items: TreeNode[]
1557
- - onSelect: ((id: string) => void)
1558
-
1559
1298
  ### VisuallyHidden
1560
1299
  Renders its children off-screen (Tailwind `sr-only`) so they are announced by screen readers without occupying visual space — e.g. a text label for an icon-onl…
1561
1300
  - as: ElementType<any, keyof IntrinsicElements> = span