@hirobius/design-system 0.20.0 → 0.22.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 (130) hide show
  1. package/AGENTS.md +125 -0
  2. package/CHANGELOG.md +969 -0
  3. package/CONSUMING.md +27 -11
  4. package/DESIGN.md +28 -17
  5. package/MIGRATIONS.md +603 -0
  6. package/NOTICE.md +1 -1
  7. package/README.md +39 -5
  8. package/UPGRADING.md +222 -0
  9. package/codemods/hds-prefix.mjs +28 -13
  10. package/codemods/jsx-fold.mjs +28 -13
  11. package/codemods/lib/atomic-write.mjs +28 -0
  12. package/codemods/lib/installed-version.mjs +354 -0
  13. package/codemods/lib/project.mjs +395 -0
  14. package/codemods/lib/record.mjs +110 -0
  15. package/codemods/lib/scan.mjs +323 -0
  16. package/codemods/lib/semver.mjs +57 -0
  17. package/codemods/not-found-pattern.mjs +3 -3
  18. package/codemods/patterns-subpath.mjs +28 -12
  19. package/codemods/registry.mjs +67 -0
  20. package/codemods/tile-grid.mjs +3 -3
  21. package/codemods/upgrade.mjs +909 -0
  22. package/dist/chunks/empty-state-DNoThgfV.js +1227 -0
  23. package/dist/chunks/form-By82qZcC.js +66 -0
  24. package/dist/fonts/geist-mono-400.woff2 +0 -0
  25. package/dist/fonts/satoshi-400.woff2 +0 -0
  26. package/dist/fonts/satoshi-500.woff2 +0 -0
  27. package/dist/fonts/satoshi-700.woff2 +0 -0
  28. package/dist/fonts.css +53 -0
  29. package/dist/form.js +1 -1
  30. package/dist/hirobius-ui.js +876 -877
  31. package/dist/manifest.js +3 -3
  32. package/dist/patterns.js +189 -170
  33. package/dist/styles.css +2 -2
  34. package/dist/tokens.css +1 -1
  35. package/dist/tokens.js +136 -57
  36. package/dist/types/src/app/components/alert-dialog.d.ts +5 -0
  37. package/dist/types/src/app/components/alert.d.ts +1 -1
  38. package/dist/types/src/app/components/asset-img.d.ts +1 -0
  39. package/dist/types/src/app/components/avatar-group.d.ts +1 -0
  40. package/dist/types/src/app/components/avatar.d.ts +1 -1
  41. package/dist/types/src/app/components/badge.d.ts +2 -2
  42. package/dist/types/src/app/components/blockquote.d.ts +1 -0
  43. package/dist/types/src/app/components/breadcrumb.d.ts +1 -1
  44. package/dist/types/src/app/components/button.d.ts +3 -1
  45. package/dist/types/src/app/components/callout.d.ts +1 -1
  46. package/dist/types/src/app/components/card.d.ts +4 -3
  47. package/dist/types/src/app/components/checkbox.d.ts +1 -1
  48. package/dist/types/src/app/components/code-block.d.ts +1 -1
  49. package/dist/types/src/app/components/combobox.d.ts +1 -1
  50. package/dist/types/src/app/components/data-table-section.d.ts +1 -0
  51. package/dist/types/src/app/components/destructive-section.d.ts +1 -0
  52. package/dist/types/src/app/components/dialog.d.ts +2 -1
  53. package/dist/types/src/app/components/disclosure.d.ts +1 -1
  54. package/dist/types/src/app/components/divider.d.ts +1 -1
  55. package/dist/types/src/app/components/empty-state.d.ts +1 -1
  56. package/dist/types/src/app/components/error-pattern.d.ts +6 -1
  57. package/dist/types/src/app/components/field.d.ts +1 -1
  58. package/dist/types/src/app/components/form-actions.d.ts +1 -0
  59. package/dist/types/src/app/components/form-control.d.ts +10 -0
  60. package/dist/types/src/app/components/form.d.ts +1 -1
  61. package/dist/types/src/app/components/grid.d.ts +5 -0
  62. package/dist/types/src/app/components/hds-tooltip.d.ts +1 -1
  63. package/dist/types/src/app/components/icon.d.ts +1 -0
  64. package/dist/types/src/app/components/inline-link.d.ts +1 -1
  65. package/dist/types/src/app/components/input.d.ts +1 -1
  66. package/dist/types/src/app/components/kbd.d.ts +1 -0
  67. package/dist/types/src/app/components/menu.d.ts +1 -1
  68. package/dist/types/src/app/components/metadata-list.d.ts +1 -0
  69. package/dist/types/src/app/components/metric-tiles.d.ts +1 -0
  70. package/dist/types/src/app/components/pagination.d.ts +1 -1
  71. package/dist/types/src/app/components/popover.d.ts +1 -1
  72. package/dist/types/src/app/components/progress.d.ts +2 -2
  73. package/dist/types/src/app/components/radio.d.ts +1 -1
  74. package/dist/types/src/app/components/select.d.ts +1 -1
  75. package/dist/types/src/app/components/skeleton.d.ts +2 -2
  76. package/dist/types/src/app/components/slider.d.ts +1 -1
  77. package/dist/types/src/app/components/spinner.d.ts +1 -1
  78. package/dist/types/src/app/components/stat.d.ts +5 -4
  79. package/dist/types/src/app/components/status-list-item.d.ts +1 -1
  80. package/dist/types/src/app/components/status-tile.d.ts +4 -3
  81. package/dist/types/src/app/components/table.d.ts +6 -1
  82. package/dist/types/src/app/components/tabs.d.ts +1 -1
  83. package/dist/types/src/app/components/tag.d.ts +1 -1
  84. package/dist/types/src/app/components/text.d.ts +53 -6
  85. package/dist/types/src/app/components/textarea.d.ts +1 -1
  86. package/dist/types/src/app/components/timestamp.d.ts +1 -0
  87. package/dist/types/src/app/components/toast.d.ts +1 -1
  88. package/dist/types/src/app/components/toggle.d.ts +1 -1
  89. package/dist/types/src/app/data/manifest-types.d.ts +4 -0
  90. package/dist/types/src/app/design-system/generated-token-refs.d.ts +37 -28
  91. package/dist/types/src/app/design-system/generated-token-values.d.ts +3 -0
  92. package/dist/types/src/app/design-system/tokens.d.ts +117 -15
  93. package/dist/types/src/index.d.ts +122 -72
  94. package/dist/variables.css +41 -33
  95. package/docs/CONSUMING.md +63 -39
  96. package/hirobius.tokens.json +163 -112
  97. package/llms.txt +158 -50
  98. package/mcp/catalog.mjs +369 -0
  99. package/mcp/core-set.mjs +66 -0
  100. package/mcp/guide.mjs +272 -0
  101. package/mcp/hds-mcp.mjs +49 -0
  102. package/mcp/server.mjs +94 -0
  103. package/package.json +53 -11
  104. package/public/hds-manifest.json +430 -336
  105. package/public/llms/components.txt +4 -3
  106. package/public/llms/layout.txt +1 -1
  107. package/public/llms-full.txt +190 -70
  108. package/public/llms.txt +158 -50
  109. package/scripts/eslint-plugin-hds/index.d.mts +29 -0
  110. package/scripts/eslint-plugin-hds/index.mjs +55 -0
  111. package/scripts/eslint-plugin-hds/package.json +30 -0
  112. package/scripts/eslint-plugin-hds/rules/no-raw-controls.mjs +47 -0
  113. package/scripts/eslint-plugin-hds/rules/no-raw-hex.mjs +70 -0
  114. package/scripts/eslint-plugin-hds/rules/no-raw-px-spacing.mjs +125 -0
  115. package/scripts/eslint-plugin-hds/rules/prefer-hds-layout-primitive.mjs +54 -0
  116. package/scripts/eslint-plugin-hds/rules/sx-token-first.mjs +111 -0
  117. package/scripts/eslint-plugin-hds/rules/utils.mjs +66 -0
  118. package/src/app/data/component-api.json +154 -93
  119. package/upgrade/index.json +208 -0
  120. package/upgrade/releases/0.17.0.json +962 -0
  121. package/upgrade/releases/0.18.0.json +8 -0
  122. package/upgrade/releases/0.19.0.json +233 -0
  123. package/upgrade/releases/0.19.1.json +8 -0
  124. package/upgrade/releases/0.20.0.json +5108 -0
  125. package/upgrade/releases/0.21.0.json +538 -0
  126. package/upgrade/releases/0.22.0.json +94 -0
  127. package/upgrade/schema.json +989 -0
  128. package/dist/chunks/empty-state-BBqhCx0S.js +0 -1217
  129. package/dist/chunks/form-COP3vK9_.js +0 -66
  130. package/dist/types/src/app/components/status-dot.d.ts +0 -39
package/CHANGELOG.md ADDED
@@ -0,0 +1,969 @@
1
+ # Changelog
2
+
3
+ ## 0.22.0
4
+
5
+ ### Upgrade
6
+
7
+ ```sh
8
+ npx @hirobius/design-system@latest upgrade
9
+ ```
10
+
11
+ - Do by hand: The raw tokens (the tokens export and hirobius.tokens.json) are strict DTCG now, so read semantic.motion.\*.\$value.timingFunction instead of .easing, and the elastic spring and each semantic.elevation level from \$extensions\["com.hirobius.hds"].\$value.
12
+ - Do by hand: The figmaUrl of each component in the ./manifest export (public/hds-manifest.json) now points at file 2VgBbVpKiDnu0aftJEVyBQ, "HDS Tokens & Components"; node ids are unchanged, so a stored link needs only the new file key.
13
+ - Do by hand: The hds-mcp get_component tool now marks core: true only on the 43 ratified core components and recommended: true on the wider set AGENTS.md recommends, and list_core names its set, so read recommended where you relied on core for that wider set.
14
+ - Looks different: --component-tag-lineHeight is now 1.5 instead of an invalid value the browser ignored, so text that reads it gets that line height.
15
+ - And 2 more: see [UPGRADING.md](https://github.com/hirobius/hds/blob/main/UPGRADING.md#0220).
16
+
17
+ ### Minor Changes
18
+
19
+ - cd10fa5: `hirobius.tokens.json` is now strict W3C DTCG. `component.tag.lineHeight` is typed `number`, matching the token it aliases, which also fixes `--component-tag-lineHeight` in `dist/hds-tokens.css` (it was `undefinedundefined`, now `1.5`). Generated CSS variables, TypeScript and Figma variables are otherwise unchanged, but the raw JSON (the file itself and the `tokens` export) changes shape in three places, so code that reads it directly must move: `semantic.motion.*.$value.easing` is now `semantic.motion.*.$value.timingFunction` (a DTCG `transition`, with a `delay` of 0ms, and the group's `$type` is `transition`); `primitive.easing.elastic.$value` is now a `cubicBezier` array, with the spring parameters under `$extensions["com.hirobius.hds"].$value`; and each `semantic.elevation.*` level has no `$value`, its surface, shadow and border sit under `$extensions["com.hirobius.hds"].$value`. The `$type` values `motion`, `spring` and `elevation` also moved into that extension. `fromDtcg` in `scripts/lib/token-dialect.mjs` returns the previous shape.
20
+
21
+ ### Patch Changes
22
+
23
+ - 4ae32c7: One source for the core set. The ratified core list now lives in `mcp/core-set.mjs`, and the hds-mcp server and `AGENTS.md` build the guide's wider set on it. `list_core` and `AGENTS.md` now call that wider set "recommended" (`list_core` gains a `set` field, `get_component` marks `recommended: true`), and `get_component` marks `core: true` only for the 43 ratified components, matching the manifest flag. The names shown are unchanged.
24
+ - 3c66e95: Figma links point at the one HDS library. Every `figmaUrl` in `public/hds-manifest.json` (the `./manifest` export) and every `@figma` tag now names file `2VgBbVpKiDnu0aftJEVyBQ`, "HDS Tokens & Components"; the file they named before is retired. Node ids are unchanged, so a tool that reads the node id keeps working, and one that stored a URL only needs the new file key. New components are drafted in a separate file, "HDS Staging", and redrawn in the library before they ship, so no link the package carries ever names HDS Staging. No component, prop, token or style changed.
25
+ - fc61af3: One command now upgrades you (hds#452): `npx @hirobius/design-system@latest upgrade`. It reads the version you have from your lockfile (pnpm, npm, yarn or bun; then `node_modules`, then `--from`), upgrades every workspace package that uses HDS from its own version, runs each release's codemods, bumps the range with its operator kept, adds back a dependency HDS stopped installing that your code still imports, installs and runs your typecheck. It then prints four lists: Fixed for you, Looks different (only what your code uses), Coming next and Do by hand. It exits 0 when done, 1 while work is left and 2 when it refuses, changing nothing: below the 0.16.0 floor, a downgrade, or a version it has no steps for. `--dry-run` and `--check` write nothing, `--json` and `--report <file>` give the report as JSON (`upgrade/schema.json`, `$defs.upgradeReport`). The steps always come from the copy you run, so `@latest` brings the newest. `hds-upgrade` is the same command as a bin of its own.
26
+ - dc49033: The package now ships its upgrade record (hds#451). `UPGRADING.md` lists what each release from 0.17.0 asks of you, newest first, in four lists: Fixed for you, Looks different, Coming next and Do by hand. `MIGRATIONS.md` and `CHANGELOG.md` ship too, so the links to them work inside `node_modules`, and so do `upgrade/index.json` (every release, its breaking count and the 0.16.0 floor), the per-release ledgers in `upgrade/releases/` and their schema, `upgrade/schema.json`. From this release on, each CHANGELOG section opens with an Upgrade block: the command to run, then what is left to do by hand. Divider's `strong` and InlineCode's `compact` props, deprecated before 0.16.0, join the record (removed in 1.0.0). Nothing in the code changes.
27
+
28
+ ## 0.21.0
29
+
30
+ ### Minor Changes
31
+
32
+ - def444a: Agent tooling ships in the package (hds#515). Nothing changes on the main entry or any existing subpath.
33
+ - `AGENTS.md` at the package root: one answer per screen need (a row of numbers is `MetricTiles`, a destructive confirm is `AlertDialog`, a saved message is a toast through `useToast`, a form is `Form` with `FormActions`, and so on for 24 needs), the import path of each core component, and the rules. Generated from `mcp/guide.mjs`; `pnpm check:agents-md` keeps it in step.
34
+ - `hds-mcp` bin: an MCP server over stdio (run the `hds-mcp` bin in an app that has the package installed, or `npx -p @hirobius/design-system@^0.21 hds-mcp`; no dependencies, no network) with four tools, `list_core`, `search_components`, `get_component` and `search_tokens`. Each answer is compact JSON under 2 KB, read from the manifest and component API data the package already ships.
35
+ - `./eslint-plugin` subpath: the consumer ESLint plugin (`import hds from '@hirobius/design-system/eslint-plugin'`, then `...hds.configs.recommended`).
36
+ - **Breaking for anyone already using the plugin** (`@hirobius/eslint-plugin-hds` from git, now 0.2.0): the new rule `hds/no-raw-controls` is `error` in `recommended`. It fails raw `<button>`, `<input>`, `<select>`, `<textarea>` and `<form>`; replace them with `Button`, `Input`, `Select`, `Textarea` and `Form` (from `/patterns`), or turn the rule off in your config: `rules: { 'hds/no-raw-controls': 'off' }`.
37
+ - Usage contracts (`usage` in `component-api.json` and the manifest, shown in `llms.txt`): `AlertDialog` gains one (when to use it, when to use `Dialog` instead), and `Dialog` points to `AlertDialog` for a destructive confirm. `Stat` now reads as an inline figure inside prose or a dense list and, like `StatusTile` and `Card.Metric`, points to `MetricTiles` for a row of headline numbers. No component changes behaviour.
38
+ - `llms.txt` opens with a "Start here" section naming AGENTS.md, the MCP server and the lint plugin, then the same "Pick by need" list as AGENTS.md, rendered from the same source.
39
+
40
+ - 57ef003: Component defects from the 2026-10-07 bug bash (hds#522).
41
+
42
+ Behaviour removed or changed, so this is a minor (0.x): `ErrorPattern` no longer fills 100vh unless `fullPage` is passed; `Grid.Item` no longer sets `height: 100%`; `Grid`'s default `align` is now `start` (it was `stretch`), so Cards, Surfaces and StatusTiles in a Grid hug instead of filling the row (pass `align="stretch"` where a row needs equal heights); `Table` is now flush by default (`flush={false}` restores the padding); Surface, Card and StatusTile lost their `h-full`. Alert, Toggle and labelled dot Badge/StatusDot roles also changed (see A11y).
43
+
44
+ Broken: Progress's neutral fill had the same colour as its track (`bg-accent` and `bg-muted` both map to near-white), so it was invisible; the fill is now the accent surface and `sm` is 6px. SegmentedControl `md` segments were `w-full` in a `w-fit` rail, so one segment filled the rail and the rest scrolled away; they now hug, and the rail is 40px like Button and Input (`sm` 32px). Slider's track was a padded Surface (48px slab, 0px fill, over the label); it is now an 8px bar with a visible fill. Checkbox, Radio and Toggle had two tab stops because motion's `whileTap` put `tabindex=0` on the label; the label is now `tabindex=-1`. Skeleton `rectangular` defaults to 5rem high and its fill is `surface-sunken`, so it shows on a Card. Disclosure `variant="card"` no longer clips its content or leaves empty space when closed. CodeBlock's `<pre>` is padded 16px, not the 80px section stack.
45
+
46
+ Layout: Input, Textarea, Select, Combobox and Slider cap at 40rem (override with `--hds-form-control-max-width`, or `max-w-none` on the control's `className`). Select matches Input (40px, 44px at 390; same fill, 14px label, up-down chevron), and the built-in labels of Input, Textarea and Select are the FormField label (14px). Card, Surface, StatusTile and Grid.Item no longer stretch to the tallest sibling: Grid takes `align` (default `start`, `stretch` to opt in; MetricTiles stretches). Where a row needs equal heights, pass `align="stretch"` to the Grid (a dashboard Grid of Cards needs it). Table's scroll region is flush by default, so cells are not inset twice (`flush={false}` restores it). AssetImg's no-`src` fallback and ErrorPattern fit their container; ErrorPattern takes `fullPage` for the old 100vh behaviour, which is no longer the default. Checkbox, Toggle and Radio rows share one inset and height. Stat aligns to the start. MetadataList horizontal sizes its term column to its content. A vertical Divider stretches across a row without relying on a parent height.
47
+
48
+ A11y: Combobox returns focus to its trigger after Escape or a pick (hds#311). Alert is `role="status"` for info and success and `role="alert"` for danger and warning. Toggle is `role="switch"`. A labelled dot Badge and StatusDot are `role="img"`, not live regions. A pressed toggle Button fills with the accent surface. On touch, Checkbox, Radio and Toggle rows, the Pagination page buttons and the CodeBlock copy button reach 44px. Dialog and AlertDialog keep a 16px margin from the viewport edge at 390. `<FormActions primary>` makes a bare Button primary. Badge warns once in dev when `label` is used without `dot`.
49
+
50
+ Not in this patch (contract wave): Card.Header/Body/Footer double padding, the dark raised-surface contrast step, MetadataList inset across variants.
51
+
52
+ - b9694ea: The ratified core set is published where consumers and agents read it (hds#374). The 43 core components (the hds#254 disposition table as updated by hds#393 and hds#394) carry `core: true` on their spec in `public/hds-manifest.json` (the `./manifest` subpath), in `component-api.json` and in the agent manifest projection; `core` is a new optional boolean in `manifest/schema.json`, independent of `tier` (five core components are `tier: pattern`, and not every `tier: primitive` component is core), and non-core specs omit it. `llms.txt` gains a "Core set" section listing the 43 by name and category, and its "Which one when" lines mark them `[core]` (`Button: [core] Trigger an action…`; the name still comes first). The `hds-consumer` agent skill gains a "Core set" section before its allow-list, and the README a generated "What belongs in the system" section naming the core set by category and the 12 modules on `@hirobius/design-system/patterns`.
53
+ - 14eb027: Brand fonts are now an opt-in `fonts.css` instead of being base64-embedded in `tokens.css` and `styles.css` (hds#479). Consumers that want the HDS fonts must add one import:
54
+
55
+ ```ts
56
+ import '@hirobius/design-system/fonts.css'; // Satoshi 400/500/700 + Geist Mono 400
57
+ ```
58
+
59
+ Without it, text falls back to the family stack in `--hds-font-family` and `--hds-font-family-mono` (`"Satoshi", system-ui, …`), so a consumer that brings its own fonts no longer downloads ours. `fonts.css` ships with the four woff2 files in `dist/fonts/`; its URLs are relative (`./fonts/satoshi-400.woff2`), so Vite, Next and webpack resolve and hash them, and every face keeps `font-display: swap`.
60
+
61
+ Size: `tokens.css` goes from 152.8 kB to 20.3 kB gzipped (304 kB to 130 kB raw), and `styles.css` from 152.2 kB to 19.6 kB. The four woff2 files plus `fonts.css` are 132 kB raw, read only when a face is used.
62
+
63
+ A consumer app must add `import '@hirobius/design-system/fonts.css'` next to its `tokens.css` or `styles.css` import when it upgrades, or its UI renders in the system font.
64
+
65
+ - 5d7c98c: **`StatusDot` and `StatusDotProps` are removed (hds#465).** Both were deprecated in 0.20.0 with `@removeIn 0.21.0`. Replace `<StatusDot tone size label>` with `<Badge dot tone size label>`: `tone`, `size` and `label` map one to one, and so does the dot. Badge takes no `style`, so there is no codemod: move any `style` to a wrapper element or a `className`, then swap the component (MIGRATIONS.md, "0.21.0 removals"). From 0.22.0, `npx @hirobius/design-system@latest upgrade` finds every use by import and by JSX tag. ADR-014 now allows a removal to ship with a manual step like this one.
66
+ - 14eb027: Type ramp cut to 5 roles plus mono; old composite names are deprecated aliases; headings render smaller (hds#483, #485, #486, #487).
67
+
68
+ The ramp is now `display` 48/1.05 700, `title` 24/1.25 700, `body` 16/1.6 400, `ui` 14/1.5 500, `caption` 12/1.5 500 and `mono` 13/1.5 400. `semantic.typography.title` is new, and `display`, `body`, `ui`, `caption` and `mono` change value: display goes 60 to 48px, body 17 to 16px with 1.6 leading, caption gets 1.5 leading, mono 14 to 13px.
69
+
70
+ Every old name still resolves, so no import breaks, and each now holds its role's value: `h1`, `h2`, `h3` (48, 30 and 20px) and `typeStyles.heading1` to `heading3` render as `title` (24px, so headings render smaller); `eyebrow`, `badge` and `micro` render as `caption` in sentence case (the uppercase and wide tracking are gone); `technical`, `monoXs` and `monoSm` render as `mono`; `small`, `label` and `bodySmall` render as `ui`. `<Text variant>` takes the six role names; `heading1` to `heading3`, `technical`, `eyebrow`, `badge` and the four doc variants stay accepted, tagged `@deprecated` with `@removeIn 1.0.0`, as do the matching `hds.typeStyles` keys. The deprecated `Text` variants keep their old default elements (`heading1` is still an `h1`); the new `title` defaults to an `h2`, so pass `as` where another level matters.
71
+
72
+ The fluid `clamp()` size overrides on display and the headings are gone; the ramp is static. Title is 700 because Satoshi has no 600 face. Satoshi still ships 500 and 700 faces only, so body (400) draws on the 500 face until a Satoshi Regular face is added.
73
+
74
+ New: the `.hds-type-<role>` classes in `theme.css`, and `scripts/check-type-ramp.mjs`, a pre-commit gate that fails a raw size, weight or line height in `src/` and any deprecated composite. Figma text styles need a Sync after this ships (hds#489).
75
+
76
+ ### Patch Changes
77
+
78
+ - dd00c68: `Button` and `Card` no longer use the Tailwind important modifier (`!`) for `tone` (hds#372, ADR-030). A status tone now wins over `variant` the way every other class override does, by tailwind-merge class-group replacement: Button's tone strings also set the hover fill and hover border, so no variant hover colour leaks through, and Card's tone sets `border` plus the feedback colour, replacing `accent`'s `border-2`. No visual change in any variant, tone, state (`iconOnly`, a toggle on or off, a selectable or selected Card) or theme; a toned toggle keeps the tone colours when on, as before. One behaviour change: a consumer `className` can now override a tone's colours the same way it already overrides a variant's (before, `!` made tone colours unoverridable), which is the documented escape-hatch model.
79
+
80
+ ## 0.20.0
81
+
82
+ ### Minor Changes
83
+
84
+ - c506c3b: Button (and so an icon-only Button; IconButton is removed in this release, hds#394) expresses the pressed state through a token (hds#322). `semantic.color.state.pressed.overlay` is a new opaque colour (black in light, white in dark) exposed as `role.pressed-overlay` and the Tailwind colour `pressed-overlay`; Button applies it as `active:inset-shadow-[0_0_0_9999px] active:inset-shadow-pressed-overlay/5`, an inset box-shadow wash that composes with the focus ring and any consumer `shadow-*`, in place of `active:brightness-95 dark:active:brightness-110`. The visible change: a press now tints the control's fill only (label and icons are no longer run through the filter), reads 5% white rather than a 110% brightness bump in dark mode, and no longer transitions; the tone variants' hover brightness is unchanged. The variable ships in the Figma model so the staging Pressed variants can bind their overlay fill to it instead of a hard-coded 5% black.
85
+ - 9f9d143: Box `sx` spacing props take the t-shirt scale by name (hds#206): `p: 'md'`, `gap: 'sm'`, also inside a responsive map, resolve to `var(--semantic-space-scale-*)`. Before, those names passed through as invalid CSS and rendered no spacing. Nothing else Box or Stack renders changes: every other `sx` spacing value and every Stack `gap` value computes the same pixels as before, in every tenant, density and breakpoint (a Chromium test locks this against the previous commit). Box's deprecated `'tight'`, `'normal'`, `'inset'` and `'spacious'` still read the fixed `--semantic-space-layout-*` vars, and Stack's same four names still read the scale steps, which compact density tightens; both are removed in 1.0. In a development build, each of Box `sx`'s four logs one `[HDS deprecation]` console warning naming the step to use (`'sm'` to `'xl'`); Stack's do not warn yet. Stack's `gap` and Box's `sx` now share one resolver (`resolveSpacingValue` in `box-sx.ts`), each with its own vocabulary. No token is removed; MIGRATIONS.md lists the old names, their replacements, and what the swap does under `data-density="compact"`. Inside HDS, `check-spacing-vocabulary` now blocks raw integers on `sx` spacing props at pre-commit.
86
+
87
+ Token descriptions only, no value changes: 15 `$description`s in `hirobius.tokens.json` (8 of them the hds#206 spacing ones) are trimmed to the 20-word limit that `check-token-descriptions --no-missing` enforces in `pnpm check`.
88
+
89
+ `hds.density.*` is deprecated (`@deprecated`, `@removeIn 1.0.0`). It was a second t-shirt vocabulary whose names mean different pixels (`density.sm` is 8px, `scale.sm` is 16px). Use `hds.semantic.space.scale.*`, one name down: `density.sm|md|lg|xl|xl2` compute the same pixels as `scale.xs|sm|md|lg|xl` at both densities. `density.xs`, `xl3` and `xl4` have no scale step. It still works; MIGRATIONS.md has the table.
90
+
91
+ - 8e53a8a: `Tooltip` is public in the manifest again (hds#390). Component discovery read a JSDoc tag anywhere in a comment, so the words "an @internal image-expand pill" in Tooltip's description marked it `hidden` with category `Internal`. Tags now count only at the start of a JSDoc line. `componentSpecs.Tooltip` in `hds-manifest.json` is `hidden: false`, category `Overlays`, and the consumer skill lists it under Overlays. No other component changed.
92
+
93
+ Deprecated components carry their deprecation in the manifest (hds#390). A spec whose component JSDoc has `@deprecated` now gets `deprecated` (the notice), `removeIn` (from `@removeIn`) and `useInstead` (from `@useInstead` in the same block), declared as optional string properties in `manifest/schema.json` and typed on `ManifestComponentSpec`. The five specs that carried them (CinematicLink, ComponentInstanceMatrix, FoundationSwatch, Sketch and Token) are removed in this same release (hds#389 R1), so the shipped manifest has no deprecated spec; the consumer skill (`skills/hds-consumer/SKILL.md`) and the `llms.txt` "Which one when" section and props digest skip any spec that does.
94
+
95
+ Manifest `props` and `propConstraints` now follow the code (hds#390). `pnpm manifest:generate` used to fill them from `component-api.json` only when a spec had none, so a prop kept the type it had when first recorded. They are now rebuilt from `component-api.json` on every run, including Button, Card, Input and Dialog, whose props `pnpm tokens` used to overwrite with a hand-kept list: 56 specs change, 132 props the code has are added and 14 it no longer has are dropped. For example `Stack.props.wrap` is `FlexWrap` (it said `boolean`), `SegmentedControl.size` is `sm | md` (it said `default | compact`), and `Stat.tone` lists `neutral`, not `default`. Literal unions stay enums. A prop typed by a named alias (`InputSize`, `NavVariant`, `GapOption`, `PaddingOption`) now carries the alias name instead of a hand-kept value list, because `component-api.json` does not expand aliases.
96
+
97
+ - ebe0a41: Every root deprecation now says when it is removed (hds#390, hds#389 D6). The 27 deprecated root exports carry `@removeIn 1.0.0`: the 21 pattern-tier names that should be imported from `@hirobius/design-system/patterns` (ActivityFeed, AppShell, AssetImg, Calendar, Carousel, CodeBlock, CommandPalette, DocLinkCard, ErrorPattern, FileInput, Form, Lightbox, NavItem, OverflowList, Page, Reveal, SideNav, Stepper, Toolbar, TopNav, TreeList) and the six `Hds*` aliases (HdsCheckbox, HdsRadio, HdsSelect, HdsSlider, HdsToggle, HdsTooltip). So do the three deprecated `hds.semantic.space` tokens on the `/tokens` entry: `component.padding`, `component.gap` and `layout.gutter`. The 27 root names are then removed in this same release (hds#389 R1, see the BREAKING entry and MIGRATIONS.md); the three tokens still work and keep their 1.0.0 target. Imports from `/patterns` are not deprecated.
98
+ - ecb0a04: **BREAKING (0.x minor): already-deprecated public API is removed (hds#389 R1).** Before 1.0, a deprecated name may now be removed in a minor; ADR-014 and MIGRATIONS.md's "The cycle" record the rule, and 1.0 stays parked (#396). Every removed name, its replacement and its codemod is listed in MIGRATIONS.md under "0.20.0 removals". A consumer on a caret range below 0.20 (such as `^0.16`) is not affected until it upgrades; run the codemods first.
99
+ - **The 21 root re-exports of `/patterns` components are gone** (hds#254): ActivityFeed, AppShell, AssetImg, Calendar, Carousel, CodeBlock, CommandPalette, DocLinkCard, ErrorPattern, FileInput, Form, Lightbox, NavItem, OverflowList, Page, Reveal, SideNav, Stepper, Toolbar, TopNav and TreeList, plus everything else their modules exported from the root (props types, parts such as `FormField` and `Step`, `useFieldWiring`, `*Variants`): 79 names in all. Import them from `@hirobius/design-system/patterns`, which exports them unchanged, except the 15 modules this release then removes outright (hds#394, see the wave 4a entry). `npx -p @hirobius/design-system@^0.20.0 hds-patterns-subpath --root .` moves the imports; its name list is now whatever `/patterns` exports and the root does not, so it moves the props types and parts too.
100
+ - **The six `Hds*` aliases are gone** (hds#315): HdsCheckbox, HdsRadio, HdsSelect, HdsSlider, HdsToggle and HdsTooltip. Use `Checkbox`, `Radio`, `Select`, `Slider`, `Toggle` and `Tooltip`. The new `hds-prefix` codemod (`npx -p @hirobius/design-system@^0.20.0 hds-prefix --root .`, `codemods/hds-prefix.mjs`, with `--check` and `--dry-run`) rewrites each root import to the bare name under the old local name (`Checkbox as HdsCheckbox`) and changes nothing else in the file; renaming the binding afterwards is optional.
101
+ - **Five docs/lab components are deleted** (hds#232, deprecated in 0.16): CinematicLink, ComponentInstanceMatrix, FoundationSwatch, Sketch and Token, with `CinematicLinkProps`, `FoundationSwatchProps`, `SketchProps`, `TokenProps`, `tokenLabelVariants`, `tokenNodeInlineVariants` and `tokenShellVariants` (12 names). They have no replacement and no consumer imports them. Their stories, manifest specs, Figma disposition rows, Code Connect exemption, staging-promotion entries and docs-site registry rows go with them, and nothing under `src/` imports `component-api.json` any more (it still ships as a package file).
102
+
103
+ - b2e27a2: **BREAKING (0.x minor): 32 components with no survivor are removed, and the root `*Variants` helpers are private (hds#394 wave 4a, hds#389 decision update).** No consumer imports any of them (the one product app that imports components has 0 import sites, and it pins `^0.16`, which never resolves to 0.20). Every removed name has a row in MIGRATIONS.md under "0.20.0 removals", with its replacement where one exists.
104
+ - **Removed from the root:** `CaseStudyLayout`, `HdsSystemDocLayout`, `HdsDocsShell`, `ErrorBoundary`, `HistoryCard`, `NavGroup`, `Tokenizer`, `StepperField`, `HeadingStack`, `TextLockup`, `DateInput`, `DateRangeInput`, `DateTimeInput`, `ContextMenu`, `HoverCard` and `ButtonGroup`, with their props types and helpers. `HeadingStack` and `TextLockup` become `Stack` + `Text` (recipe in docs/rules/REACT_COMPONENTS.md); `StepperField` becomes `Input type="number"`.
105
+ - **Removed from `/patterns`:** `ActivityFeed`, `AppShell`, `Calendar`, `Carousel`, `CommandPalette`, `DocLinkCard`, `FileInput`, `Lightbox`, `NavItem`, `OverflowList`, `SideNav`, `StackedCardRail`, `Stepper`, `Toolbar`, `TopNav` and `TreeList`, with their props types, parts and `*Variants`.
106
+ - **The date pickers are gone** (ADR-034 supersedes ADR-020): `Calendar` from `/patterns`, and `DateInput`, `DateRangeInput` and `DateTimeInput` from the root. Use `Input type="date"`, or `type="datetime-local"` for a date and time; a range is two date inputs.
107
+ - **The root exports no `*Variants` cva helper** (38 names such as `buttonVariants`, `badgeVariants`, `cardVariants`). Each still styles its own component; style through the component's props. `/patterns` keeps its exports.
108
+ - **`hds-patterns-subpath --check` reports a named import or re-export of any removed name** (from the root or `/patterns`) as "removed in 0.20.0, no replacement" for a manual edit, instead of passing or moving it to a subpath that no longer has it. The list ships as `codemods/removed-0.20.json`. A removed name read through a namespace import or a dynamic `import()` is left to the type checker.
109
+ - **`styles.css` and `tokens.css` drop the rules only the removed components used:** `.hds-doc-link-card` (DocLinkCard), `.hds-stepper-input` (StepperField), `.hds-doc-section-header` and `.hds-doc-section-copy-icon` (TextLockup), and `.hds-page-enter` with its keyframes (HdsSystemDocLayout). A test now fails when `src/styles/theme.css` styles an `hds-*` class that no component or story names.
110
+ - `ButtonGroup`, `ContextMenu` and `HoverCard` leave the curated core set (`scripts/lib/core-components.mjs`), and `Menu`, `Popover`, `Tooltip` and `HdsRouterProvider` join it (hds#393), which makes 43 components.
111
+
112
+ - 3e819c4: `Grid` can now render what `TileGrid` renders, the first survivor API of the prune (hds#393 step 1). `layout="auto-fill"` wraps items with `repeat(auto-fill, minmax(min(<minItemWidth>, 100%), 1fr))`, and the new `minItemWidth` prop (a CSS length, default `280px`) sets where `auto-fill` and `auto-fit` wrap; `min(…, 100%)` keeps a container narrower than one item to a single full-width column. `layout="auto-fit"` without `minItemWidth` renders the `repeat(auto-fit, minmax(280px, 1fr))` track it always has. `gap` takes a new step, `medium`: 12px (`semantic.space.component.medium`, the value Stack's `medium` reads), `TileGrid`'s default gap, fixed under compact density as `TileGrid`'s is. `<Grid layout="auto-fill" minItemWidth="220px" gap="medium">` computes the same tracks and gap as `<TileGrid minTileWidth="220px">` under every tenant and density. `GridProps` now extends `React.HTMLAttributes<HTMLDivElement>`, so `role`, `aria-*`, `id` and other attributes reach the root element; Grid's own `data-hds-*` attributes still win. Every existing Grid option renders and computes what it did before. `medium` joins the shared closed layout-gap vocabulary, so a JavaScript caller passing `'medium'` to a prop whose type rejects it (Sidebar or Switcher `gap`; Cluster, Cover, Bleed and Center are removed in this release) now gets 12px where it got no spacing; Card and Stack `gap` are unchanged.
113
+ - 01183dc: `Input` takes the native date and time types and two in-flow slots (hds#393). `type` now also accepts `date`, `time` and `datetime-local` (not `file`), so `<Input type="time">` covers what `TimeInput` does; like `number`, these types show no clear button, because the platform picker owns the value UI. New `prefix` and `suffix` props render text or any node inside the field frame, beside the value, so `<Input prefix="https://">` and `<Input suffix="kg">` cover what `InputGroup`'s `leading` and `trailing` do. With either slot set, the border, surface and focus ring move from the `<input>` to a frame around the slots and the input; without them the markup is unchanged. The slots are not part of the accessible name or `aria-describedby`, so the label carries anything essential. `leadingVisual`, `trailingVisual` and every other prop work as before. `prefix` was previously typed as the RDFa `prefix` attribute (a string) and passed to the `<input>`; it is now a `ReactNode` slot, so every string still compiles.
114
+ - cc97ac2: `Button`, `Badge`, `Progress` and `Card` gain the props that `ToggleButton`, `IconButton`, `StatusDot`, `CircularProgress` and `SelectableCard` fold into (hds#393). All additive: nothing is removed or renamed. Existing props render the same markup as before, with one addition: the linear Progress bar now also carries `data-variant="linear"` and `data-tone` (`"neutral"` by default).
115
+ - **Button.** `iconOnly` now uses `label` as the button's `aria-label` (an explicit `aria-label` still wins), so `<Button iconOnly label="Close" iconLeft={<X />} />` is named "Close". In development, an icon-only Button with no `label`, `aria-label`, `aria-labelledby` or `title` logs one warning. New `pressed` (controlled), `defaultPressed` (uncontrolled) and `onPressedChange` make a Button a toggle: it sets `aria-pressed` and `data-pressed="true|false"` (`data-state` stays `loading`'s) and fills with `role.accent` while pressed. A Button with none of the three is not a toggle and gets neither attribute. An `onClick` that calls `preventDefault()` stops the toggle.
116
+ - **Badge.** New `dot` renders a solid status dot with no text, sized by the new `size` (`sm` | `md` | `lg`, default `md`). `tone` still picks the color (a dot is not a new tone). With the new `label` the dot is `role="status"` named by it; without one it is `aria-hidden`.
117
+ - **Progress.** New `variant="circular"` draws a ring (sm 16px, md 24px, lg 32px). It is indeterminate with no `value` and spins unless reduced motion is on. New `max` (default 100) sets the scale for both shapes: `aria-valuemax` is `max` and `aria-valuenow` is the clamped value. On the default 0-100 scale, `aria-valuenow` is still rounded to a whole number. New `tone` (`neutral` | `danger` | `success` | `warning` | `info`) colors the fill; `neutral` keeps the bar's current fill and gives the ring `CircularProgress`'s. The ring's root is a `span`, as `CircularProgress`'s was, so it can sit inside a paragraph or label; the bar stays a `div`.
118
+ - **Card.** New `selectable`, `selected` and `onSelectedChange` make the whole card one checkbox-like option: `role="checkbox"`, `aria-checked`, focusable, toggled by click or Space. A click on a control inside the card, or Space typed into one, is left to that control. Enter does not toggle (the ARIA checkbox pattern uses Space only); `SelectableCard` was a native button, so Enter toggled it, and migrating code that relies on Enter needs to change. It is controlled, like the other selection controls: the card reports the next state and the page sets `selected`. Selection is a 2px ring inside the card's edge, apart from the focus outline outside it, so a focused card, a selected card and a focused selected card each look different; it composes with any `tone` or `variant`.
119
+
120
+ - 297caf5: `Combobox` takes `multiple` (hds#393). `<Combobox multiple value={values} onChange={setValues} options={options} />` picks any number of values: `value` is a `string[]` and `onChange` receives the next array. Picking an option, by click or Enter, adds or removes it and keeps the list open. The listbox is `aria-multiselectable` and every option carries `aria-selected`. The trigger shows a count (`2 selected`), and each value appears below it as a chip button named `Remove <label>`. Removing a chip moves focus to the next chip, or to the trigger when none are left. The props are a union of two arms: `ComboboxProps`, the single-select arm, is unchanged apart from an optional `multiple?: false`; `ComboboxMultipleProps` is the new arm; and `ComboboxAnyProps` names either. It is the replacement for `MultiSelector`: `<MultiSelector value onChange options />` becomes `<Combobox multiple value onChange options />`.
121
+ - bce2b48: Popover, Tooltip and HdsRouterProvider now say when to use them (hds#393).
122
+ - `Popover` and `Tooltip` carry the usage contract in `public/hds-manifest.json` (`usage.when`, `usage.whenNot`, `usage.useInstead`, `keyboard`) and appear in the "Which one when" section of `public/llms.txt`. Their `keyboard` entries list only what the keyboard contract test drives: Popover opens on Enter or Space, closes on Escape and loops Tab inside; Tooltip opens on focus, closes on Escape and closes when Tab moves on.
123
+ - `Tooltip` casts `shadow-floating` (`semantic.elevation.floating`, the popover and dropdown shadow) instead of `shadow-overlay`, which is for dialogs and sheets. Its colours, radius and caption text are unchanged.
124
+ - `Tooltip` links Figma node 93:15 (`figmaUrl` in the manifest, `docs/DESIGN_LINKS.md`, and the Design tab of its Storybook stories), so every published Figma component now maps to code (51 of 51).
125
+ - `HdsRouterProvider` has a manifest spec (category Theming, tier primitive, doc-exempt), so the manifest lists 147 components instead of 146. Nothing about its API changes.
126
+ - `DESIGN.md` "Which one, when" names the prop, not a separate component, for three needs: a pickable card is a `Card` with `selectable`, a wrapping row of equal non-metric tiles is a `Grid` with `layout="auto-fill"` and `minItemWidth`, and a bare state marker is a `Badge` with `dot` and a `label`. The table keeps its eight rows.
127
+
128
+ - 69e0049: **BREAKING (0.x minor): 13 components that fold into a survivor are removed from the root, with their props types (hds#394 wave 4b, hds#389 decision update).** Each survivor shipped with hds#393, and MIGRATIONS.md ("0.20.0 removals", "Components folded into a survivor") maps every prop. The one product app that imports components imports none of the 13.
129
+ - `IconButton` becomes `Button iconOnly` with a `label` and an `Icon` in `iconLeft`; `ToggleButton` becomes `Button pressed` (`variant="ghost"` is `variant="tertiary"`).
130
+ - `InputGroup` becomes `Input prefix` / `suffix`; `TimeInput` becomes `Input type="time"`.
131
+ - `CircularProgress` becomes `Progress variant="circular"`; `SelectableCard` becomes `Card selectable`; `MultiSelector` becomes `Combobox multiple` (`MultiSelectorOption` is `ComboboxOption`).
132
+ - `Cluster` becomes `Stack direction="row" wrap="wrap" align="center"`; `Center` becomes `Container` with a `Box` inside for the gutter.
133
+ - `Cover`, `Frame`, `Bleed` and `AspectRatio` become a `Box` with `style` (`aspectRatio`, `marginInline`, `marginBlock: 'auto'` and so on). Use `style`, not `sx`: `sx` applies on the client only.
134
+ - `StatusDot` stays: a consumer app passes it `style`, which Badge `dot` does not take.
135
+ - `hds-patterns-subpath --check` reports a named import of any of the 13 for a manual edit and names its survivor ("removed in 0.20.0, use Button iconOnly …"), from the new `replaced` map in `codemods/removed-0.20.json`.
136
+ - `@radix-ui/react-aspect-ratio` and `@radix-ui/react-toggle` leave `dependencies`: only the removed `AspectRatio` and `ToggleButton` imported them (`@radix-ui/react-toggle-group` stays and brings its own copy of the toggle primitive).
137
+
138
+ - aa07aab: **BREAKING (0.x minor): NotFoundPattern and TileGrid are removed from the root, and StatusTile moves to `/patterns` (hds#395 B5, hds#389 decision update).** These are the prune-set names a consumer app imports, so each comes with a codemod, and MIGRATIONS.md ("Components a consumer renders, each with a codemod") has every row.
139
+ - `NotFoundPattern` becomes `<ErrorPattern displayText="404" message="Page not found" />` from `@hirobius/design-system/patterns`, which is what it rendered. `npx -p @hirobius/design-system@^0.20.0 hds-not-found-pattern --root .` rewrites it.
140
+ - `TileGrid` becomes `<Grid layout="auto-fill" minItemWidth="…" gap="medium">`: `minTileWidth` is `minItemWidth` (260px when unset, since Grid defaults to 280px, and `?? '260px'` after a width expression that can be undefined) and `gap="sm"` is Grid's fixed 12px `medium`. The tracks and gap render the same under every tenant and density. `npx -p @hirobius/design-system@^0.20.0 hds-tile-grid --root .` rewrites it; `gap="xs"` or `"md"`, spreads, self-closing tags and `TileGridProps` are listed for a manual edit.
141
+ - `StatusTile`, `StatusTileProps` and `StatusTileTone` are exported from `@hirobius/design-system/patterns` only (hds#389 D5). `hds-patterns-subpath` moves the import.
142
+ - `hds-patterns-subpath --check` reports a NotFoundPattern or TileGrid import with its survivor until its codemod has run (`codemods/removed-0.20.json`).
143
+ - A consumer app (read-only dry run): `hds-not-found-pattern` 1 file, 1 site; `hds-tile-grid` 2 files, 7 sites; `hds-patterns-subpath` 12 sites in 11 files.
144
+
145
+ - aa07aab: **`StatusDot` is deprecated for `<Badge dot>`, and `StatusDotProps` for `BadgeProps`; both are removed in 0.21.0 (hds#395).** `tone`, `size` and `label` map one to one, and so does the dot; a development build logs one `[HDS deprecation]` warning. It stays in 0.20.0 because a consumer app passes it a `style`, which Badge (className-only) does not take, so no codemod can rewrite that site: move the `style` to a wrapper or a `className` first (MIGRATIONS.md, "StatusDot is deprecated").
146
+
147
+ ### Patch Changes
148
+
149
+ - 31a9c67: Six more compounds stop writing their parts onto a component at module scope (hds#365): `Menu`, `Popover` and `Tooltip` (and `ContextMenu`, `Toolbar` and `HoverCard`, which this release then removes, hds#394) are now one pure `Object.assign` around a wrapper of their own, the shape `AlertDialog`, `Dialog` and `Card` took in 0.19.1. Every part keeps its name, identity and declared type, and each root now carries a `displayName`. A Button-only consumer bundled with webpack or esbuild no longer receives the dropdown-menu, context-menu, popover, hover-card, tooltip and toolbar stacks: the esbuild probe drops from 33 `@radix-ui` packages (85.6 kB gzip) to 2 (42.1 kB); the rollup number is unchanged at 30.8 kB. The pre-commit gate `scripts/check-pure-annotations.mjs` now also flags an un-annotated top-level `Object.assign(<Component>, …)` and any top-level `X.Part = …` or `X.displayName = …` write (`--fix` annotates the former and reports the latter), and the esbuild Button-only probe fails on any `@radix-ui/*` package outside what `button.tsx` itself pulls in (`react-slot`, `react-compose-refs`), printing the packages that reached it.
150
+ - 614cc00: `docs/CONSUMING.md` §11 and the `hds-consumer` skill now give the ESLint plugin install line against the repository's current name, `pnpm add -D "@hirobius/eslint-plugin-hds@github:hirobius/hds#path:/scripts/eslint-plugin-hds"` (the repository's pre-rename name is only a mirror of it). The plugin is not on npm, so this git-path line is the only install route; the same fix repoints the plugin README, the `meta.docs.url` ESLint prints beside each HDS rule violation, and the README's pointer to PR #39 (hds#373). A front-door test now fails if any doc, script or skill names the pre-rename repo.
151
+ - a121851: Dialog's slots stay in the manifest whichever regen runs last (hds#379). `pnpm tokens` used to drop the `trigger` slot from `componentSpecs.Dialog.slots` in `hds-manifest.json`, because the token build replaced the slot list that `pnpm manifest:generate` had merged from the component's `@slot` tags. The token build no longer writes slots, so both commands now leave the manifest byte-identical. Dialog's JSDoc also names the overlay, header, title, description, footer and close slots, so all eight appear in `component-api.json`, not only trigger and surface.
152
+ - c148051: `Select` and `Combobox` name their open overlays, so a screen reader no longer enters a bare "listbox" or "dialog" (hds#398, hds#399). The open `Select` listbox is `aria-labelledby` its visible field label; with `showLabel={false}` it carries the `label` text as `aria-label`, and with an empty `label` the trigger's own name (the selected option). The `Combobox` popover (`role="dialog"`) takes the same name as the listbox inside it: the `aria-label` prop, or the `placeholder` when there is none, which clears axe's `aria-dialog-name` on the open popover. No prop changes meaning.
153
+ - a9ece9a: Deletes internal code that nothing imports (hds#391, under hds#133 option A). No public export changes: the API report still lists 401 symbols.
154
+ - **Source:** `src/docs-tooling/` (20 files), `animated-label.tsx`, the Möbius shader, curve and store (`mobius-distortion.ts`, `shaders/mobius.glsl.ts`, `mobius-constants.ts`, `stores/mobiusStore.ts`, `stores/mobiusCurve.ts`), `tokenTableUtils.ts`, `components/types.ts`, `context/ReactRouterBridge.tsx`, `data/hdsEditorial.tsx`, `data/tenants.ts`, `data/tokenAuditReportTypes.ts` and `hooks/useHdsManifest.ts`.
155
+ - **`hds-manifest.json`:** 24 `utilities` entries are removed: `AnimatedLabel`, `ApiReference`, `ComponentPreview`, `ControlsPanel`, `ControlsSection`, `DemoBlock`, `DocPageHeader`, `DocPageSpec`, `HdsLegacyTokenGovernancePanel`, `HdsMobileTopBar`, `HdsSidebarUtilityButton`, `LegacyTokenList`, `PageFooter`, `PreviewFrame`, `SketchButton`, `SketchCheckbox`, `SketchPanelToggle`, `SketchRange`, `SketchTextarea`, `ThemeToggle`, `TokenCollectionList`, `TokenDisplayToggle`, `TokenList` and `VariantStrip`. 13 utilities remain, and `componentSpecs` is unchanged at 146.
156
+ - **`./styles.css` and `./tokens.css`:** no HDS component used these selectors, so they are removed: `.hds-sketchbook-shell__stage-frame`, `.hds-sketchbook-canvas-shell`, `.hds-sketchbook-canvas-stage`, `.hds-sketchbook-canvas-fill`, `.hds-sketchbook-canvas-overlay`, `.hds-sketchbook-canvas-overlay__content`, `.hds-visuals-bento-grid`, `.hds-visuals-bento-item`, `.hds-visuals-bento-card`, `.hds-token-chip`, `.hds-nav-indicator`, `.portfolio-impact-card`, `.hds-soft-nav-card`, `.hds-mobius-acrylic`, `.hds-sidebar-utility-button`, `.hds-dropdown-item`, `.hds-dropdown-label`, `.hds-dropdown-indicator` and `.hds-desktop-nav-button`, with their state variants. The `:root` custom property `--hds-local-sketchbookCanvas-maxWidth-stage` is also removed. `.hds-mobius-acrylic:hover` was the only `!important` rule that `theme.css` wrote into `./styles.css`.
157
+ - **Tailwind utilities kept:** 16 utilities (`pt-1`, `pb-8`, `p-16`, `max-w-2xl`, `h-3.5`, `w-3.5`, `sm:text-4xl`, `sm:inline-block`, `group-hover:opacity-100`, `text-muted-foreground/70`, `transition-[transform,opacity]`, two `data-[…]:text-foreground` variants and three `[color:var(--semantic-color-feedback-*warning)]` arbitrary values) appeared only in the deleted files, so Tailwind would have stopped emitting them. An `@source inline(…)` list in `theme.css` keeps them in `./styles.css` and `./tokens.css` until 1.0, because consumers that use `./tokens.css` as their only compiled Tailwind still use them. Apart from the `hds-*` selectors above, the published selector set is unchanged.
158
+ - **devDependencies:** `three`, `@types/three`, `zustand` and `fuse.js` are removed. None of them was a runtime dependency.
159
+
160
+ The interaction-surface contract test (#127) now checks `.hds-bg-hover:hover`, which reads `--semantic-color-surface-hover`. It used to check the deleted `.hds-dropdown-item:hover`.
161
+
162
+ - 75288d2: Kept components stop rendering IconButton and Cluster inside (hds#392), so both can go without touching consumers of the kept ones: this release removes them (hds#394, MIGRATIONS.md).
163
+ - **Pagination and InlineCode:** Previous, Next and the copy button are now `Button iconOnly` with an `aria-label` and a 16px `Icon` (`size="small"`). The rendered markup is byte-identical: same element, classes, attributes and accessible names ("Previous page", "Next page", "Copy", "Copied").
164
+ - **PageHeader, FormActions, DataTableSection and DestructiveSection:** their wrapping rows are now `Stack direction="row" wrap="wrap"` with `align` and `justify` passed explicitly. Layout is unchanged (direction, wrap, gap, alignment and distribution), but the row elements inside these 4 patterns now carry `data-hds-component="Stack"` instead of `data-hds-component="Cluster"`, and their inline style adds `flex-direction: row`, the initial value. Update any selector or test that targets `[data-hds-component="Cluster"]` inside one of these patterns.
165
+ - **AssetImg's expand pill (internal ExpandTooltip):** the label now uses `content.onAccent` instead of fixed white. In dark mode `surface.accent` is a light neutral, so white text read at 1.09:1; it now reads 17.32:1 (light mode is unchanged at 18.88:1).
166
+
167
+ - cde9ffe: Combobox and Select accessibility fixes (hds#407, hds#408). Combobox options now sit in `<li role="none">`, so the open listbox owns its options directly: axe no longer reports `aria-required-children`, `aria-required-parent` and `listitem` on it, and a screen reader reads each option with its real position and set size ("position 2, set size 8", not "position 1, set size 1"). The no-results message now sits beside the listbox, not inside it, with the same spacing. Select and Combobox accept `id`, `aria-describedby` and `aria-invalid` and forward them to the trigger, so a `FormField` label, helper text and error now reach the control (`getByLabelText` finds it). A Select with `showLabel={false}` names its trigger by the label and the value ("Fruit: Apple"), as it already did with the label shown, not by the value alone. Combobox with `aria-label=""` names its popover and listbox by the placeholder, as it already did with no `aria-label`. New `Open` stories for both open the overlay in a play function, so the Storybook axe gate now scans the open state in light and dark.
168
+ - 08b766e: `pnpm check:full` passes its token-tier, motion, security-baseline and migration-log gates again (hds#403).
169
+ - Two new tokens: `primitive.size.120` (120px, `--primitive-size-120`) and `semantic.size.tile` (`--semantic-size-tile`, aliasing it). `MetricTile` takes its min-height from `--semantic-size-tile` instead of `calc(var(--primitive-size-96) + var(--primitive-size-24))`. Same 120px, so no tile changes size; the tile height is now one token a theme can override.
170
+ - `TOKEN_MIGRATION.md` now records the six primitive typography tokens removed on 2026-09-24 (`size.2xs`, `weight.light`, `weight.semibold`, `letterSpacing.tighter`, `.wide`, `.wider`), each with its nearest surviving step.
171
+ - `Table`'s sortable header button now has hover feedback: its label and sort glyph ease to the muted foreground (`hover:text-muted-foreground`) over the productive motion token (`--hds-motion-productive-duration`, 150ms, zeroed under `prefers-reduced-motion`). Before, hovering a sortable header changed nothing. The resting header is unchanged.
172
+
173
+ - de88964: One layout-gap vocabulary (hds#404). Grid, Sidebar and Switcher `gap`, and Card and Stack `gap` (and Cluster, Cover, Bleed and Center, which this release removes, hds#394) no longer keep a private copy of the `'tight'` | `'normal'` | `'inset'` | `'spacious'` map: each resolves through the shared spacing resolver against the one copy of those names in `box-sx.ts`. Nothing renders differently. Every value each prop takes, and its default, emits the same inline style and computes the same pixels as before, in every tenant, density and breakpoint (a Chromium test locks this against 8e53a8a). A value its type rejects but a JavaScript caller can still pass computes the same pixels too. The one inline-style difference: an inherited object key such as `'constructor'` no longer writes a function's source into the style attribute. That was invalid CSS, so nothing rendered from it before either. Card's `gap` still takes the `hds.space` keys and still passes any other value through; the others still set no style for a value outside the four names. Inside HDS, `check-layout-gap-vocabulary` fails a second copy of the map anywhere in `src/` at pre-commit.
174
+ - 01183dc: `Input` now pads each side of the field with its own class and never with a `px-*` shorthand (hds#393). The shorthand used to override the side-specific padding in two ways. A `leadingVisual` with nothing trailing lost its icon inset, so the placeholder ran under the icon. At the `md` and `lg` sizes, a filled field, a `trailingVisual` and a loading field kept only the resting right padding, so long text ran under the clear button, the trailing icon or the spinner. Both now keep their inset. The new `prefix` and `suffix` slots get the same gap whether one or both are set, and the gap no longer changes on the first keystroke. A `px-*` passed through `inputClassName` still overrides both sides.
175
+ - 57de36b: Drop five runtime dependencies that nothing in the package imports after the 0.20.0 removals (hds#429), so installing the package no longer downloads them: `@radix-ui/react-context-menu` (ContextMenu), `@radix-ui/react-hover-card` (HoverCard), `@radix-ui/react-toolbar` (Toolbar), and `date-fns` and `react-day-picker` (the date pickers, ADR-034). No export changes and no bundle grows. A new test, `tests/runtime-dependencies.test.ts`, fails when a package in `dependencies` has no import site in shipped `src/`, or when the `pnpm-lock.yaml` root importer lists a different set.
176
+ - 7c86aec: The docs-site sidebar model is deleted (hds#431). `src/app/data/hds-nav-data.ts`, `nav-model.ts` and `nav-model.json` held the navigation of the docs shell, which went with the docs SPA (#51) and `HdsDocsShell` (0.20.0). Nothing imported them and the package never exported them, so no import changes. In `hds-manifest.json`, `componentSpecs.Sidebar.consumers` no longer lists `src/app/data/nav-model.ts`.
177
+
178
+ `./manifest` (and `public/hds-manifest.json`) drops the top-level `health` field (hds#431). It was a snapshot dated 2026-06-18 that no script wrote; the token build forwarded it unchanged, so it still named removed components (NavGroup, StepperField, Sketch) and file paths that no longer exist. The token build no longer writes it, and nothing in the package read it. `SystemManifest` never declared it, so no type changes. If you read `manifest.health`, it is now `undefined`.
179
+
180
+ ## 0.19.1
181
+
182
+ ### Patch Changes
183
+
184
+ - 786e7e2: Clears the last nine serious axe violations in Storybook (hds#349); the axe allowlist is now empty. `StatusTile` and `StatusListItem` stories showed their trailing status as a hand-rolled label with literal white text on a feedback fill, which drops to 1.7-2.8:1 in dark because those fills flip to their 400 stops; they now use `Badge` (`tone="success" | "warning" | "danger"`), whose feedback text and background pair holds in both themes. `FoundationSwatch` stories pointed at `--semantic-color-bg-default`, `bg-subtle` and `bg-brand`, which are not tokens, so they fell back to a fixed light hex (1.03-1.09:1 against the theme-aware label in dark), and two swatches forced white text; they now use `surface.page`, `surface.raised`, `surface.accent`, `content.primary` and `content.secondary` with `content.inverse` and `content.onAccent` labels. The layout `Sketch` `WithControls` story gives its range input an accessible name (`aria-label="Speed"`). No token value, component or export changes; consumers who hand-roll a solid feedback fill with white text should use `Badge` or a dark-safe foreground.
185
+ - 2d8c1b6: Compound components no longer mutate the Radix Root export: `AlertDialog`, `Dialog` and `Card` are assembled with one pure `Object.assign` around a wrapper of their own (parts, names and types unchanged), so consumers on webpack/esbuild get smaller Button-only bundles. Every top-level `forwardRef` / `cva` / `createContext` / `withHdsPortal` call under `src/` now carries `/* @__PURE__ */`, enforced by a new pre-commit gate (`scripts/check-pure-annotations.mjs`, `--fix` available), and the Button-only budget probe also bundles with esbuild and fails if any `@radix-ui/react-dialog` or `@radix-ui/react-alert-dialog` code reaches a Button-only consumer.
186
+
187
+ ## 0.19.0
188
+
189
+ ### Minor Changes
190
+
191
+ - 731c669: Portalled overlays (Dialog, AlertDialog, Menu, ContextMenu, Popover, Select, HoverCard, Tooltip, Lightbox and the image-expand cursor pill) now mount inside the nearest `data-hds` scope instead of `document.body`, so `<div data-hds data-theme="dark">` themes them. Each Content part gains an optional `container` prop to override the target. The modal scrim uses the new theme-aware `semantic.color.surface.scrim` token (`role.scrim`, `bg-scrim/60`), which is black in dark mode instead of a white wash, and the dark theme now declares `color-scheme: dark`.
192
+ - a47459b: `data-density="compact"` now remaps the spacing variables components actually read (`--semantic-space-scale-*`, plus surface padding and region gutter), so compact visibly tightens the default brand, and `Table` with no `density` prop follows the ancestor `data-density` attribute (an explicit prop still fixes it). Existing compact consumers will see tighter spacing.
193
+ - 1cd40ca: Add three screen-level patterns to `@hirobius/design-system/patterns`: `PageHeader` (breadcrumb, title fixed at `heading2`, status and actions slots), `MetricTiles` with `MetricTile` (one fixed-height tile, `min(tiles, 4)` columns) and `FormActions` (primary right-most and last in DOM order, destructive on the far left). The manifest's `patternInventory` is now derived from a new `@screenPattern` JSDoc tag instead of a directory that no longer exists, `llms.txt` names the three patterns in "How To Lay Out A Screen", and `DESIGN.md` gains the page-title rule and a "Which one, when" table for `MetricTiles`, `Stat`, `Card.Metric` and `StatusTile`.
194
+ - dbba2c7: Add `DestructiveSection` (title, explanation, one danger button that opens an `AlertDialog`; `confirmLabel`, `confirmBody` and `onConfirm` required) and `DataTableSection` (heading, toolbar slot, `Table` with a consumer-supplied row-actions column, `EmptyState` for zero rows, horizontal scroll on narrow widths with no caller `minWidth`) to `@hirobius/design-system/patterns`, plus a Client detail reference screen story. Containers now share one radius that follows the tenant knob: `Surface`, `StatusTile` and the three `theme.css` card rules move from the fixed `--component-card-radius` (8 px) to `rounded-lg` (role radius + 4 px: 12 px by default, 4 px under `brutalist-demo`), matching `Card`; the shipped `./static.css` `.hds-card` rule follows suit (was a fixed 8 px). This is a visible change for those containers; `--component-card-radius` stays defined. `DESIGN.md` no longer contradicts itself on the container and action radius and gains `Card` / `Surface` / `DestructiveSection` / `DataTableSection` rows in "Which one, when". `Table` gains an optional `labelledBy` id that names the table and its scroll region when there is no caption; `DataTableSection` passes its heading.
195
+ - 91849c7: Component contract tags reach the generated files (hds#339). `@usage`, `@whenNot`, `@useInstead`, `@slot`, `@keyboard` and `@ai-rules` in a component's JSDoc are parsed into `usage`, `slots`, `keyboard` and `aiRules` on each spec in `public/hds-manifest.json` and `component-api.json`, and into a "Which one when" section of `llms.txt`; tag text no longer leaks into `description`. The agent manifest keeps `a11yRules`. In development, `<Button iconOnly>` with no `iconLeft` now warns once instead of rendering an empty square.
196
+ - df1fa12: Add a `@hirobius/design-system/icons` subpath: a curated set of 50 Lucide icons (canonical names such as `Ellipsis`, `Pencil`, `Trash2`) for `IconButton` and `Icon`, listed in the manifest `iconSet` and in `llms.txt`, so consumers no longer need to install `lucide-react` separately.
197
+
198
+ ### Patch Changes
199
+
200
+ - 419e30b: Docs API data now includes Radix passthrough props (Menu, Popover, AlertDialog, Tabs, HoverCard, ContextMenu and others) and Table's nine cell-slot descriptions, rendered as a "Cell slots" list in the API reference.
201
+ - 91849c7: Button and Card.Metric usage contracts no longer point at IconButton and Stat, which hds#254 folds into them.
202
+ - e8dba56: Every core component now says when to use it: `usage`, `whenNot`, `useInstead` and, for the overlays and Combobox, `keyboard` and `slots` flow into the manifest, `component-api.json` and the "Which one when" section of `llms.txt`.
203
+ - 6596b2d: The npm package now ships the agent context: `llms.txt`, `public/llms-full.txt` (with topic slices in `public/llms/`), `DESIGN.md`, `CONSUMING.md`, `docs/CONSUMING.md` and `src/app/data/component-api.json`, and the same files are served at the Storybook host. `hds-manifest.json` now points `docs` and `llmsTxt` at `https://hirobius-design-system.vercel.app` and names `llms.txt` as `agentEntrypoint`. A new `check-pack-contents` gate (part of `smoke:consumer`) fails when the tarball is missing any of these.
204
+
205
+ ## 0.18.0
206
+
207
+ ### Minor Changes
208
+
209
+ - eaff950: Adds the patterns-subpath codemod (hds#316): `hds-patterns-subpath` (also `node codemods/patterns-subpath.mjs`) rewrites root imports of the 21 deprecated pattern components to `@hirobius/design-system/patterns`, with `--root`, `--check` and `--dry-run`. The deprecation cycle (deprecated in 0.17, removed from the root at 1.0 by default, per hds#254) is documented in the new MIGRATIONS.md, and the README gains an "In use" section with numbers generated by `pnpm consumer:usage`.
210
+
211
+ ## 0.17.0
212
+
213
+ ### Minor Changes
214
+
215
+ - 43fd957: Turn on `strict` TypeScript for the package; export the six previously `Hds`-prefixed components under bare names (`Toggle`, `Checkbox`, `Radio`, `Slider`, `Select`, `Tooltip`) with the `Hds*` names kept as `@deprecated` aliases until the 1.0 alias-removal window (hds#254); add a size-limit budget for a Button-only root import.
216
+ - 643860b: hds#206 slice 3: the two runtime-overridden spacing tokens get canonical names,
217
+ with their overrides expressed as t-shirt scale steps. No computed value changes.
218
+ - New `semantic.space.surface.padding` (`--semantic-space-surface-padding`,
219
+ `hds.semantic.space.surface.padding`), default `scale.md` (24px). Tenants
220
+ override it per brand and density (brutalist-demo: `scale.sm`, `scale.xs`
221
+ under `[data-density="compact"]`).
222
+ - New `semantic.space.region.gutter` (`--semantic-space-region-gutter`,
223
+ `hds.semantic.space.region.gutter`), default `scale.md`. `theme.css` sets it
224
+ to `scale.lg` (32px), and `scale.sm` (16px) below 640px.
225
+ - `semantic.space.component.padding` and `semantic.space.layout.gutter` stay
226
+ as `$deprecated` aliases of the new names. Each tenant block redeclares the
227
+ deprecated alias next to its override, so consumers still reading the old
228
+ var inside a `[data-brand]` subtree keep the tenant value.
229
+ - `$deprecated` (DTCG) now marks all seven hds#206 aliases in
230
+ `hirobius.tokens.json`.
231
+ - hds's own `src/` now reads the new names, rewritten by
232
+ `scripts/codemod-spacing-vocabulary.mjs`.
233
+
234
+ - d41c65e: Add `semantic.space.scale.{xs,sm,md,lg,xl}` — one monotonic t-shirt spacing
235
+ scale (8/16/24/32/48px) per Adrian's 2026-09-26 decision on hds#206. The
236
+ existing gap families (`layout.tight/normal/gutter/inset/spacious`,
237
+ `component.gap/padding`) are unchanged and kept live as deprecated aliases —
238
+ no breaking change. Adds `check-spacing-vocabulary` (warn-severity, manual/
239
+ on-demand) flagging raw integer literals on Box `sx` spacing props (the
240
+ px-ambiguous form hds#206 identifies), registered in
241
+ `docs/guardrails/registry.json`. Consumer codemod off the deprecated names,
242
+ alias removal, and unifying `Stack`'s gap resolver with `box-sx`'s are
243
+ tracked as remaining hds#206 work.
244
+ - d41c65e: Add `semantic.size.{control,icon,avatar,row}` and `semantic.zIndex.{control,sticky}`
245
+ to `hirobius.tokens.json` per Adrian's 2026-09-26 decision on hds#242, plus
246
+ `semantic.radius.control` (checkbox glyph corner radius, a real design-scale
247
+ value distinct from `semantic.radius.action`) and `semantic.motion.distance`
248
+ (scroll-reveal `translateY` offset). Repoints all 15 `check-tier-bypass`
249
+ judgement-call violations named in hds#242 — activity-feed, checkbox,
250
+ code-block, radio, slider, table, `scroll-motion.css` — onto the new semantic
251
+ tokens, and adds `// tier-ok:` exemptions (matching #186's precedent) for the
252
+ two `radius-full` "fully round, one possible value" references that share a
253
+ line with a now-fixed size token. `pnpm check:tier-bypass` is green (was 15).
254
+
255
+ `scripts/lib/figma-model.mjs`: `semantic.radius.control` needed its own
256
+ `$type` (siblings inherit it from a group `$type` that `semantic.radius`
257
+ doesn't set); `semantic.motion.distance` is declared in `NOT_IN_FIGMA` (a
258
+ translateY offset has nothing to bind to in Figma, same as duration/easing).
259
+
260
+ - d41c65e: Adds `@hirobius/design-system/patterns` (hds#254, ratified 2026-09-26): the 22
261
+ `pattern`-tier components from the disposition table — `Calendar`,
262
+ `FileInput`, `Form`, `AppShell`, `OverflowList`, `Page`, `ActivityFeed`,
263
+ `AssetImg`, `Carousel`, `CodeBlock`, `StackedCardRail`, `DocLinkCard`,
264
+ `NavItem`, `SideNav`, `Stepper`, `TopNav`, `TreeList`, `ErrorPattern`,
265
+ `Toolbar`, `CommandPalette`, `Lightbox`, `Reveal` — now importable from their
266
+ own subpath (`vite.config.lib.ts` entry, `package.json#exports`). Non-breaking:
267
+ 21 of the 22 stay re-exported from the package root too, each now carrying a
268
+ `@deprecated`/`@removeIn 1.0.0` JSDoc notice pointing at the new subpath; the
269
+ root re-export is dropped at the next major once consumers have a codemod
270
+ (`StackedCardRail` is new to the published surface either way — it was
271
+ `pattern`-tiered in the manifest but missing from `src/index.ts`, so it has no
272
+ root re-export to deprecate).
273
+
274
+ This is the non-breaking half of the hds#254 decision. The 41 `fold` API
275
+ absorptions in the same disposition table wait for 1.0 and a consumer codemod —
276
+ tracked on hds#254/hds#124, not filed as new issues.
277
+
278
+ - d41c65e: Standard type ramp (Adrian's 2026-09-26 decision on hds#283): lift Tailwind 4's
279
+ ten default `fontSize` steps into `primitive.typography.size.*` verbatim —
280
+ `xs` 13→12, `sm` 15→14, `base` 17→16, `lg` 20→18, `xl` 24→20, `2xl` 30→24,
281
+ `3xl` 36→30, `4xl` 48→36, `5xl` 72→48, `6xl` 80→60 — and repoint every
282
+ semantic composite at its new rung per the 2026-09-24 audit table:
283
+ - Rendered size changes: `body` 17→16px, `ui` 15→14px, `display` 72→60px,
284
+ `eyebrow`/`caption` 13→12px (closes the eyebrow/caption duplicate), `mono`
285
+ 13→14px (repointed off `xs`, since `xs` alone now gives 12px).
286
+ - Rendered size unchanged, rung renamed to keep the primitive scale
287
+ monotonic: `h1` (`4xl`→`5xl`, 48px), `h2` (`2xl`→`3xl`, 30px, line-height
288
+ 42px→40px onto the 4px grid), `h3` (`lg`→`xl`, 20px).
289
+ - `component.button.size.{sm,md,lg}.fontSize` descriptions corrected
290
+ (13/15/17px → 12/14/16px); `tag`/`badge`.fontSize already referenced
291
+ `primitive.typography.size.xs` and pick up 12px automatically.
292
+ - `--semantic-typography-display-font-size` clamp max in `src/styles/theme.css`
293
+ updated 72px→60px to match.
294
+ - Hardcoded `text-[10px]` / `text-[11px]` / `text-[15px]` classes that bypassed
295
+ the ramp (`command-palette.tsx`, `badge.tsx`, `segmented-control.tsx`)
296
+ replaced with ramp-driven `text-xs` / `text-sm` utilities (now that
297
+ `tailwind.config.tokens.cjs` wires `fontSize.*` straight onto these same
298
+ primitives, this also closes the fork with a consumer app's Tailwind
299
+ defaults).
300
+
301
+ Base-size rationale recorded in `DECISIONS.md`. Not in this pass: a consumer's
302
+ duplicated display/h1 CSS clamp mins, Figma `figma:push`/`figma:snapshot
303
+ --ingest` re-sync, and a consumer app's 54 call sites on the `xs` rung (tracked as
304
+ remaining work on hds#283).
305
+
306
+ - d41c65e: Table: `TableColumn` gains optional `sortable`, `sortDirection` (`'ascending' | 'descending' | 'none'`), and `onSort`. Sortable columns render a real button inside the header cell, set `aria-sort` on the header cell, and show a token-sized direction glyph (ArrowUp/ArrowDown/ArrowUpDown). Non-sortable columns render exactly as before (pixel parity).
307
+
308
+ ### Patch Changes
309
+
310
+ - 999cc04: Storybook front door: an Introduction landing page that opens first, the Hirobius brand in the sidebar, share-preview tags, a Foundations/Primitives/Patterns sort order, spaced story titles (story ids unchanged), and six internal components hidden from the published build.
311
+ - 0b32d55: Accessibility fixes from the axe sweep (#311): StepperField label/input pairing, Card.Progress accessible name, Carousel and StackedCardRail focusable named scroll regions, OverflowList and MetadataList valid list semantics, AssetImg aria-label only on roled elements, Calendar outside-month day contrast, Tokenizer state contrast, Bleed story token colour, unconditional `:focus-visible` ring for `.hds-focus`, highlighted-row inset ring in Menu, Select, Combobox and MultiSelector.
312
+ - d41c65e: Delete dead internal (non-exported) files with zero consumers: `morph-card.tsx`,
313
+ `CascadeText.tsx`, `controls.tsx`. None were in `src/index.ts`'s public barrel
314
+ and none were imported anywhere in the repo — confirmed by grep before removal
315
+ (hds#133, Tier-1 zero-risk bucket, Adrian's option-A decision 2026-09-26).
316
+ - 75cf4ab: Relocate the docs-tooling bucket (hds#133 ISSUE-09, Adrian's option-A decision:
317
+ delete/relocate only non-exported internals, keep every exported component) out
318
+ of `src/app/components/` into `src/docs-tooling/`: `api-reference`,
319
+ `component-preview`, `controls-panel`, `demo-block`, `doc-page-header`
320
+ (+ its test), `health-rail`, `page-footer`, `preview-frame`, `propTableUtils`,
321
+ `shell-controls`, `sketch-controls`, `theme-toggle`, `variant-strip`,
322
+ `DocPageSpec`, `TokenDisplayToggle`. None were exported from `src/index.ts` or
323
+ `src/patterns.ts`, so this is a zero-risk, non-breaking move — only internal
324
+ relative import paths changed. `pnpm manifest:generate` now correctly drops
325
+ these from `public/hds-manifest.json` (component-discovery only scans
326
+ `src/app/components/`), which is the intended effect: they were never
327
+ consumer-facing. `lab/*` relocation stays remaining — its earlier blocker
328
+ (extracting `lab/tokenUtils.ts` from the public `Token` primitive) was already
329
+ resolved in hds#233/369dce5; the file lives at `src/app/components/tokenUtils.ts`
330
+ now.
331
+ - 643860b: Relocate the internal token-lab views (hds#133, Adrian's option-A decision) from
332
+ `src/app/components/lab/` to `src/docs-tooling/lab/`: `legacy-token-detail`,
333
+ `legacy-token-list`, `token-collection-list`, `token-list`. None were exported
334
+ from `src/index.ts` or `src/patterns.ts`, and the shared `tokenUtils` module the
335
+ public `Token` primitive depends on already lives outside `lab/`
336
+ (`src/app/components/tokenUtils.ts`, hds#233), so no exported component is
337
+ removed or renamed and the published package is unchanged. A new
338
+ docs-tooling boundary test keeps lab modules out of the component tree.
339
+ - 75cf4ab: Reviewer-fix follow-up to the docs-tooling relocation (hds#133, commit edb31c0):
340
+ re-key `.token-path-baseline.txt`'s `health-rail.tsx` line to its new location
341
+ `src/docs-tooling/health-rail.tsx` (the stale `src/app/components/` path was
342
+ tripping `check-token-paths-ratchet.mjs`'s default-mode gate with a "new"
343
+ violation that was really the same pre-existing one, just moved). Regenerate
344
+ `src/app/design-system/token-usage-map.json` (`pnpm tokens:index`) and
345
+ `docs/audits/exceptions-audit.md` (`node scripts/audit-exceptions.mjs`) so both
346
+ reference the moved docs-tooling paths instead of the old
347
+ `src/app/components/*` ones. No component code changed.
348
+
349
+ Refs hirobius/hds#133
350
+
351
+ - 75cf4ab: hds#206 remaining work: codemod hds's own internal spacing-token references
352
+ (CSS `var(--semantic-space-{layout,component}-*)` reads and
353
+ `hds.semantic.space.{layout,component}.*` / bare `semantic.space.*` dotted
354
+ paths in `src/`) off the deprecated aliases and onto the canonical
355
+ `semantic.space.scale.{xs,sm,md,lg,xl}` t-shirt scale added in #297. Pure
356
+ rename — every rewritten reference resolves to the identical px value, no
357
+ visual or behavioral change. Added `hds.semantic.space.scale.*` to the token
358
+ bridge (`tokens.ts`) so the canonical accessor exists in JS, not just CSS.
359
+
360
+ Script: `scripts/codemod-spacing-vocabulary.mjs` (idempotent, `--check` for a
361
+ dry run, unit-tested in `scripts/__tests__/codemod-spacing-vocabulary.test.mjs`).
362
+
363
+ Out of scope for this slice (unchanged): the public `gap`/`padding` prop
364
+ VALUES components accept (`gap="tight"`, `padding="component"`, etc.) and
365
+ `box-sx.ts`'s own resolver (it builds the deprecated var name dynamically,
366
+ not as a literal — unifying it with Stack's resolver is hds#206 item #4).
367
+ The deprecated aliases in `hirobius.tokens.json` are kept live and the
368
+ `check-spacing-vocabulary` gate stays in warn mode, per Adrian's 2026-09-26
369
+ decision.
370
+
371
+ - 75cf4ab: hds#206 review fix: the previous codemod commit rewrote two spacing-token
372
+ sites onto `semantic.space.scale.md` when it should not have, because that
373
+ scale token has no runtime override where the original alias did:
374
+ - `semantic.space.layout.gutter` — `theme.css` overrides
375
+ `--semantic-space-layout-gutter` to 32px desktop / 16px under 639px;
376
+ `scale.md` is a fixed 24px. Reverted `tokens.ts`'s `layout.gutter` accessor
377
+ and `shell-controls.tsx`'s `paddingInlineStart` back to the alias var.
378
+ - `semantic.space.component.padding` — `tenants.css` overrides
379
+ `--semantic-space-component-padding` per tenant/density (brutalist-demo).
380
+ Reverted the `tokens.ts` `component.padding` accessor and its 14
381
+ consumption sites (`callout.tsx`, `code-block.tsx` x2, `container.tsx`,
382
+ `surface-padding.ts`, `surface.tsx`, `table.tsx` x2, `static.css`
383
+ `.hds-card`, `legacy-token-detail.tsx`, `shell-controls.tsx`
384
+ `paddingInlineEnd`, `sketch-controls.tsx`, `sketch.tsx`) back to the alias
385
+ var so tenant/density and responsive overrides keep working.
386
+
387
+ `semantic.space.component.gap` and `semantic.space.layout.normal` (also
388
+ touched by the codemod) have no such override and are left on `scale.xs`/
389
+ `scale.md` — genuinely pure renames.
390
+
391
+ - d41c65e: Fix hds#254 review defect: the `@deprecated`/`@removeIn 1.0.0` JSDoc on the 21
392
+ pattern-tier components was on the component declarations themselves (e.g.
393
+ `export const AppShell` in `app-shell.tsx`), so TypeScript attached the
394
+ deprecation to the symbol everywhere — including the new
395
+ `@hirobius/design-system/patterns` subpath the notice tells consumers to
396
+ migrate to (`import { AppShell } from '.../patterns'` produced TS6385).
397
+
398
+ The declarations are now plain (undeprecated); the `@deprecated` notice lives
399
+ only on a root-only `const` alias in `src/index.ts` for each of the 21 names,
400
+ which shadows the star-exported binding for root-import consumers per ES
401
+ module semantics. Importing from `/patterns` now gives no deprecation
402
+ warning; importing the same name from the package root still does.
403
+
404
+ - d41c65e: Delete the dead `componentPreviewRegistry` island: `componentPreviewRegistry.tsx`,
405
+ `specimen-block.tsx`, `variant-preview-deck.tsx` (~900 lines). None were exported
406
+ from `src/index.ts`'s public barrel, none were imported outside this trio, and
407
+ knip flagged all three as unused — the remains of the docs SPA deleted in #90.
408
+ The Storybook-built component reference site (#280) replaces what this was
409
+ reaching for; its hand-written `DEFAULT_PREVIEW_PROPS` table had decayed to 46
410
+ entries covering 30 of 139 manifest components. Decision and the pattern worth
411
+ keeping (`import.meta.glob` module discovery + manifest-driven
412
+ `preview.exportName`/`preview.sizing`) recorded in `DECISIONS.md` (hds#286,
413
+ Adrian's decision 2026-09-26).
414
+ - d41c65e: Table: fix invalid ARIA structure from the sortable-columns change (hds#294 review). The grid now carries `role="table"`, header/data rows are wrapped in `role="row"` elements (`display: contents`, so CSS Grid layout is unaffected), every header cell has `role="columnheader"` (not just sortable ones), and data cells have `role="cell"` — so `aria-sort` no longer sits on an orphan columnheader outside any table/row ancestry. Also drops the manual `onKeyDown` on the sort button (native `<button>` already turns Enter/Space into a click; the duplicate handler risked a double-toggle in browsers with inconsistent Space-keyup behavior).
415
+
416
+ ## 0.16.0
417
+
418
+ ### Minor Changes
419
+
420
+ - f20513f: - **New exports:** `HdsSelect` and its `SelectProps` are now reachable from the package root (hds#231), and `HdsDocsShell` is a new 3-column docs layout with independently sticky rails (hds#280).
421
+ - **New token:** `primitive.size.interactive.minCompact` (24px). Tokenizer's remove button now uses it, so it meets the WCAG 2.2 2.5.8 minimum target size (hds#287).
422
+ - **Fixes:** Radio's colour transition now actually animates (hds#257); StackedCardRail no longer breaks its styles when server-rendered (hds#284).
423
+ - **Deprecations:** `ComponentInstanceMatrix`, `FoundationSwatch`, `Sketch`, `Token` and `CinematicLink` are docs-internal and now warn once. They leave the published barrel in 1.0.0 (hds#232).
424
+ - **Smaller bundle:** the main entry drops from 202 to 183 kB gzip, because the docs-only token corpus no longer ships in the published library (hds#279).
425
+ - Type-ramp prerequisites (hds#283, steps 0–3). **Removes six unused primitive tokens**: `--primitive-typography-size-2xs`, `--primitive-typography-weight-light`, `--primitive-typography-weight-semibold`, and `--primitive-typography-letterSpacing-tighter` / `-wide` / `-wider`. None had a consumer in hds or the apps that use it. Icons are now sized from `primitive.size.16/20/24` instead of the type scale; a `tracking-caps` utility replaces stock `tracking-wide` for eyebrow text; DateInput and DateTimeInput calendar buttons no longer collapse under flexbox; and a type specimen story documents the scale.
426
+
427
+ ## 0.15.0
428
+
429
+ ### Minor Changes
430
+
431
+ - a5d33b6: Every props type behind a component the package exports is now exported too, so a
432
+ consumer can name it.
433
+
434
+ 41 of them were not. `src/index.ts` re-exports each public module with `export *`, so a
435
+ type ships only if its module exports it — and for `Alert`, `Container`, `Stack`,
436
+ `Grid`, `CardHeader`, `ErrorBoundary` and 35 others it did not. The single most ordinary
437
+ thing a consumer writes,
438
+
439
+ ```tsx
440
+ const Wrapped = (props: AlertProps) => <Alert {...props} />;
441
+ ```
442
+
443
+ did not compile, and the author had to fall back to `React.ComponentProps<typeof Alert>`
444
+ or restate the shape by hand. Nothing here could see it: `tsc` is satisfied because the
445
+ type is in scope inside its own module, the component renders fine, and every test in
446
+ this repo imports from `src/`, where the barrel is irrelevant.
447
+
448
+ The rule is reachability, not the `Props` suffix. A type is exported iff it annotates the
449
+ props of a component the module exports — directly, through a trailing `export { … }`, or
450
+ through an `Object.assign` compound like `Grid`. That deliberately leaves internal: the 33
451
+ `VariantProps<typeof xVariants>` cva aliases (`button.tsx` sets that convention),
452
+ composition bases such as `NavNativeProps`, union arms such as `TokenBaseProps`, cast
453
+ targets such as `WiredChildProps`, and the props of sub-components the module keeps to
454
+ itself.
455
+
456
+ Purely additive — 41 symbols added, none removed, no module changed.
457
+ `scripts/check-props-exports.mjs` runs from `pretest` and holds the line.
458
+
459
+ ### Patch Changes
460
+
461
+ - c2b9e86: Every React-bearing bundle now starts with `'use client'`, so the package works in the
462
+ Next.js App Router that `CONSUMING.md` advertises.
463
+
464
+ Before this, `dist/` shipped 20 `useState` calls and zero client directives. In the App
465
+ Router a module is a Server Component until it says otherwise, and a Server Component
466
+ that calls a hook fails at render — so the first `import { Button } from
467
+ '@hirobius/design-system'` in a Next.js page threw. No existing consumer had hit it
468
+ (the consumer apps use Vite and Astro), which is exactly why a prospective user would have
469
+ found it first.
470
+
471
+ The directive is applied **per chunk, not as a blanket banner**. The framework-free
472
+ subpaths — `brand`, `tokens`, `cn`, `manifest`, `mui` — stay unmarked on purpose:
473
+ `'use client'` turns every export of a module into an opaque client reference when it is
474
+ imported from server or edge code, which would break `tokens.color.primary` in a Server
475
+ Component and defeat `brand`'s documented "static Astro build or edge runtime" use.
476
+
477
+ Marked: the main barrel, `contexts`, `form`, `scroll`, and the two shared chunks they
478
+ import. A chunk is marked iff it imports React, or imports a chunk that does.
479
+
480
+ Gated: `scripts/check-rsc-directives.mjs` runs from `smoke:consumer` and re-derives the
481
+ rule from the emitted files, in both directions, so neither a missing nor a spurious
482
+ directive can ship again.
483
+
484
+ ## 0.14.0
485
+
486
+ ### Minor Changes
487
+
488
+ - 22ec2b5: Three shipped guards were dead in every installed copy, baked by our own build.
489
+
490
+ Library code read `import.meta.env`, which Vite's library build evaluates at HDS's build
491
+ time rather than the consumer's. It baked to a constant inside `dist/`, so the guards had
492
+ never run for anyone who installed the package:
493
+ - `Icon` never warned about a missing `icon` prop (`import.meta.env.DEV` baked to `false`).
494
+ - `warnOnce` never emitted a deprecation warning (`import.meta.env.PROD` baked to `true`),
495
+ so the whole deprecation channel was silent.
496
+ - `TenantProvider` never wrote `data-brand` / `data-tenant`, because `VITE_TENANT_SLUG` was
497
+ read from HDS's environment and baked to `{}`. It was inert regardless of what a consumer
498
+ set.
499
+
500
+ The first two now read `process.env.NODE_ENV`, which consumer bundlers substitute at the
501
+ build that actually knows the answer.
502
+
503
+ **Breaking:** `TenantProvider` takes the tenant as a `slug` prop instead of reading an
504
+ environment variable. A library component cannot read its consumer's environment, so there
505
+ is no backwards-compatible version of the old behaviour — but nothing depended on it
506
+ either, since it never worked once installed. Omitting `slug` writes no attribute, which is
507
+ what every existing call site already got.
508
+
509
+ ```tsx
510
+ <TenantProvider slug={import.meta.env.VITE_TENANT_SLUG as TenantSlug}>
511
+ ```
512
+
513
+ Two standing guards keep the class from returning: `tests/no-shipped-import-meta-env.test.ts`
514
+ fails on any `import.meta.env` reachable from shipped source, including via bracket access
515
+ or destructuring, and `src/lib/env.test.ts` pins the source shape that makes the bundler
516
+ substitution work.
517
+
518
+ ### Patch Changes
519
+
520
+ - c1561f5: The published types did not resolve for any `node16`/`nodenext` ESM consumer.
521
+
522
+ `tsconfig.dts.json` compiles with `moduleResolution: "Node"`, so `tsc` emitted relative
523
+ specifiers the old CommonJS way — `from './app/components/button'`, no extension — into a
524
+ package that declares `"type": "module"`. ESM does not guess extensions, so all 150 of them
525
+ failed to resolve. `attw` reported `InternalResolutionError` against 7 of the 14 entry
526
+ points: `.`, `tokens`, `manifest`, `contexts`, `form`, `brand`, `scroll`.
527
+
528
+ In practice a Next.js app, or any `"type": "module"` project on `moduleResolution: node16`,
529
+ installed this package and got broken types for all of it.
530
+
531
+ Nothing here could see it. `tsc` typechecks `src/`, every test imports `src/`, and
532
+ `smoke:consumer` proved each subpath _imports_ cleanly — which it does; it is the `.d.ts`
533
+ resolution that was broken. The emitted output is the one artifact nothing read.
534
+ - `scripts/add-dts-extensions.mjs` rewrites each emitted specifier to what it actually
535
+ resolves to (`./button` → `./button.js`, `./context` → `./context/index.js`), and exits 1
536
+ on any specifier that resolves to nothing rather than emitting a path that will fail in
537
+ someone's install. It runs as part of `pnpm build:types`.
538
+ - `scripts/check-published-types.mjs` runs `attw` over the packed tarball from
539
+ `smoke:consumer`, so the regression cannot return. Profile `esm-only`, because this
540
+ package has no CommonJS build and node10 cannot read `exports`; the four CSS subpaths are
541
+ excluded because a stylesheet has no types to resolve.
542
+
543
+ Every checked entry point is now green under `node16 (from ESM)` and `bundler`.
544
+
545
+ ## 0.13.1
546
+
547
+ ### Patch Changes
548
+
549
+ - 68df525: The published `hds-manifest.json` now carries Alert's Figma node URL in `componentSpecs.Alert.figmaUrl`, the single source the repo projects into its README, Storybook and Figma links.
550
+ - 5db84a4: Fix Figma mapping drift in the published `hds-manifest.json`. Alert declares its real `tone` axis (`success | danger | warning | info`), not `variant` / `error`. Button's `tone` is now documented. HeadingStack binds `subheading`. Dialog's Title, Description and close toggle bind to its compound parts. Avatar, Card, Divider and TextLockup declare their cva axes.
551
+ - f3268b4: **chore(pkg):** the package now declares its own front door. `license` is
552
+ `SEE LICENSE IN NOTICE.md` — the code is MIT (root `LICENSE`), but the embedded
553
+ fonts ship under their own terms, which no single SPDX expression covers — and
554
+ `NOTICE.md` is added to `files` so the published tarball carries those terms.
555
+ `repository`, `homepage`, `bugs` and `keywords` are set, so npm and GitHub link
556
+ back to the repo instead of showing nothing.
557
+ - 65a5bf2: **fix(manifest):** `public/hds-manifest.json` no longer fills `figmaLink` with
558
+ `TODO:hds-master:<Name>` placeholders. Each `componentSpecs[]` and `utilities[]`
559
+ entry now carries a real Figma URL or `null`, so tooling can count linked
560
+ components directly. Consumers that treated any non-null `figmaLink`
561
+ as "has a Figma link" now get the right answer.
562
+ - 65a5bf2: **fix(tokens):** the generated tenant overlay CSS no longer drops the first
563
+ brand's base rule. Its header comment contained `*/` inside a file glob, which
564
+ closed the comment early and turned the rest of the header into an invalid
565
+ selector prefix for the first `[data-brand]` rule. That brand's light overrides
566
+ now apply.
567
+
568
+ ## 0.13.0
569
+
570
+ ### Minor Changes
571
+
572
+ - 9e9f19e: **0.13.0 — final clean cut before the shelf.** Rolls up the work merged to `main`
573
+ after 0.12.0 and one bug fix, so the published package matches the shelved repo.
574
+ - **fix(icon):** stop forwarding a token (`var(--primitive-*)`) to Lucide's numeric
575
+ `size` prop, which landed on the SVG `width`/`height` **attribute** and logged a
576
+ console error on every icon render (icons still rendered — the `style` sized them
577
+ — but the console was noisy). Now only a numeric size is forwarded; token sizes
578
+ flow through `style` (valid as CSS). (#123)
579
+ - **feat(styles):** `@hirobius/design-system/static.css` — a CSS-only static-primitive
580
+ layer so a zero-React Astro/HTML page can render `Badge`/`Card`/`Alert`/`Divider`/`Tag`
581
+ via `.hds-*` classes, token-bound and theme-aware. (#64)
582
+ - **docs(scroll):** the llms.txt scroll-driven-sections recipe + golden-path stories
583
+ for the `Reveal`/`Pin`/`useScrollProgress` primitives. (#116)
584
+
585
+ ## 0.12.0
586
+
587
+ ### Minor Changes
588
+
589
+ - aba78d2: Promote three previously-internal components to the public API surface (#76):
590
+ - **SideNav** (`Navigation`) — sidebar navigation row primitive (root/nested levels).
591
+ - **CommandPalette** (`Overlays`) — Cmd-K / Ctrl-K fuzzy search over the HDS manifest.
592
+ - **HdsLightbox** (`Overlays`, was internal `ImageLightbox`) — full-bleed image viewer on Radix Dialog.
593
+
594
+ Each flips `@internal`→`@public`, drops `@doc-exempt`, moves from `utility`→`primitive` tier, and gains a Storybook story, a Code Connect stub, and a public barrel export. `ImageLightbox` is renamed to `HdsLightbox` to match the Radix-overlay naming convention.
595
+
596
+ The vitest config now resolves the `virtual:hds-manifest` module (mirroring the app/lib builds) so manifest-consuming components like CommandPalette render under the story-render smoke gate.
597
+
598
+ - aba78d2: Add Tier-1 native primitives closing the Astryx coverage gap (#76), with zero new runtime dependencies:
599
+ - **Kbd** — inline keyboard key / shortcut hint (`<kbd>`).
600
+ - **StatusDot** — compact solid status indicator with semantic tones.
601
+ - **Timestamp** — semantic `<time>` with date/time/datetime/relative formatting.
602
+ - **Blockquote** — quoted passage with optional attribution.
603
+ - **VisuallyHidden** — screen-reader-only content (`sr-only`), polymorphic `as`.
604
+ - **AvatarGroup** — overlapping avatar cluster with a `+N` overflow chip.
605
+ - **ButtonGroup** — attaches Button children into one segmented control.
606
+ - **InputGroup** — text input framed with leading/trailing adornments.
607
+ - **CircularProgress** — SVG progress ring (determinate + indeterminate).
608
+
609
+ Each ships with tokens-only styling, colocated tests, a Storybook story, a Code Connect stub, and a Swiss-canon fixture.
610
+
611
+ - aba78d2: Add Tier-1 Radix-skinned interactive/overlay primitives closing the Astryx coverage gap (#76). Each wraps a Radix primitive themed with the HDS overlay role tokens (matching Menu/Dialog/HdsTooltip), adding these `@radix-ui` dependencies:
612
+ - **HdsToggleButton** (`@radix-ui/react-toggle`) — single two-state pressable button (`aria-pressed`).
613
+ - **HdsAlertDialog** (`@radix-ui/react-alert-dialog`) — modal confirmation dialog for destructive actions; Dialog-matched skin, no dismiss-on-outside-click.
614
+ - **HdsHoverCard** (`@radix-ui/react-hover-card`) — hover/focus preview card (not a substitute for an accessible tooltip).
615
+ - **HdsContextMenu** (`@radix-ui/react-context-menu`) — right-click menu sharing Menu's exact SURFACE/ITEM token skin.
616
+ - **HdsAspectRatio** (`@radix-ui/react-aspect-ratio`) — width:height ratio box that prevents media layout shift.
617
+
618
+ Each ships colocated tests (Radix jsdom polyfills), a Storybook story (overlays render closed on mount), a Code Connect stub, and a Swiss-canon fixture.
619
+
620
+ - aba78d2: Add the Tier-2 pattern layer closing the Astryx coverage gap (#76) — 8 composite/pattern components, zero new runtime dependencies:
621
+ - **MetadataList** (`Display`) — semantic `<dl>` of term/description pairs; the canonical metadata slot (horizontal/vertical).
622
+ - **OverflowList** (`Layout`) — count-based list that collapses overflow into a `+N` chip (SSR/test-safe, not width-measured).
623
+ - **TopNav** (`Navigation`) — top navigation bar with brand / links / trailing slots and optional sticky.
624
+ - **AppShell** (`Layout`) — application scaffold with header, sidebar, and main regions (semantic `<header>`/`<aside>`/`<main>`).
625
+ - **SelectableCard** (`Actions`) — card that behaves as a selectable option (`role="checkbox"`, full keyboard/ARIA).
626
+ - **Tokenizer** (`Inputs`) — token/chip input composing `Tag`; Enter to add, Backspace to remove.
627
+ - **FileInput** (`Inputs`) — styled file picker / dropzone over a real native `<input type="file">`, with drag-and-drop.
628
+ - **HdsMultiSelector** (`Inputs`) — multi-select dropdown composing `Popover` + a checkbox option list (no new dep).
629
+
630
+ Each ships colocated tests, a Storybook story, a Code Connect stub, and a Swiss-canon fixture. The `virtual hds-manifest` size-limit budget was bumped 41→46 kB to accommodate the enlarged public component surface (justified in `.size-limit.cjs`).
631
+
632
+ - aba78d2: Complete the Tier-2 pattern layer (Astryx coverage, #76) with 4 more components:
633
+ - **Toolbar** (`Actions`) — control group with roving focus on `@radix-ui/react-toolbar`; compound `Toolbar.Button` / `Separator` / `ToggleGroup` / `ToggleItem` / `Link`, skinned to match Menu.
634
+ - **TreeList** (`Navigation`) — hierarchical expand/collapse list with correct `role="tree"` / `treeitem` / `group` ARIA (native, no dep).
635
+ - **Stepper** (`Navigation`) — wizard/progress step indicator with complete/current/upcoming states (distinct from the existing `StepperField` number input).
636
+ - **Carousel** (`Display`) — native CSS scroll-snap track with Prev/Next affordances (no carousel library).
637
+
638
+ Adds one dependency: `@radix-ui/react-toolbar`. Each ships colocated tests, a Storybook story, a Code Connect stub, and a Swiss-canon fixture.
639
+
640
+ Also lands **ADR-020** (date/time components on react-day-picker v10 + date-fns) and installs those dependencies as groundwork for the Tier-3 date/time family — no Tier-3 components ship in this change yet.
641
+
642
+ - aba78d2: Add the Tier-3 date/time family, completing the Astryx coverage build (#76) per ADR-020 (react-day-picker v10 + date-fns):
643
+ - **HdsCalendar** (`Inputs`, primitive) — HDS-token-skinned month calendar over `react-day-picker`; passes through `mode` (single/range/multiple), `selected`, `onSelect`, disabled/min/max, locale. The foundation the date inputs compose.
644
+ - **HdsTimeInput** (`Inputs`, primitive) — token-skinned native `<input type="time">` (no calendar dependency).
645
+ - **HdsDateInput** (`Inputs`, pattern) — single-date text field with a calendar Popover; date-fns parse/format.
646
+ - **HdsDateRangeInput** (`Inputs`, pattern) — date-range trigger + range calendar Popover.
647
+ - **HdsDateTimeInput** (`Inputs`, pattern) — combined date (calendar Popover) + time (`HdsTimeInput`) producing one `Date`.
648
+
649
+ Each ships colocated tests (Radix Popover jsdom polyfills), a Storybook story, a Code Connect stub, and a Swiss-canon fixture. Dependencies `react-day-picker` and `date-fns` (added as ADR-020 groundwork) are now in use; they tree-shake out of consumer bundles that don't import a date component (verified by `smoke:consumer`, and the app `size-limit` main-entry budget is unchanged).
650
+
651
+ - f8f9e1f: Add `Box` — a polymorphic layout primitive with a token-first `sx` engine (#92), a deliberate subset of MUI's `sx`: spacing shorthands (`m`/`p`/`gap` family) resolve off the HDS 4px scale and named layout steps, `color`/`bgcolor`/`borderColor`/`fill`/`stroke` resolve dotted token keys to semantic CSS vars, responsive `{ xs, sm, md, lg, xl }` values compile to `min-width` media queries, and `&`-prefixed keys compose nested selectors. Resolves to a deterministic, hash-cached, SSR-safe injected CSS class (`src/app/components/box-sx.ts`).
652
+ - 0b3738f: Add the `@hirobius/design-system/brand` subpath (#64): a framework-free palette → HDS-semantic overlay bridge. `brandOverlayVars` / `brandOverlayStyle` / `brandOverlayCss` map a small brand palette (six colours + optional font/radius) onto the HDS semantic custom properties, so a downstream render target (a static Astro site, an SSR shell, an email) can theme itself from a brand palette while inheriting HDS's contrast-checked semantics, accent states, and radius. Interactive accent states are derived with CSS `color-mix()` — no colour-math dependency. Imports no React and no Node built-ins. This is the seam that lets the `hirobius/clients` Astro factory consume HDS instead of maintaining a parallel `--brand-*` token system.
653
+ - 6a4f0f6: CSS-in-JS → Tailwind (#60): migrate `ActivityFeed` to Tailwind + `cva`, formalizing the status axis as a `tone` variant (`activityToneVariants` / `activityAvatarVariants`). Typography composites (`heading3`/`body`/`ui`/`technical`) and the avatar/list resets move from inline `style` objects onto var()-bound Tailwind classes on the exact same `--semantic-*`/`--primitive-*` custom properties — no visual change.
654
+
655
+ **Value rename (non-breaking):** `ActivityEvent`'s color axis now follows the HDS tone vocabulary (`neutral | danger | success | warning | info`) via a new optional `tone` prop, matching the fixed contract in `docs/architecture/variant-contract.md`. The old `status` prop (`ActivityStatus`, including `'error'`) is deprecated but still fully supported: `status="error"` resolves to the same `danger` tone as `tone="danger"` (proven by a byte-for-byte render-output test). New code should use `tone`; `status`/`ActivityStatus` will be removed in a future major.
656
+
657
+ - 254d9eb: Drop the `Hds` prefix from 12 net-new leaf component families — step 1 of retiring the `Hds` prefix fleet-wide. **Breaking rename shipped as a `minor` per the pre-1.0 semver-zero convention (0.x: breaking changes bump the minor)** — safe now because these families are new in this release cycle with only fresh, first-party consumers. These families shipped in 0.12.0/0.13.0 with zero external consumers, so the rename is safe to land as the first cut. Compound sub-exports (e.g. `HdsAlertDialog.Trigger`), `*Props`/`*Component` types, and source filenames (`hds-*.tsx` → `*.tsx`) all moved together. `scripts/scaffold-component.mjs` and `scripts/component-discovery.mjs` were flipped in the same PR: new components must now be plain PascalCase, and a leading `Hds` is the namespace violation going forward (not the absence of one).
658
+
659
+ | Old | New |
660
+ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
661
+ | `HdsToggleButton` | `ToggleButton` |
662
+ | `HdsAlertDialog` (+ `.Trigger`/`.Portal`/`.Overlay`/`.Content`/`.Header`/`.Footer`/`.Title`/`.Description`/`.Action`/`.Cancel`) | `AlertDialog` (same compounds) |
663
+ | `HdsHoverCard` (+ `.Trigger`/`.Content`) | `HoverCard` (same compounds) |
664
+ | `HdsContextMenu` (+ `.Trigger`/`.Content`/`.Item`/`.CheckboxItem`/`.RadioGroup`/`.RadioItem`/`.Label`/`.Separator`/`.Group`/`.Sub`/`.SubTrigger`/`.SubContent`) | `ContextMenu` (same compounds) |
665
+ | `HdsAspectRatio` | `AspectRatio` |
666
+ | `HdsMultiSelector` (+ `HdsMultiSelectorOption`) | `MultiSelector` (+ `MultiSelectorOption`) |
667
+ | `HdsLightbox` | `Lightbox` |
668
+ | `HdsCalendar` | `Calendar` |
669
+ | `HdsTimeInput` | `TimeInput` |
670
+ | `HdsDateInput` | `DateInput` |
671
+ | `HdsDateRangeInput` | `DateRangeInput` |
672
+ | `HdsDateTimeInput` | `DateTimeInput` |
673
+
674
+ Consumers importing any of the above must update to the new names; there is no compatibility alias. Everything else — the `hds-` CSS class namespace, `data-*` attributes, the JSX-compiler DSL tags (`HdsFrame`/`HdsHeading`/`HdsLabel`/`HdsPhosphor`/`HdsCaption`), the provider/theme surface, the manifest type surface, and the `Form`/`FormField`/`ThemeProvider`/`Tooltip` collision families — is unchanged and out of scope for this PR.
675
+
676
+ - 7f48641: Add the "every-layout" layout primitives (#96): `Cluster` (wrapping flex row with tokenized gap/align/justify), `Center` (max-width column with auto margins and an optional tokenized gutter), `Sidebar` (fixed-width rail + fluid content that wraps to stacked via flex-basis/flex-grow disparity, no media query), `Switcher` (flips a row to a column below a `threshold` via the flex-basis calc() technique, with a `limit` to force stacking), `Cover` (vertical shell with an optional header/footer and an auto-margin-centered main region), `Frame` (aspect-ratio-locked, token-clipped media box — a thin radius/overflow skin over `AspectRatio`), and `Bleed` (controlled negative-margin full-bleed within a padded container). All seven are `as`-polymorphic, `forwardRef`, and token-driven — spacing resolves off the `--semantic-space-layout-*` scale, never raw px.
677
+ - 3faaea9: CSS-in-JS→Tailwind migration (#93): convert `Table` and `Disclosure` off inline
678
+ `CSSProperties` objects onto Tailwind semantic classNames, then adopt `cva`.
679
+ - `Table`: `density` (`comfortable | compact`) — the variant contract's
680
+ canonical density example — plus column `align` and `stickyHeader` are now
681
+ `cva` axes (`tableHeaderCellVariants`, `tableDataCellVariants`) driving
682
+ Tailwind padding/min-height/position utilities bound to the same semantic
683
+ tokens the previous inline styles referenced. Sort/selection are not
684
+ implemented on `Table` (none existed pre-migration); `stickyHeader`
685
+ positioning behavior is unchanged.
686
+ - `Disclosure`: the `variant` (`panel | nav | card`) trigger styling is now
687
+ `cva`-driven (`disclosureTriggerVariants`). Open/closed state moved from a
688
+ JS `hovered`-state-driven inline style to a Radix-style
689
+ `data-state="open"|"closed"` attribute with `data-[state=open]:` Tailwind
690
+ selectors — same visual result, no more mousemove-triggered re-renders. The
691
+ `motion/react` height/opacity/chevron-rotation animation is untouched (still
692
+ JS-driven, not a CSS transition).
693
+
694
+ No public prop or export renames — both components keep their existing prop
695
+ API. `tableHeaderCellVariants`/`tableDataCellVariants`/
696
+ `disclosureTriggerVariants` are new `@internal` cva-helper exports (same
697
+ pattern as `buttonVariants`) — compose via component props, not directly.
698
+
699
+ - e1a127c: CSS-in-JS → Tailwind (#93): migrate `Token` fully to Tailwind + `cva` (`tokenShellVariants` / `tokenNodeInlineVariants` / `tokenLabelVariants`), replacing `Token.module.css` (deleted) and a duplicate inline-style `isSelected` override with one variant set on the same `--semantic-*`/`--component-*`/`--primitive-*` custom properties — no visual or prop changes.
700
+
701
+ `SegmentedControl`'s active×hover×pressed×disabled×secondary state matrix is also converted to `cva` compound variants (`segmentedControlWrapperVariants` / `segmentedControlRailVariants` / `segmentedControlItemVariants` / `segmentedControlIndicatorVariants` / `segmentedControlDescriptionVariants` / `segmentedControlLabelVariants` / `segmentedControlFocusRingVariants`), replacing the inline `style` objects with the same tokens. Per ADR-015 the component keeps its own hover/focus tracking (not the shared `useInteractionState` hook, out of scope here) — only the class composition changed.
702
+
703
+ No breaking changes: all public props, prop vocabularies, and rendered DOM/ARIA structure are unchanged for both components. The new `*Variants` exports are additive (compose via component props instead, per the variant-contract convention).
704
+
705
+ - 36e3eb1: Remove `InfoPage` from the public API (#49) — a breaking change, bumped as
706
+ `minor` per the pre-1.0 semver-zero convention (0.x.y: breaking changes
707
+ increment the minor).
708
+
709
+ `InfoPage` was dead-portfolio scaffolding: a branded profile surface built
710
+ for the portfolio landing page that hardcoded the (already-deleted)
711
+ `/assets/adrian.webp` asset. It had no design-system-consumer use. Also
712
+ removes `src/app/data/projects.ts`, the portfolio project data it and the
713
+ now-deleted portfolio pages depended on — it has zero remaining importers.
714
+
715
+ Migration: none. `InfoPage` was never intended for consumption outside the
716
+ portfolio landing page; there is no drop-in replacement.
717
+
718
+ - b1057ac: Add `Reveal` and `Pin` scroll primitives (#116) to the core library — CSS scroll-driven, **zero JS and zero dependencies**. `Reveal` fades/slides/scales content in as it enters the viewport via `animation-timeline: view()` (`animation`: `fade | fade-up | fade-down | scale`); `Pin` is a token-friendly `position: sticky` wrapper (`top` offset) for pinned scroll scenes. Both are progressive-enhancement-safe: content is fully visible by default and the effect applies only where `animation-timeline` is supported and the user has not requested reduced motion (a `@media (prefers-reduced-motion: reduce)` + `@supports` fallback). Composes with the opt-in `@hirobius/design-system/scroll` primitives (`SmoothScroll`, `useScrollProgress`) for the full "pinned scale-reveal" hero effect on our own stack — no GSAP, no Lenis required (Lenis remains an optional add-on for momentum). See ADR-021.
719
+ - b1057ac: Add the `@hirobius/design-system/scroll` subpath (#116): opt-in scroll-motion primitives. `SmoothScroll` is a Lenis momentum-scroll provider (reduced-motion-first — skips Lenis entirely when the user prefers reduced motion — and SSR-safe), `useLenis` re-exports Lenis's React hook, and `useScrollProgress` is a Motion-based `0→1` scroll-progress hook (no new dependency; works with or without `SmoothScroll`). `lenis` is an **optional peer dependency** (`pnpm add lenis` to use `SmoothScroll`) and is externalized from the build, so it never lands in the main barrel and consumers who don't import `/scroll` pay nothing. Refines ADR-021: GSAP stays out of the library; Lenis is in as an opt-in subpath rather than downstream-only.
720
+ - 4420596: Variant contract (#60, Phase 1): establish the standard `cva` axis vocabulary — `variant` (structural), `tone` (`neutral | danger | success | warning | info`), `size` (`sm | md | lg`), `density` (`comfortable | compact`) — documented in `docs/architecture/variant-contract.md` and enforced by the extended `check-prop-vocabulary` gate (new tone/density allowlist rules). Converts the first reference batch to the contract: `Tag`, `Divider`, `Stat`, `Field`, `AvatarGroup`, `CircularProgress`.
721
+
722
+ Breaking (value rename): `Stat` and `Field` rename their `tone="default"` value to `tone="neutral"` to align with the fixed tone set — pass `neutral`, or omit it (it remains the default). `Divider` gains a `variant="default" | "strong"` axis; the existing `strong` boolean is retained as deprecated back-compat and still works.
723
+
724
+ - cca9ba0: Variant contract (#60, Phase 3): convert className-based composites/overlays to `cva`. New cva adopters: `Card`, `Breadcrumb`, `Carousel`, `CommandPalette` (coverage: 35/123, was 31/123). `AlertDialog`, `Dialog`, `Tabs`, `Menu`, `ContextMenu`, `HoverCard`, `Popover`, `HdsTooltip` were reviewed and have no prop-driven className branching to formalize (their Radix `data-[state=…]` styling already lives directly in a single class string, contract-compliant as-is) — skipped, nothing to convert. `AspectRatio` has no className surface at all (thin Radix passthrough). The legacy `Tooltip` (image-expand pill) is CSS-in-JS/`framer-motion`-driven — flagged for a future inline-style→Tailwind migration before it can adopt `cva`. `ToggleButton` was already cva-converted in an earlier phase.
725
+
726
+ Breaking (value rename): `Card`'s single `tone` prop is now two axes — `variant` (`'default' | 'accent'`, the former structural values) and `tone` (fixed `neutral | danger | success | warning | info`). Migrate:
727
+ - `<Card tone="default">` → `<Card variant="default">` (or omit both — same default)
728
+ - `<Card tone="accent">` → `<Card variant="accent">`
729
+ - `<Card tone="success" | "warning" | "danger">` → unchanged (still valid on `tone`)
730
+
731
+ `Card.Progress` and `Card.Metric`'s `tone` prop drops the `'accent'` value (no known internal consumers) and renames `'default'` → `'neutral'` (same default rendering — content-accent fill for Progress, content-primary color for Metric). Both gain a new `'info'` tone value for fixed-vocab completeness.
732
+
733
+ ### Patch Changes
734
+
735
+ - 157990d: Variant contract (#60): migrate `HeadingStack` from CSS-in-JS (inline `style` + a `LEVEL_STYLES` `CSSProperties` map) to Tailwind + `cva`, formalizing `level` (`heading1 | heading2 | heading3`) as a cva variant and `gap` (`px4 | px8`) as a second, layout-axis variant (mirroring `Divider`'s `orientation`). Every emitted class binds the exact same `--semantic-typography-*`/`--semantic-color-content-*`/`--primitive-space-*` custom property the removed inline styles referenced — zero visual change. Public prop API, defaults, and tag overrides (`as`/`headingAs`/`subheadingAs`) are unchanged; non-breaking.
736
+ - f86ab1a: CSS-in-JS → Tailwind (#60): migrate `SideNav` to Tailwind + `cva` (`sideNavVariants`), formalizing the `level` (`root` | `nested`) axis as a cva variant alongside the existing rest/hover/focus/active/disabled `state` matrix. Replaces the inline `style` object and the removed `BG`/`TEXT` token maps with the same `--semantic-*`/`--component-*`/`--primitive-*` custom properties, bound 1:1.
737
+
738
+ The `useFrozenState` (Storybook demo-state freeze) override is preserved exactly, including its pre-existing quirks: only `hover`/`active`/`disabled`/`pressed`/`press` freeze values are recognized (unlike `NavItem`, `focused`/`rest` fall through to live interaction state), and a frozen `pressed` state shares `active`'s visual tokens but does not set `aria-current="page"` the way a frozen `active` state does. Resolved state is now exposed via `data-state`/`data-level` attributes for testability, mirroring `nav-item.tsx`'s established pattern (ADR-015). Modality-aware focus-visible detection now goes through the shared `useFocusVisible` hook instead of a duplicated inline implementation.
739
+
740
+ No breaking changes: the public prop API, defaults, RTL-aware indent math, the titleLabel/button/anchor branching, and the 44px minimum interactive size are all unchanged.
741
+
742
+ - 4c72ebc: CSS-in-JS → Tailwind (#60): migrate `DocLinkCard` to Tailwind + `cva` (`docLinkCardVariants`), formalizing the `variant` (`feature` | `pager`) axis. Inline `style` objects for layout (padding, gap, flex direction, absolute positioning, margins) are replaced with Tailwind classes bound to the same `--semantic-space-*` custom properties; typography composites (`heading3`/`ui`/`caption`) stay inline per the existing HDS pattern, with color/margin moved onto the shared `text-primary`/`text-secondary` utility classes. RTL (`isRtl`) and directional-affordance logic are preserved exactly — the pager icon/title left-right decision is now a single derived boolean equivalent to the prior nested ternaries. `motion/react` icon-nudge animations (`headerIconControls` / `pagerIconControls`) are untouched.
743
+
744
+ No breaking changes: all public props, defaults, and rendered DOM/ARIA structure are unchanged. `docLinkCardVariants` is an additive `@internal` export (compose via `DocLinkCard` props instead, per the variant-contract convention).
745
+
746
+ ## 0.11.0
747
+
748
+ ### Minor Changes
749
+
750
+ - 39e5e4d: Autonomous issue sweep — additive components, tokens, and tooling (non-breaking):
751
+ - **HdsTooltip** (#69): new accessible hover/focus tooltip on Radix Tooltip — collision-aware positioning, ARIA, keyboard, delay; inverse-surface skin with arrow.
752
+ - **HdsThemeProvider + `useHdsTheme`** (#61): framework-agnostic theming contract over the four `data-*`/CSS-var dials (theme, density, brand, font), zero-JS compatible.
753
+ - **Textarea** (#73): new multi-line text-field primitive mirroring Input's token skin, with label/helper/error slots and Default/Focus/Error/Disabled states.
754
+ - **`semantic.typography.caption`** (#71): new 12/16 caption type composite in the token pipeline; `hds.typeStyles.caption` now resolves to it instead of aliasing `ui`.
755
+ - **Alert** (#72): lighter icon stroke (2 → 1.5) and title alignment nudge to match the Figma spec.
756
+ - Tooling (not shipped in the bundle): `pnpm hds:new` one-command component scaffold (#63) and a `design.md` → brand-overlay harvester (#67); source hygiene and suppression triage (#57).
757
+
758
+ ## 0.10.0
759
+
760
+ ### Minor Changes
761
+
762
+ - d371372: **BREAKING:** remove the `./protocol` subpath export. It carried the signed
763
+ WebSocket envelope for the legacy hand-rolled Figma bridge, which has been
764
+ archived (`archive/figma-bridge` branch) in favor of the official Figma MCP
765
+ server + Code Connect (ADR-019). No consumer app imports it — it existed only
766
+ to share the envelope with the custom Figma plugin. All other exports
767
+ (components, stylesheets, `tokens`, `cn`, `manifest`, `contexts`, `form`,
768
+ `mui`) are unchanged. If you did import `@hirobius/design-system/protocol`,
769
+ pin `<0.10.0` or vendor `protocol/envelope.mjs` from the archive branch.
770
+
771
+ ## 0.9.0
772
+
773
+ ### Minor Changes
774
+
775
+ - 27e293a: Add a reset-free component stylesheet on a new subpath:
776
+ `@hirobius/design-system/styles.css`. It ships the full component styling
777
+ (design tokens + Tailwind utilities + embedded fonts) but **no global reset** —
778
+ HDS's own base styles are scoped to `[data-hds]`, so importing `styles.css`
779
+ styles every HDS component and changes **zero** host-element styles (`*`,
780
+ `html`, `body`, headings, `button`, `a`, form controls are untouched). This is
781
+ the clean way to run HDS alongside MUI `<CssBaseline>` / Emotion or any host with
782
+ its own global CSS.
783
+
784
+ `tokens.css` is unchanged (batteries-included: `styles.css` **plus** the global
785
+ Tailwind preflight) and remains the default for HDS-first apps. A scoped
786
+ box-model reset was added to the `[data-hds]` base so components keep their
787
+ border-box model without the global preflight. The `smoke:consumer` gate now
788
+ asserts `styles.css` carries components/utilities/fonts and no global reset.
789
+
790
+ - 27e293a: Add a semantic **`tone`** axis to `Button` (`neutral | danger | success |
791
+ warning | info`), driven by the feedback tokens, so destructive and status
792
+ actions read as first-class buttons instead of falling back to a host framework:
793
+ `<Button tone="danger">Delete</Button>`. Tones apply a token-driven tonal fill
794
+ (tinted surface + matching feedback text) that clears WCAG AA in **both** light
795
+ and dark, composes with any `variant`, and defaults to `neutral` (no change to
796
+ existing buttons). `Badge` gains the matching `inProgress` tone, completing its
797
+ status set. Backed by named `feedback-*` Tailwind utilities (no arbitrary color
798
+ values).
799
+ - 27e293a: Add an **in-progress / secondary** semantic feedback hue and gate the feedback
800
+ palette for accessibility. New `--semantic-color-feedback-inProgress` (violet:
801
+ `#6d28d9` light / `#a78bfa` dark) plus its tinted-surface pair
802
+ `--semantic-color-feedback-bg-inProgress`, backed by a new `violet` primitive
803
+ ramp. This completes the status palette (success / warning / info / error /
804
+ in-progress) so consumers can theme status UI — Saved / Applied / Interviewing /
805
+ Offer / Rejected — from HDS tokens alone, with no local status hex.
806
+
807
+ `scripts/check-contrast.mjs` now asserts every hex feedback token clears WCAG AA
808
+ for small text on **both** the page and card (`raised`) surfaces, in light and
809
+ dark. `docs/CONSUMING.md` publishes the feedback token names for downstream
810
+ mapping.
811
+
812
+ - 27e293a: Add an optional Material UI theme preset on a new subpath:
813
+ `@hirobius/design-system/mui`. `hdsMuiThemeOptions()` returns an MUI
814
+ `ThemeOptions`-shaped object whose palette is wired to HDS token CSS variables
815
+ (`error`/`warning`/`info`/`success` map to the same feedback tokens as
816
+ `<Button tone>` / `<Badge tone>`), so MUI-based apps theme from HDS without a
817
+ local token-copy step. It imports no MUI code (structural return type), so it
818
+ adds zero weight and no peer requirement unless imported. Designed for MUI v6+
819
+ CSS-variables mode: `createTheme({ cssVariables: true, ...hdsMuiThemeOptions() })`.
820
+
821
+ ## 0.8.1
822
+
823
+ ### Patch Changes
824
+
825
+ - ab78052: Publish to the **public npm registry** instead of GitHub Packages. Consumers now
826
+ install with a plain `npm install @hirobius/design-system` — no `.npmrc`, no auth
827
+ token, and no registry configuration. The package `exports`, build output, styles,
828
+ and API are unchanged; only the distribution target moved.
829
+
830
+ ## 0.8.0
831
+
832
+ ### Minor Changes
833
+
834
+ - 3f48fc9: Ship built TypeScript declarations instead of source, so the package typechecks
835
+ cleanly in a consumer.
836
+ - `types` and every `exports[*].types` now point at emitted `dist/types/**/*.d.ts`
837
+ (via a new `build:types` step using `tsconfig.dts.json`), not raw `.ts`/`.tsx`
838
+ source. Consumers no longer compile our source, so the prior errors
839
+ (`import.meta.env`, `Cannot find module './lab/tokenUtils'`/`'./Token.module.css'`)
840
+ are gone — under `skipLibCheck`, `.d.ts` are skipped.
841
+ - Source is no longer published (`files` drops `src`); the tarball ships only
842
+ `dist/` (+ protocol, tokens json, manifest). Nothing published references a
843
+ pruned path.
844
+ - The `manifest` and raw-`tokens` exports are typed (`SystemManifest` / inlined
845
+ DTCG shape) so the emitted `.d.ts` is self-contained (no JSON-path imports);
846
+ CSS side-effect imports are stripped from `.d.ts`.
847
+ - Verified: a Vite-style consumer (strict, `skipLibCheck`, bundler resolution)
848
+ can `import { Button } from '@hirobius/design-system'` (and from subpaths) and
849
+ pass `tsc --noEmit` with only react/react-dom installed. `smoke:consumer` now
850
+ gates on that consumer typecheck plus `publint`.
851
+
852
+ - 2c8ca1c: Host-safe CSS + verified consumer build (packaging task C-partial / D / E).
853
+ - **New `@hirobius/design-system/variables.css`** — design tokens as CSS custom
854
+ properties ONLY (no Tailwind preflight, no `@layer base` reset, no utilities).
855
+ Importing it cannot restyle a host app's elements, so it's the safe path for
856
+ embedding HDS tokens inside MUI/another design system. The full `tokens.css`
857
+ is unchanged (greenfield).
858
+ - **`sideEffects`** tightened to `["**/*.css"]` (source no longer ships), so
859
+ bundlers tree-shake unused JS exports while keeping CSS side-effectful.
860
+ - **`smoke:consumer` now also runs a consumer `vite build`** of a `Button`-only
861
+ app with NONE of `react-router`/`react-hook-form`/`zod`/`@hookform/resolvers`
862
+ installed — proving leaf imports don't drag in the router/form stack (they're
863
+ optional peers, absent from the main bundle graph). Plus the consumer
864
+ `tsc --noEmit` + `publint` gates added previously.
865
+ - **docs/CONSUMING.md** documents the vars-only path for MUI coexistence and the
866
+ verified "leaf imports stay light" guarantee.
867
+
868
+ Note: scoping the full stylesheet's global Tailwind **preflight** reset is a
869
+ tracked follow-up (needs visual-regression verification); `variables.css` is the
870
+ host-safe path in the meantime.
871
+
872
+ ## 0.7.0
873
+
874
+ ### Minor Changes
875
+
876
+ - a37eef7: Bundle web fonts into the package. `@hirobius/design-system/tokens.css` now embeds the Satoshi, Clash Display, and Geist Mono `woff2` files directly (base64), so a fresh consumer importing `tokens.css` renders the real typefaces with zero extra setup — no need to copy font files into a web root. Self-contained at the cost of a larger `tokens.css`.
877
+ - 06df9d6: Add **HdsCheckbox** — a custom-drawn checkbox primitive with `checked`, `indeterminate` (aria-checked="mixed" + DOM property), disabled, and label support, on the shared interaction-state seam.
878
+
879
+ Also export the existing input-family primitives that were built but missing from the public barrel: **HdsToggle**, **HdsRadio**, and **HdsSlider**. Consumers can now import the full form-control set (`Input`, `Field`, `Select`, `SegmentedControl`, `HdsCheckbox`, `HdsToggle`, `HdsRadio`, `HdsSlider`) from the package root.
880
+
881
+ - 76aed0a: Add **Combobox** — a searchable single-select built on the HDS Popover. The trigger shows the current selection; opening reveals a search field that filters the option list. Full keyboard support (↑/↓ to move, Enter to choose, Esc/outside-click to close), listbox/option ARIA, and controlled `value`/`onChange`. Closes the autocomplete gap in the form-control family.
882
+ - 9223f6a: Add **Form** + **FormField** — a validation-agnostic form seam. `Form` is a styled `<form>` with consistent field rhythm; `FormField` owns the a11y wiring (label↔control association, `aria-invalid`, `aria-describedby` for description + error, required marker) and accepts a plain `error` string. HDS takes **no** form/validation dependency — the validation source is whatever the consumer brings (native constraint validation, react-hook-form, zod, Formik). Mirrors the router-adapter philosophy: works with zero deps, richer when you inject your own. (The `Input` primitive already self-wires its label/error, so use its props directly; `FormField` is for controls that don't, e.g. native inputs, `Select`, `Combobox`, checkboxes.)
883
+ - 9adfe22: Add an optional React Hook Form + Zod form adapter on a new subpath:
884
+ `@hirobius/design-system/form`. It exports `useHdsForm(schema)` (RHF's `useForm`
885
+ pre-wired with a Zod resolver and `onTouched` validation), `HdsForm` (wraps the
886
+ presentational `Form` in RHF's `FormProvider` and routes submit through
887
+ `handleSubmit` + `noValidate`), and `HdsFormField` (a render-prop that binds a
888
+ control to RHF by `name` and surfaces the field's Zod error through the existing
889
+ HDS label/error/aria markup).
890
+
891
+ `react-hook-form`, `zod`, and `@hookform/resolvers` are **optional** peer
892
+ dependencies — only apps importing this subpath pull them in, so the main barrel
893
+ stays validation-agnostic and zero-dependency. The core `form` module also now
894
+ exports `FormFieldShell` + `useFieldWiring` (a non-cloning field-markup shell and
895
+ the shared id/aria computation) for controls that manage their own callback ref.
896
+
897
+ - 8b3fe5b: Add four presentational primitives that consumers hit immediately: **Spinner** (indeterminate loading indicator), **Skeleton** (content-loading shimmer, `text`/`rectangular`/`circular`), **Progress** (linear determinate/indeterminate bar), and **Avatar** (image with initials fallback). All token-driven, accessible (`role="status"`/`progressbar"`/`img`), and honor `prefers-reduced-motion`.
898
+ - f444c6e: Add **Menu** — a Radix-backed dropdown-menu primitive (compound parts: `Menu`, `Menu.Trigger`, `Menu.Content`, `Menu.Item`, `Menu.CheckboxItem`, `Menu.RadioGroup`/`RadioItem`, `Menu.Label`, `Menu.Separator`, `Menu.Group`, `Menu.Sub`/`SubTrigger`/`SubContent`) themed with the overlay role tokens to match Dialog/Popover. Roving focus, type-ahead, checkbox/radio items, submenus, and dismissal come from Radix. Promotes the dropdown that previously lived privately inside the theme-toggle into a public component (no new dependency — `@radix-ui/react-dropdown-menu` was already present).
899
+ - 8529e25: Add **Popover** — a Radix-backed floating-surface primitive (compound parts: `Popover`, `Popover.Trigger`, `Popover.Anchor`, `Popover.Content`, `Popover.Close`) themed with the overlay role tokens to match Dialog. Collision-aware positioning, outside-click + ESC dismissal, focus management, and portal mounting out of the box. Adds `@radix-ui/react-popover` as a dependency.
900
+ - 93171e1: Components no longer require react-router. Navigation is sourced from an injectable adapter (`RouterContext`): by default links render as plain anchors and navigation falls back to `window.location`, so HDS works with zero router. react-router / Next.js consumers inject their router once at the app root via `<HdsRouterProvider adapter={...}>`. `react-router` is now an optional peer dependency.
901
+ - 45fe9ab: Scope the design-system base styles (element resets, body/heading type baseline, theme transition) to a `[data-hds]` subtree so HDS can drop into a section of a non-HDS app (e.g. one running MUI `<CssBaseline>`) without competing resets or font-cascade fights. Namespaced token custom properties stay on `:root` (harmless).
902
+
903
+ **Migration:** add `data-hds` to your app root (or the section that hosts HDS) so the base styles apply — e.g. `<html data-hds>` or `<div data-hds>…</div>`. Without it, components still receive their token-driven styling but the global type baseline/resets won't apply. See `docs/adr/016-scoped-base-styles.md`. Tailwind preflight remains global for now (documented follow-up).
904
+
905
+ - 01cdcd9: Add **Toast** — transient feedback notifications backed by Radix Toast. Wrap the app once in `<ToastProvider>`, then call `useToast().toast({ title, description, tone })` imperatively from anywhere. Tones (`neutral`/`info`/`success`/`danger`/`warning`) tint the leading icon via the feedback tokens; auto-dismiss, swipe-to-dismiss, the a11y live region, and the viewport portal come from Radix. Adds `@radix-ui/react-toast`.
906
+
907
+ ## 0.6.0
908
+
909
+ ### Minor Changes
910
+
911
+ - 82fbcf0: Alert: unify the feedback prop with Badge/Card/Callout. `variant` is renamed to
912
+ `tone`, and the destructive value `"error"` is renamed to `"danger"` (the
913
+ feedback red is `danger` everywhere now). The token CSS variables are unchanged.
914
+
915
+ Migration: `pnpm codemod -t codemods/alert-tone-to-danger.cjs <path>`
916
+
917
+ - 82fbcf0: Button / IconButton: remove the deprecated `isDark` prop. Button chrome is
918
+ theme-aware via CSS variables, so the prop was a no-op. Remove it from call
919
+ sites.
920
+
921
+ Migration: `pnpm codemod -t codemods/remove-button-isdark.cjs <path>`
922
+
923
+ - 82fbcf0: SegmentedControl: move `size` onto the shared `sm | md | lg` ramp used by
924
+ Button/Input/IconButton. `size="default"` → `size="md"`, `size="compact"` →
925
+ `size="sm"`. Rendering is unchanged.
926
+
927
+ Migration: `pnpm codemod -t codemods/segmentedcontrol-size.cjs <path>`
928
+
929
+ ## 0.5.0
930
+
931
+ ### Minor Changes
932
+
933
+ - 23cfdae: Make the package publishable and consumable.
934
+ - Publish target set to GitHub Packages (`publishConfig.registry`); `private`
935
+ removed so `npm publish` is allowed.
936
+ - Declare `react`, `react-dom`, and `react-router` as peer dependencies so
937
+ consumers provide a single copy (no duplicate-React hook errors).
938
+ - Wire up Changesets (`changeset add` / `version` / `release`) and a CI release
939
+ workflow that publishes on merge to `main`.
940
+
941
+ - 390a877: Harden and slim the consumable package surface.
942
+
943
+ **Packaging**
944
+ - Cut the published tarball from ~49 MB / ~400 files to ~0.55 MB / ~207 files:
945
+ the library build no longer copies the 47 MB `public/` tree (portfolio PNGs,
946
+ fonts, JSON) into the package (`publicDir: false`), no longer ships sourcemaps
947
+ (`sourcemap: false`), and no longer drags the whole component-preview universe
948
+ (every component via `import.meta.glob`, the 3D mobius-scene chunk, the lab
949
+ modules, the component-api/token-audit artifacts) into the bundle.
950
+ - Excluded demo/lab/3D source (`src/stories`, `src/app/components/lab`, the
951
+ mobius/shaders modules) from the package `files`.
952
+
953
+ **Consumer resolution (fixes latent breakage in 0.4.0/0.4.1)**
954
+ - Externalize `motion/react` correctly so the bundle imports it (resolved via the
955
+ `motion` dependency) instead of emitting a bare `framer-motion` import — a
956
+ package that was never a dependency, which broke every consumer of a
957
+ motion-based component (tooltip, alert, disclosure, …).
958
+ - Demote app-only runtime deps (`three`, `@react-three/*`, `postprocessing`,
959
+ `express`, `cors`, `fuse.js`, `zustand`) from `dependencies` to
960
+ `devDependencies`; consumers no longer transitively install the three.js
961
+ ecosystem.
962
+
963
+ **API surface**
964
+ - Added subpath export `@hirobius/design-system/protocol` (bridge envelope).
965
+ - BREAKING: removed `ComponentDocPage` and `SpecimenBlock` from the main barrel
966
+ — they are docs-shell renderers, not consumable primitives. Import them from
967
+ the in-repo doc site directly if needed.
968
+
969
+ _Releases before 0.5.0 are in git history._