@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
package/AGENTS.md ADDED
@@ -0,0 +1,123 @@
1
+ # AGENTS.md — @hirobius/design-system
2
+
3
+ <!-- Generated by scripts/generate-agents-md.mjs from mcp/guide.mjs and src/app/data/component-api.json. Do not hand-edit above the repo-notes marker: pnpm agents:generate -->
4
+
5
+ For agents building UI with `@hirobius/design-system`. Read this first. It settles the choices the rest of the docs leave open, so two agents given the same screen build it the same way.
6
+
7
+ ## Look things up, do not guess
8
+
9
+ - **MCP server `hds`** (stdio, ships in this package, no network): `npx hds-mcp` from an app that has the package installed. Client config:
10
+ `{ "mcpServers": { "hds": { "command": "npx", "args": ["hds-mcp"] } } }`
11
+ - Tools: `list_core` (call first), `search_components` (by name or need), `get_component` (props, usage contract, compound parts), `search_tokens` (by path, prefix or words). Each answer is under 2 KB.
12
+ - Without the server, the same data is in `src/app/data/component-api.json` (props, `usage`), `public/hds-manifest.json` (tokens, compound parts), `llms.txt` and `DESIGN.md` in this package.
13
+
14
+ ## Imports
15
+
16
+ - `@hirobius/design-system`: `Alert`, `AlertDialog`, `Avatar`, `AvatarGroup`, `Badge`, `Blockquote`, `Box`, `Breadcrumb`, `Button`, `Card`, `Checkbox`, `Combobox`, `Container`, `Dialog`, `Disclosure`, `Divider`, `EmptyState`, `Field`, `Grid`, `HdsRouterProvider`, `HdsThemeProvider`, `Icon`, `InlineLink`, `Input`, `Kbd`, `Menu`, `MetadataList`, `Pagination`, `Pin`, `Popover`, `Progress`, `Radio`, `SegmentedControl`, `Select`, `Sidebar`, `Skeleton`, `Slider`, `Spinner`, `Stack`, `Surface`, `Switcher`, `Table`, `Tabs`, `TabsContent`, `TabsList`, `TabsTrigger`, `Tag`, `Text`, `Textarea`, `Timestamp`, `ToastProvider`, `Toggle`, `Tooltip`, `VisuallyHidden`, `useToast`.
17
+ - `@hirobius/design-system/patterns`: `DataTableSection`, `DestructiveSection`, `Form`, `FormActions`, `MetricTile`, `MetricTiles`, `Page`, `PageHeader`. These are not in the root barrel.
18
+ - Load one stylesheet once at the app root and put `data-hds` on the root element (`CONSUMING.md`).
19
+
20
+ ## Pick by need
21
+
22
+ 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.
23
+
24
+ - **Outermost wrapper of a screen** → `Page`, `Stack`. Not: `Container`, `Box`, a bare Stack as the root.
25
+ `<Page><Stack gap="spacious">…one child per section…</Stack></Page>`. `Container` is for a full-bleed surface that is not a page.
26
+ - **Screen title with breadcrumb, status and actions** → `PageHeader`, `Breadcrumb`, `Badge`. Not: Text as the page title, a Stack row of Text and Badge.
27
+ Exactly one per screen: `<PageHeader breadcrumb={<Breadcrumb items={[{ label, href }, { label }]} />} title="…" status={<Badge tone="success">Active</Badge>} />`.
28
+ - **Row of headline numbers (stats, KPIs, counts, totals)** → `MetricTiles`, `MetricTile`. Not: `Stat`, `StatusTile`, `CardMetric`, Card with Text.
29
+ `<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).
30
+ - **Table of records with row actions** → `DataTableSection`, `Badge`, `Button`. Not: Table on its own, Card around a Table.
31
+ `<DataTableSection title="Projects" columns={[{ key, label }]} rows={[{ key, cells: [...], actions: <Stack direction="row" gap="tight">…</Stack> }]} />`; leave the actions column out of `columns`.
32
+ - **Switch between panels of related content** → `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`. Not: `SegmentedControl`, Button rows.
33
+ `<Tabs defaultValue="a"><TabsList><TabsTrigger value="a">A</TabsTrigger></TabsList><TabsContent value="a">…</TabsContent></Tabs>`.
34
+ - **Status label in a header or table cell** → `Badge`. Not: `Tag`, `Text`.
35
+ `<Badge tone="success|info|warning|danger|neutral|inProgress">…</Badge>`; tone is the only styling input.
36
+ - **Form with fields and a submit button** → `Form`, `Textarea`, `Input`, `FormActions`, `Button`. Not: a raw `<form>`, a Stack of Buttons as the footer.
37
+ `<Form onSubmit={…}><Textarea label="Notes" … /><FormActions primary={<Button type="submit">Save note</Button>} /></Form>`; every field takes its own `label`.
38
+ - **Confirm a destructive or irreversible action (delete, archive, remove)** → `AlertDialog`, `Button`. Not: `Dialog`, `window.confirm`.
39
+ 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.
40
+ - **Brief confirmation after an action (saved, archived, sent)** → `ToastProvider`, `useToast`. Not: `Badge`, `Alert`, `Text`.
41
+ Wrap the app root once in `<ToastProvider>`, then `toast({ title: "Note saved", tone: "success" })` from `useToast()`.
42
+ - **Message that stays next to its content (error, warning, info)** → `Alert`. Not: Text in a coloured Surface.
43
+ `<Alert tone="warning">…</Alert>`.
44
+ - **Button or row action** → `Button`. Not: a raw `<button>`, InlineLink for an action.
45
+ Row actions: `<Button variant="tertiary" size="sm" aria-label="View Brand refresh">View</Button>`; the destructive one adds `tone="danger"`.
46
+ - **Space between sections or items** → `Stack`, `Grid`. Not: margins on children, raw px gaps such as px8.
47
+ Stack `gap`: `spacious` between page sections, `normal` inside a section, `tight` between buttons. `Grid` for two-dimensional layout.
48
+ - **Padded background or a titled content object** → `Surface`, `Card`. Not: a Box with background and padding.
49
+ `Surface` for a plain padded background; `Card` only when the content has a title or can be pressed.
50
+ - **Progress bar or ring** → `Progress`. Not: a hand-built bar.
51
+ `<Progress value={60} label="Upload" />`; inside an existing `Card`, `CardProgress`.
52
+ - **Body copy** → `Text`. Not: Text as a section heading.
53
+ Section headings come from the pattern that owns the section (`PageHeader` `title`, `DataTableSection` `title`), not from extra Text.
54
+ - **Empty state for a list or section** → `EmptyState`. Not: Text saying "No items".
55
+ `DataTableSection` shows its own through `emptyTitle`; use `EmptyState` elsewhere.
56
+ - **The one irreversible action of a screen (delete the client, close the account)** → `DestructiveSection`. Not: a danger Button that acts without a confirm.
57
+ 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.
58
+ - **Read-only details as label and value pairs** → `MetadataList`, `Field`. Not: a Table with two columns, Text pairs in a Stack.
59
+ `<MetadataList items={[{ term: "Owner", description: "Ada" }]} />` for a set; `Field` for one pair.
60
+ - **A person, or a group of people** → `Avatar`, `AvatarGroup`. Not: an image in a rounded Box.
61
+ `<Avatar src alt initials />`; several in a row: `<AvatarGroup max={3}>…Avatars…</AvatarGroup>`.
62
+ - **A date or time** → `Timestamp`. Not: a formatted string in Text.
63
+ `<Timestamp date="2026-10-14" />` renders a `<time dateTime>`; a date in a table cell may stay plain text.
64
+ - **A keyboard key or shortcut hint** → `Kbd`. Not: `InlineCode`, Text in a Box.
65
+ `<Kbd>⌘K</Kbd>`.
66
+ - **A quotation with attribution** → `Blockquote`. Not: Callout for a quote, italic Text.
67
+ `<Blockquote attribution="Ada Lovelace">…</Blockquote>`.
68
+ - **A fixed-width side rail beside fluid content, or a row that stacks when narrow** → `Sidebar`, `Switcher`. Not: a Grid with media queries.
69
+ `Sidebar` (`side`, `sideWidth`) for rail + content; `Switcher` (`threshold`) for a row that flips to a column. Neither needs a media query.
70
+ - **Keep an element stuck while its region scrolls** → `Pin`. Not: position: sticky in style.
71
+ `<Pin top="…">…</Pin>` inside the scrolling region.
72
+
73
+ ## Rules
74
+
75
+ - Tokens only: no raw hex or rgb, no raw `px`, no Tailwind classes, no custom CSS files. Spacing comes from component props (`Stack` `gap`), colour from component `tone` props or `semantic.*` tokens (`search_tokens`).
76
+ - No raw `<button>`, `<input>`, `<select>`, `<textarea>` or `<form>`: use `Button`, `Input`, `Select`, `Textarea`, `Form`.
77
+ - Every control has an accessible name: fields take `label`, a repeated row action takes an `aria-label` naming its row.
78
+ - Section headings come from the pattern that owns the section (`title` props). Do not add a `Text` heading the screen does not ask for.
79
+
80
+ ## Finish with the lint plugin
81
+
82
+ The ESLint plugin ships in this package. Pass its `recommended` rules before you report done:
83
+
84
+ ```js
85
+ // eslint.config.mjs
86
+ import hds from '@hirobius/design-system/eslint-plugin';
87
+
88
+ export default [...hds.configs.recommended];
89
+ ```
90
+
91
+ <!-- hds:repo-notes — everything below is hand-written, for agents working in the hds repo itself; consumers can stop here. -->
92
+
93
+ ## Working in the hds repo
94
+
95
+ Notes for agents working on this repository, not with the package. Read `CLAUDE.md` for
96
+ the full engineering protocol; this section is the home for autonomous-loop rules.
97
+
98
+ ## Ralph quality bar
99
+
100
+ REPO_TYPE: library
101
+ QUALITY BAR: Public npm package. Backward compatibility matters. Tests + Storybook stories required.
102
+
103
+ Rules for any autonomous loop in this repo:
104
+
105
+ - One issue per PR. Small steps. Never push to main.
106
+ - The gate (ralph/gate.sh) must pass before any PR. No exceptions.
107
+ - Fight entropy: leave the code better than you found it.
108
+ - No shortcut that creates debt someone else pays for.
109
+ - Merges are HUMAN-approved: `ralph-approved` on the PR, or the issue
110
+ pre-tagged `ralph-auto` (batch approval). Never merge yourself.
111
+
112
+ Loop mechanics (see `ralph/README.md` for the full contract):
113
+
114
+ - Labels: `ralph-ready` (queue, human) · `p0`–`p3` (priority, human) ·
115
+ `ralph-auto`/`ralph-approved` (merge approval, human) · `ralph-wip`
116
+ (claimed, loop) · `ralph-parked`/`needs-adrian` (parked with reason, loop) ·
117
+ `ralph-selfheal-attempted` (gate's one bounded repair, spent).
118
+ - Selection is deterministic (`ralph/next.sh`: priority label, then issue #);
119
+ claims are atomic (`refs/heads/ralph/claim-<n>`); the loop's exit codes are
120
+ a contract (`ralph/run.sh` header). Agents never touch `ralph-*` labels,
121
+ claim refs, or `ralph/{.lock,runs.jsonl,logs/}` — the harness owns those.
122
+ - The engine is org-level: workflows are thin callers into
123
+ `hirobius/ralph/.github/workflows/ralph-{run,gate}-reusable.yml@v1`.
package/CONSUMING.md CHANGED
@@ -29,6 +29,7 @@ version keeps the `LucideIcon` type identical).
29
29
  ```tsx
30
30
  import { Button, Card, Dialog } from '@hirobius/design-system';
31
31
  import '@hirobius/design-system/tokens.css'; // required — design tokens as CSS vars
32
+ import '@hirobius/design-system/fonts.css'; // optional — the HDS brand fonts (Satoshi, Geist Mono)
32
33
 
33
34
  export function Example() {
34
35
  return (
@@ -43,7 +44,8 @@ Available subpaths:
43
44
 
44
45
  | Import | What |
45
46
  | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46
- | `@hirobius/design-system` | All components (primitives, patterns, templates) |
47
+ | `@hirobius/design-system` | The 43-component core set ([README → What belongs in the system](README.md#what-belongs-in-the-system)), the rest of the allow-list and the templates; the `pattern` tier is only in `/patterns` |
48
+ | `@hirobius/design-system/fonts.css` | Optional brand fonts (Satoshi, Geist Mono); woff2 files ship in the package, relative URLs, `font-display: swap` |
47
49
  | `@hirobius/design-system/tokens.css` | Token CSS variables (import once at app root) |
48
50
  | `@hirobius/design-system/tokens` | Token values as TS constants |
49
51
  | `@hirobius/design-system/cn` | `cn()` class-merge helper |
@@ -51,8 +53,8 @@ Available subpaths:
51
53
  | `@hirobius/design-system/contexts` | Theme / language / tenant / font providers |
52
54
  | `@hirobius/design-system/brand` | Palette → HDS-semantic overlay bridge (static / SSR / Astro; see [`docs/CONSUMING.md` §12](docs/CONSUMING.md#12-static-astro-sites--the-brand-overlay-bridge)) |
53
55
  | `@hirobius/design-system/scroll` | Opt-in scroll-motion primitives — `SmoothScroll` (Lenis), `useScrollProgress` (Motion). Requires the optional peer `lenis`. See [`docs/CONSUMING.md` §13](docs/CONSUMING.md#13-scroll-motion-the-scroll-subpath) |
54
- | `@hirobius/design-system/patterns` | The 22 `pattern`-tier components (nav shells, feeds, rails, pickers — see hds#254). Also still re-exported from the root for one minor (`@deprecated`, removed at the next major) — prefer this subpath in new code. |
55
- | `@hirobius/design-system/icons` | Curated Lucide icon set for `IconButton` / `Icon` (`Ellipsis`, `Pencil`, `Trash2`, `X`, …). Names listed in the manifest `iconSet`. |
56
+ | `@hirobius/design-system/patterns` | The 12 pattern modules: the hds#254 `pattern` tier (page shells, forms, code blocks, page sections). The only entry that exports them: 0.20.0 removed their root re-exports (the `hds-patterns-subpath` codemod, see MIGRATIONS.md). |
57
+ | `@hirobius/design-system/icons` | Curated Lucide icon set for `Icon`, and so for `Button iconOnly` (`Ellipsis`, `Pencil`, `Trash2`, `X`, …). Names listed in the manifest `iconSet`. |
56
58
  | `@hirobius/design-system/static.css` | CSS-only static-primitive layer — `.hds-badge`/`.hds-card`/`.hds-alert`/`.hds-divider`/`.hds-tag` classes, no React. See [`docs/CONSUMING.md` §14](docs/CONSUMING.md#14-css-only-static-primitives--badgecardalertdividertag-with-no-react) |
57
59
 
58
60
  The package is **ESM-only**, so consume it with a modern bundler (Vite, Next.js,
@@ -113,8 +115,11 @@ Releases follow [semver](https://semver.org/) and are tracked in
113
115
  pnpm update @hirobius/design-system # latest within your version range
114
116
  ```
115
117
 
116
- For a major (breaking) release, bump the version explicitly and review the
117
- CHANGELOG entry. Token or export changes are released as majors.
118
+ For a breaking release, bump the version explicitly and review the CHANGELOG
119
+ entry. Before 1.0 a breaking change (a removed export or token) ships in a 0.x
120
+ minor, which a caret range such as `^0.19` never picks up; from 1.0 on it ships
121
+ in a major. Each removal is listed in [MIGRATIONS.md](MIGRATIONS.md) with its
122
+ replacement and, where one applies, a codemod (ADR-014).
118
123
 
119
124
  ---
120
125
 
@@ -122,7 +127,7 @@ CHANGELOG entry. Token or export changes are released as majors.
122
127
 
123
128
  ```bash
124
129
  pnpm changeset add # record a patch/minor/major bump + notes
125
- pnpm changeset:version # apply bumps + regenerate CHANGELOG.md
130
+ pnpm changeset:version # apply bumps + regenerate CHANGELOG.md + refresh docs/api/api-baseline.json
126
131
  # commit + push to main → the Release workflow publishes to public npm
127
132
  ```
128
133
 
package/DESIGN.md CHANGED
@@ -28,28 +28,29 @@ Feedback hues are never decorative; do not use them as accents. Light and dark m
28
28
  <!-- auto:start:typography -->
29
29
  HDS ships three typefaces — each with a distinct and exclusive role:
30
30
 
31
- - **Display / Heading font**: Satoshi. Bound exclusively to `display`, `h1`, `h2`, and `h3` styles. Never used for body copy or UI labels.
31
+ - **Display / Heading font**: Satoshi. Bound exclusively to the `display` and `title` styles. Never used for body copy or UI labels.
32
32
  - **Body / UI font**: Satoshi. All prose, labels, small text, captions, and UI copy.
33
33
  - **Mono font**: Geist Mono. Reserved for tokens, code, technical callouts, and metric readouts.
34
34
 
35
- 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.
35
+ Weights declared: `400` regular, `500` medium, `700` bold. Heading styles (display · title) use `700` bold; body, UI, and caption use `400` regular / `500` medium.
36
36
 
37
37
  ### Type ramp
38
38
 
39
39
  | Role | Size (desktop max) | Weight | Use |
40
40
  | --- | --- | --- | --- |
41
- | `semantic.typography.display` | 60px | 700 | Display headline |
42
- | `semantic.typography.h1` | 48px | 700 | Primary section headings (h1) |
43
- | `semantic.typography.h2` | 30px | 700 | Secondary section headings (h2) |
44
- | `semantic.typography.h3` | 20px | 700 | Component and card headers (h3) |
45
- | `semantic.typography.body` | 16px | 500 | Body prose |
46
- | `semantic.typography.ui` | 14px | 500 | UI text for nav, labels, captions |
47
- | `semantic.typography.caption` | 12px | 500 | Caption / helper text |
48
- | `semantic.typography.eyebrow` | 12px | 500 | Eyebrow / kicker label |
49
- | `semantic.typography.mono` | 14px | 400 | Monospace for code, token names, shortcuts |
41
+ | `semantic.typography.display` | 48px | 700 | One per page, hero only |
42
+ | `semantic.typography.title` | 24px | 700 | Section and card headings |
43
+ | `semantic.typography.body` | 16px | 400 | Prose |
44
+ | `semantic.typography.ui` | 14px | 500 | Controls, nav, table text, values |
45
+ | `semantic.typography.caption` | 12px | 500 | Metadata, help text and labels, in sentence case |
46
+ | `semantic.typography.mono` | 13px | 400 | Code and ids |
47
+ | `semantic.typography.h1` | 24px | 700 | deprecated: use semantic.typography.title |
48
+ | `semantic.typography.h2` | 24px | 700 | deprecated: use semantic.typography.title |
49
+ | `semantic.typography.h3` | 24px | 700 | deprecated: use semantic.typography.title |
50
+ | `semantic.typography.eyebrow` | 12px | 500 | deprecated: use semantic.typography.caption |
50
51
  | `semantic.typography.lineHeight.none` | | | Semantic alias for leading-none (1) |
51
52
 
52
- > Responsive `clamp()` overrides live in `src/styles/theme.css`; tokens store the desktop-max static value.
53
+ > 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.
53
54
  <!-- auto:end:typography -->
54
55
 
55
56
  ## Spacing
@@ -59,9 +60,19 @@ Weights declared: `400` regular, `500` medium, `700` bold. Heading styles (displ
59
60
 
60
61
  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`)
61
62
 
62
- 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.
63
+ 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.
63
64
  <!-- auto:end:spacing -->
64
65
 
66
+ ## Layout contract (v1)
67
+
68
+ 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`).
69
+
70
+ 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.
71
+ 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"`.
72
+ 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`.
73
+ 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.
74
+ 5. **Forms.** A form control in a form context has a max width (`40rem` by default) and siblings share one width.
75
+
65
76
  ## Corner-Radius Policy
66
77
 
67
78
  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.
@@ -90,7 +101,7 @@ Depth is communicated through 4 elevation roles bundled by `semantic.elevation.*
90
101
 
91
102
  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.
92
103
 
93
- 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.
104
+ 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.
94
105
 
95
106
  ## Motion
96
107
 
@@ -121,7 +132,7 @@ Default most interactive feedback to `productive` (150ms, decelerate). Reserve `
121
132
 
122
133
  | Token | Value | Purpose |
123
134
  | --- | --- | --- |
124
- | `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). |
135
+ | `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). |
125
136
  <!-- auto:end:motion -->
126
137
 
127
138
  ## Components
@@ -129,7 +140,7 @@ Default most interactive feedback to `productive` (150ms, decelerate). Reserve `
129
140
  <!-- auto:start:components -->
130
141
  | Component | Radius | States | Guidance |
131
142
  | --- | --- | --- | --- |
132
- | **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`. |
143
+ | **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`. |
133
144
  | **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`. |
134
145
  | **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. |
135
146
  | **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. |
@@ -152,7 +163,7 @@ Every HDS card surface must conform to this anatomy exactly. No creative interpr
152
163
  | 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 |
153
164
  | Padding | `var(--semantic-space-component-padding)` or `<HdsSurface padding="component">` | Raw pixel values or ad hoc insets |
154
165
  | 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 |
155
- | Title | `hds.typeStyles.heading3` / `<HdsText variant="heading3">` | Any other type style for the primary card heading |
166
+ | Title | `hds.typeStyles.title` / `<Text variant="title">` | Any other type style for the primary card heading |
156
167
  | Subtitle / meta | `hds.typeStyles.caption` + `var(--semantic-color-content-secondary)` | Primary content color or body size for secondary text |
157
168
  | Hover (interactive only) | `transform: scale(1.02)` | Background fill change, border color shift, or opacity fade on hover |
158
169
 
@@ -164,7 +175,7 @@ Five patterns sit above the primitives so that every screen is composed the same
164
175
 
165
176
  ### Page title rule
166
177
 
167
- 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`.
178
+ 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`.
168
179
 
169
180
  ### Which one, when
170
181
 
@@ -176,14 +187,16 @@ Pick by what the thing is. The first four rows are for a headline number or a st
176
187
  | `Stat` | An inline number inside prose or a dense list. | `MetricTiles` when the number stands alone in a row of its own. |
177
188
  | `Card.Metric` | A metric inside an existing `Card` only. | `MetricTiles` when there is no enclosing `Card`. |
178
189
  | `StatusTile` | State with notes and a trailing badge, never a number. | `MetricTiles` when the tile is a number; `StatusTile` never carries a value. |
179
- | `Card` | A titled or interactive content object (header, body, footer, or selectable). | `Surface` when there is no title and nothing to press. |
190
+ | `Card` | A titled or interactive content object (header, body, footer, `selectable`). | `Surface` when there is no title and nothing to press. |
180
191
  | `Surface` | A plain padded background around content that has no header of its own. | `Card` when the content is titled or interactive. |
181
192
  | `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. |
182
193
  | `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. |
183
194
 
195
+ 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.
196
+
184
197
  `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.
185
198
 
186
- `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`.
199
+ `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`.
187
200
 
188
201
  ### Destructive and table sections
189
202
 
@@ -215,6 +228,6 @@ Unresolved rules that the live repo cannot yet answer confidently are tracked in
215
228
  <!-- auto:start:build-meta -->
216
229
  ---
217
230
 
218
- > Generated from `hirobius.tokens.json` (381 tokens) and `public/hds-manifest.json` by `scripts/build-design-md.mjs`.
231
+ > Generated from `hirobius.tokens.json` (389 tokens) and `public/hds-manifest.json` by `scripts/build-design-md.mjs`.
219
232
  > Hand-edit `DESIGN.source.md`; this file (`DESIGN.md`) is overwritten by `pnpm tokens`.
220
233
  <!-- auto:end:build-meta -->
package/NOTICE.md CHANGED
@@ -3,7 +3,7 @@
3
3
  `@hirobius/design-system` is not under a single license.
4
4
 
5
5
  - **HDS code** (components, compiled JavaScript, types, CSS, and token files): MIT. See [LICENSE](LICENSE).
6
- - **Satoshi** (weights 500 and 700, by Indian Type Foundry, distributed through [Fontshare](https://www.fontshare.com/fonts/satoshi)): ITF Free Font License.
6
+ - **Satoshi** (weights 400, 500 and 700, by Indian Type Foundry, distributed through [Fontshare](https://www.fontshare.com/fonts/satoshi)): ITF Free Font License.
7
7
  - **Geist Mono** (weight 400, by Vercel, [source](https://github.com/vercel/geist-font)): SIL Open Font License 1.1.
8
8
 
9
9
  Both fonts are embedded as base64 `@font-face` data in `dist/tokens.css` and `dist/styles.css`. The MIT license does not cover them; each font keeps its own license. `dist/variables.css` embeds no fonts.
package/README.md CHANGED
@@ -12,9 +12,9 @@ pnpm add @hirobius/design-system
12
12
 
13
13
  <!-- auto:start:front-door-counts -->
14
14
 
15
- - **109** public component modules, exported from `src/index.ts`
16
- - **381** DTCG tokens in `hirobius.tokens.json`, compiled to CSS variables and TypeScript constants
17
- - **484** Storybook stories in **121** story files
15
+ - **53** public component modules, exported from `src/index.ts`
16
+ - **389** DTCG tokens in `hirobius.tokens.json`, compiled to CSS variables and TypeScript constants
17
+ - **341** Storybook stories in **71** story files
18
18
 
19
19
  <!-- auto:end:front-door-counts -->
20
20
 
@@ -29,9 +29,12 @@ Agents: install the consumer skill with `npx skills add hirobius/hds --skill hds
29
29
 
30
30
  <!-- auto:start:consumer-usage -->
31
31
 
32
- The Ops dashboard (`hirobius/ops`) is the only verified component-level consumer: **35** of its source files import from `@hirobius/design-system`, using **25** distinct components. The two screenshots below are Ops pages rendered from its `main` (measured at commit `01fce2f`).
32
+ The Ops dashboard (`hirobius/ops`) is the only product app that uses components: **33** of its source files import from `@hirobius/design-system`, using **25** distinct components. The two screenshots below are Ops pages rendered from its `main` (measured at commit `76ef65e`).
33
33
 
34
- Other consumers: the split into product apps and token-level sites is not yet confirmed, so it is not stated here.
34
+ | Consumer kind | Count | How it uses HDS |
35
+ | ----------------- | ----- | ----------------------------------------- |
36
+ | Product apps | 1 | Components and tokens |
37
+ | Token-level sites | 4 | Tokens and CSS only, no component imports |
35
38
 
36
39
  [![Ops library page](docs/images/ops-library.png)](docs/images/ops-library.png)
37
40
 
@@ -39,7 +42,36 @@ Other consumers: the split into product apps and token-level sites is not yet co
39
42
 
40
43
  <!-- auto:end:consumer-usage -->
41
44
 
42
- The numbers come from `pnpm consumer:usage --root <ops checkout>`, which measures that checkout and records its commit in `docs/data/consumer-usage.json`; without a root it reuses the committed snapshot. `scripts/__tests__/consumer-usage.test.mjs` fails if this section drifts from that snapshot. The product-app and token-level-site split is a declared figure in that file and stays out of this section until `consumersConfirmed` there is set to true.
45
+ The numbers come from `pnpm consumer:usage --root <ops checkout>`, which measures that checkout's `src/` and records its commit in `docs/data/consumer-usage.json` (pass `--commit <sha>` when the root is a `git archive` export); without a root it reuses the committed snapshot. A deprecated alias counts as the component it points to (`HdsCheckbox` as `Checkbox`). The components that Ops's page- and video-clone prompts tell generated code to import are listed there as `promptContracts` and are not counted here. `scripts/__tests__/consumer-usage.test.mjs` fails if this section drifts from that snapshot. The product-app and token-level-site split is a declared figure in that file, not a measurement. It was confirmed on 2026-10-01 (hds#389: there is no second product app), so `consumersConfirmed` there is true and the table above shows it.
46
+
47
+ ## What belongs in the system
48
+
49
+ <!-- auto:start:core-set -->
50
+
51
+ **43** components are the core set: the brand-neutral, composable surface every HDS screen is built from, and the part of the system that has to be excellent. Import them from the package root; each carries `core: true` in `public/hds-manifest.json`.
52
+
53
+ - **Actions:** `Button`
54
+ - **Display:** `Avatar`, `Card`, `EmptyState`, `Field`, `Icon`, `Kbd`, `Table`
55
+ - **Feedback:** `Alert`, `Badge`, `Progress`, `Skeleton`, `Spinner`, `ToastProvider`
56
+ - **Inputs:** `Checkbox`, `Combobox`, `Input`, `Radio`, `SegmentedControl`, `Select`, `Slider`, `Tag`, `Textarea`, `Toggle`
57
+ - **Layout:** `Box`, `Container`, `Disclosure`, `Divider`, `Grid`, `Stack`, `Surface`
58
+ - **Navigation:** `Breadcrumb`, `InlineLink`, `Pagination`, `Tabs`
59
+ - **Overlays:** `Dialog`, `Menu`, `Popover`, `Tooltip`
60
+ - **Theming:** `HdsRouterProvider`, `HdsThemeProvider`
61
+ - **Typography:** `Text`
62
+ - **Utility:** `VisuallyHidden`
63
+
64
+ **12** pattern modules ship from `@hirobius/design-system/patterns`, and only from there: composed surfaces that assume a product (page shells, forms, code blocks, page sections), built from the core set. One name per module; its parts and props types come with it.
65
+
66
+ `AssetImg`, `CodeBlock`, `DataTableSection`, `DestructiveSection`, `ErrorPattern`, `Form`, `FormActions`, `MetricTiles`, `Page`, `PageHeader`, `Reveal`, `StatusTile`
67
+
68
+ The package root also exports the rest of the allow-list (compound parts, and the components outside the core set; the [consumer skill](skills/hds-consumer/SKILL.md) lists them by category): reach for the core set first. 0.20.0 removed the root re-exports of the pattern modules, the docs internals and 13 components that fold into a survivor ([MIGRATIONS.md](MIGRATIONS.md#0200-removals-2026-10-01)).
69
+
70
+ Every component's disposition (core, pattern, fold or internal) is in the [architecture doc](docs/hds-architecture-2026-09-18.html), ratified 2026-09-26 in [hds#254](https://github.com/hirobius/hds/issues/254); [ADR-031](docs/adr/031-core-set-and-dispositions.md) records the decision and what has changed since.
71
+
72
+ <!-- auto:end:core-set -->
73
+
74
+ This section is generated by `pnpm readme:counts` (which `pnpm tokens` also runs) from `scripts/lib/core-components.mjs`, `src/patterns.ts` and the manifest. `scripts/__tests__/core-set.test.mjs` fails if it drifts from them.
43
75
 
44
76
  ## Using the published package
45
77
 
@@ -51,7 +83,7 @@ guide: **[docs/CONSUMING.md](docs/CONSUMING.md)**.
51
83
  > `@hirobius/design-system` entry under GitHub Packages
52
84
  > (`/pkgs/npm/design-system`). That listing is **frozen and no longer updated** —
53
85
  > publishing moved to the public npm registry in
54
- > [#39](https://github.com/hirobius/hirobius-design-system/pull/39). Always
86
+ > [#39](https://github.com/hirobius/hds/pull/39). Always
55
87
  > install from npm:
56
88
  > **[npmjs.com/package/@hirobius/design-system](https://www.npmjs.com/package/@hirobius/design-system)**.
57
89
 
@@ -63,8 +95,9 @@ npm install @hirobius/design-system react react-dom
63
95
  ```
64
96
 
65
97
  ```tsx
66
- // once at the app root — full bundle: tokens + theme + utilities + embedded fonts
98
+ // once at the app root — full bundle: tokens + theme + utilities
67
99
  import '@hirobius/design-system/tokens.css';
100
+ import '@hirobius/design-system/fonts.css'; // optional: the HDS brand fonts
68
101
  import { Button } from '@hirobius/design-system';
69
102
 
70
103
  // add data-hds to the root (or any section) so the scoped base styles apply
@@ -153,7 +186,7 @@ pnpm build-storybook
153
186
 
154
187
  HDS is built around three structural rules:
155
188
 
156
- - **Strict semantics** — public surfaces prefer system primitives such as `Stack`, `Grid`, `Surface`, and `TextLockup` instead of raw layout divs or ad hoc CSS.
189
+ - **Strict semantics** — public surfaces prefer system primitives such as `Stack`, `Grid`, `Surface`, and `Text` instead of raw layout divs or ad hoc CSS.
157
190
  - **Polymorphism** — primitives preserve semantic HTML while staying composable through governed APIs such as `forwardRef`, `as`, and layout slots.
158
191
  - **12-column grid** — page structure follows a consistent editorial grid: readable center columns, intentional breakout zones, and explicit `gap` ownership rather than one-off spacing math.
159
192
 
@@ -165,6 +198,8 @@ Source-of-truth files:
165
198
  - `DESIGN.md` — lean visual spec for agents and engineers.
166
199
  - `DESIGN-HANDOFF.md` — verbose visual mirror for handoff and review.
167
200
 
201
+ Which components belong in the system, and what happens to the rest, is decided in the [architecture doc](docs/hds-architecture-2026-09-18.html): every component is core, pattern, fold or internal, ratified 2026-09-26 in hds#254 and recorded in [ADR-031](docs/adr/031-core-set-and-dispositions.md). [What belongs in the system](#what-belongs-in-the-system) above lists the result.
202
+
168
203
  Component-by-component Figma links live in [docs/DESIGN_LINKS.md](docs/DESIGN_LINKS.md), generated by `pnpm figma:links`.
169
204
 
170
205
  ## Accessibility
@@ -173,10 +208,10 @@ What CI and the hooks check today, and nothing more:
173
208
 
174
209
  - **Contrast:** `scripts/check-contrast.mjs` enforces WCAG AA contrast on the core token pairs, in light and dark. It runs in CI and the pre-commit hook.
175
210
  - **Lint:** `jsx-a11y` rules run inside ESLint with zero warnings allowed.
176
- - **Focus:** `scripts/check-focus-states.mjs` (`pnpm check:focus`) audits focus styles on interactive components. It runs in the agent gate and on demand, not in the main CI workflow, and currently reports 1 violation (`src/app/components/asset-img.tsx`).
177
- - **Storybook:** the `addon-a11y` panel shows axe results for each story while you review it. It reports; it does not gate.
211
+ - **Focus:** `scripts/check-focus-states.mjs` (`pnpm check:focus`) audits focus styles on interactive components. It runs in `pretest` under `pnpm test`, so in the pre-push hook and in CI, and passes with 0 violations.
212
+ - **Storybook:** the `addon-a11y` panel shows axe results for each story while you review it. The gate is `scripts/check-storybook-axe.mjs`: CI scans every built story in light and dark, including the open Select and Combobox, and fails on serious or critical violations outside its allowlist (`scripts/axe-allowlist.json`). The allowlist has one entry: `aria-hidden-focus` on the open Select story, where Radix Select hides the page around its listbox while focus stays trapped inside it (hds#407).
178
213
 
179
- There is no automated screen-reader testing.
214
+ Screen-reader output is checked by `tests/primitive-contracts/screen-reader.contract.test.tsx`, which runs in `pnpm test` and walks Dialog, Menu, Select, Combobox, Table and Alert with a virtual screen-reader (`@guidepup/virtual-screen-reader`) under jsdom, asserting the role, name and state phrases it reads and the alert's live-region announcement; it reads the jsdom accessibility tree, not what a desktop or mobile screen-reader says.
180
215
 
181
216
  ## Visual direction: Editorial Enterprise
182
217
 
@@ -201,7 +236,7 @@ The gates are deterministic and need no browser or live site:
201
236
  Three agents build the same Client-detail screen from the public docs, and `pnpm eval:consistency` measures how alike the results are: builds, token violations, axe, component-set overlap (Jaccard) and pixel diff. Today it runs the offline half over a directory of apps (`pnpm eval:consistency -- --apps <dir> --offline`, violations and Jaccard from source only). The ledger, the thresholds and the screen spec live in [`eval/consistency/`](eval/consistency/README.md). The latest recorded run, as printed by `pnpm eval:consistency -- --summary`:
202
237
 
203
238
  ```text
204
- Agent consistency 2026-09-30 (harness, design-system 0.18.0): FAIL - builds 3/3, violations 0, axe 0, Jaccard min 0.7368 (limit >= 0.85), light diff max 1.0268% (limit <= 1.5%)
239
+ Agent consistency 2026-10-05 (harness, design-system 0.20.0): FAIL - builds 3/3, violations 0, axe 0, Jaccard min 0.4091 (limit >= 0.85), light diff max 3.2096% (limit <= 1.5%)
205
240
  ```
206
241
 
207
242
  The baseline misses its thresholds on purpose: they are not softened to make it pass.