@hirobius/design-system 0.19.1 → 0.20.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 (142) hide show
  1. package/CONSUMING.md +9 -6
  2. package/DESIGN.md +7 -5
  3. package/README.md +14 -11
  4. package/codemods/hds-prefix.mjs +231 -0
  5. package/codemods/jsx-fold.mjs +515 -0
  6. package/codemods/not-found-pattern.mjs +71 -0
  7. package/codemods/patterns-subpath.mjs +242 -81
  8. package/codemods/patterns-subpath.names.json +41 -15
  9. package/codemods/removed-0.20.json +194 -0
  10. package/codemods/tile-grid.mjs +283 -0
  11. package/codemods/unrewritable.mjs +291 -0
  12. package/dist/chunks/DemoStateContext-Cz5TmrYu.js +32 -0
  13. package/dist/chunks/empty-state-BBqhCx0S.js +1217 -0
  14. package/dist/contexts.js +157 -78
  15. package/dist/hirobius-ui.js +1517 -5270
  16. package/dist/manifest.js +12 -13
  17. package/dist/patterns.js +780 -513
  18. package/dist/styles.css +1 -1
  19. package/dist/tokens.css +1 -1
  20. package/dist/tokens.js +29 -8
  21. package/dist/types/src/app/components/avatar-group.d.ts +1 -2
  22. package/dist/types/src/app/components/badge.d.ts +8 -3
  23. package/dist/types/src/app/components/blockquote.d.ts +1 -2
  24. package/dist/types/src/app/components/box-sx.d.ts +110 -13
  25. package/dist/types/src/app/components/box.d.ts +6 -3
  26. package/dist/types/src/app/components/breadcrumb.d.ts +0 -5
  27. package/dist/types/src/app/components/button.d.ts +33 -4
  28. package/dist/types/src/app/components/card.d.ts +7 -8
  29. package/dist/types/src/app/components/combobox.d.ts +47 -11
  30. package/dist/types/src/app/components/container.d.ts +2 -2
  31. package/dist/types/src/app/components/data-table-section.d.ts +1 -1
  32. package/dist/types/src/app/components/dialog.d.ts +7 -1
  33. package/dist/types/src/app/components/disclosure.d.ts +1 -5
  34. package/dist/types/src/app/components/field.d.ts +1 -2
  35. package/dist/types/src/app/components/grid.d.ts +10 -6
  36. package/dist/types/src/app/components/hds-tooltip.d.ts +12 -2
  37. package/dist/types/src/app/components/inline-code.d.ts +1 -2
  38. package/dist/types/src/app/components/input.d.ts +8 -5
  39. package/dist/types/src/app/components/kbd.d.ts +1 -2
  40. package/dist/types/src/app/components/metadata-list.d.ts +1 -2
  41. package/dist/types/src/app/components/page-header.d.ts +0 -2
  42. package/dist/types/src/app/components/popover.d.ts +8 -0
  43. package/dist/types/src/app/components/progress.d.ts +33 -6
  44. package/dist/types/src/app/components/segmented-control.d.ts +2 -26
  45. package/dist/types/src/app/components/select.d.ts +10 -1
  46. package/dist/types/src/app/components/skeleton.d.ts +2 -3
  47. package/dist/types/src/app/components/spinner.d.ts +1 -2
  48. package/dist/types/src/app/components/stack.d.ts +4 -1
  49. package/dist/types/src/app/components/stat.d.ts +1 -2
  50. package/dist/types/src/app/components/status-dot.d.ts +18 -4
  51. package/dist/types/src/app/components/status-list-item.d.ts +1 -2
  52. package/dist/types/src/app/components/surface.d.ts +2 -3
  53. package/dist/types/src/app/components/switcher.d.ts +1 -1
  54. package/dist/types/src/app/components/table.d.ts +1 -15
  55. package/dist/types/src/app/components/tag.d.ts +0 -6
  56. package/dist/types/src/app/components/text.d.ts +1 -2
  57. package/dist/types/src/app/components/textarea.d.ts +1 -2
  58. package/dist/types/src/app/components/toast.d.ts +1 -2
  59. package/dist/types/src/app/components/tooltip.d.ts +1 -1
  60. package/dist/types/src/app/context/DemoStateContext.d.ts +1 -1
  61. package/dist/types/src/app/context/RouterContext.d.ts +5 -5
  62. package/dist/types/src/app/data/manifest-types.d.ts +6 -5
  63. package/dist/types/src/app/design-system/generated-token-refs.d.ts +8 -0
  64. package/dist/types/src/app/design-system/generated-token-values.d.ts +1 -0
  65. package/dist/types/src/app/design-system/tokens.d.ts +22 -3
  66. package/dist/types/src/icons.d.ts +1 -1
  67. package/dist/types/src/index.d.ts +33 -124
  68. package/dist/types/src/patterns.d.ts +20 -25
  69. package/dist/variables.css +6 -0
  70. package/docs/CONSUMING.md +21 -13
  71. package/hirobius.tokens.json +48 -15
  72. package/llms.txt +19 -20
  73. package/package.json +17 -22
  74. package/public/hds-manifest.json +2405 -6537
  75. package/public/llms/components.txt +41 -423
  76. package/public/llms/layout.txt +4 -4
  77. package/public/llms-full.txt +67 -448
  78. package/public/llms.txt +19 -20
  79. package/src/app/data/component-api.json +1379 -3271
  80. package/dist/chunks/LanguageContext-BxdCwiFg.js +0 -95
  81. package/dist/chunks/TokenDisplayContext-DKYJMVWk.js +0 -26
  82. package/dist/chunks/activity-feed-BGQ6v3wc.js +0 -22040
  83. package/dist/types/src/app/components/activity-feed.d.ts +0 -73
  84. package/dist/types/src/app/components/app-shell.d.ts +0 -40
  85. package/dist/types/src/app/components/aspect-ratio.d.ts +0 -25
  86. package/dist/types/src/app/components/bleed.d.ts +0 -39
  87. package/dist/types/src/app/components/button-group.d.ts +0 -22
  88. package/dist/types/src/app/components/calendar.d.ts +0 -22
  89. package/dist/types/src/app/components/carousel.d.ts +0 -26
  90. package/dist/types/src/app/components/center.d.ts +0 -36
  91. package/dist/types/src/app/components/cinematic-link.d.ts +0 -18
  92. package/dist/types/src/app/components/circular-progress.d.ts +0 -28
  93. package/dist/types/src/app/components/cluster.d.ts +0 -39
  94. package/dist/types/src/app/components/command-palette.d.ts +0 -46
  95. package/dist/types/src/app/components/component-instance-matrix.d.ts +0 -27
  96. package/dist/types/src/app/components/context-menu.d.ts +0 -78
  97. package/dist/types/src/app/components/cover.d.ts +0 -43
  98. package/dist/types/src/app/components/date-input.d.ts +0 -31
  99. package/dist/types/src/app/components/date-range-input.d.ts +0 -34
  100. package/dist/types/src/app/components/date-time-input.d.ts +0 -30
  101. package/dist/types/src/app/components/doc-link-card.d.ts +0 -32
  102. package/dist/types/src/app/components/error-boundary.d.ts +0 -25
  103. package/dist/types/src/app/components/file-input.d.ts +0 -30
  104. package/dist/types/src/app/components/foundation-swatch.d.ts +0 -45
  105. package/dist/types/src/app/components/frame.d.ts +0 -38
  106. package/dist/types/src/app/components/heading-stack.d.ts +0 -52
  107. package/dist/types/src/app/components/history-card.d.ts +0 -12
  108. package/dist/types/src/app/components/hover-card.d.ts +0 -46
  109. package/dist/types/src/app/components/icon-button.d.ts +0 -22
  110. package/dist/types/src/app/components/image-lightbox.d.ts +0 -14
  111. package/dist/types/src/app/components/input-group.d.ts +0 -23
  112. package/dist/types/src/app/components/multi-selector.d.ts +0 -40
  113. package/dist/types/src/app/components/nav-group.d.ts +0 -29
  114. package/dist/types/src/app/components/nav-item.d.ts +0 -33
  115. package/dist/types/src/app/components/not-found-pattern.d.ts +0 -1
  116. package/dist/types/src/app/components/overflow-list.d.ts +0 -21
  117. package/dist/types/src/app/components/selectable-card.d.ts +0 -29
  118. package/dist/types/src/app/components/side-nav.d.ts +0 -23
  119. package/dist/types/src/app/components/sketch.d.ts +0 -19
  120. package/dist/types/src/app/components/stacked-card-rail.d.ts +0 -32
  121. package/dist/types/src/app/components/stepper-field.d.ts +0 -12
  122. package/dist/types/src/app/components/stepper.d.ts +0 -33
  123. package/dist/types/src/app/components/text-lockup.d.ts +0 -38
  124. package/dist/types/src/app/components/tile-grid.d.ts +0 -17
  125. package/dist/types/src/app/components/time-input.d.ts +0 -24
  126. package/dist/types/src/app/components/toggle-button.d.ts +0 -32
  127. package/dist/types/src/app/components/token.d.ts +0 -90
  128. package/dist/types/src/app/components/tokenUtils.d.ts +0 -38
  129. package/dist/types/src/app/components/tokenizer.d.ts +0 -24
  130. package/dist/types/src/app/components/toolbar.d.ts +0 -36
  131. package/dist/types/src/app/components/top-nav.d.ts +0 -23
  132. package/dist/types/src/app/components/tree-list.d.ts +0 -30
  133. package/dist/types/src/app/data/nav-model.d.ts +0 -39
  134. package/dist/types/src/app/hooks/useFocusVisible.d.ts +0 -9
  135. package/dist/types/src/app/layouts/CaseStudyLayout.d.ts +0 -20
  136. package/dist/types/src/app/layouts/DocLayout.d.ts +0 -14
  137. package/dist/types/src/app/layouts/EmbeddedDocLayoutContext.d.ts +0 -6
  138. package/dist/types/src/app/layouts/HdsDocsShell.d.ts +0 -72
  139. package/dist/types/src/app/layouts/HdsSystemDocLayout.d.ts +0 -14
  140. package/dist/types/src/app/lib/hds-search.d.ts +0 -45
  141. package/dist/types/src/app/lib/nav-active.d.ts +0 -16
  142. package/dist/types/src/app/lib/navLevels.d.ts +0 -4
package/CONSUMING.md CHANGED
@@ -43,7 +43,7 @@ Available subpaths:
43
43
 
44
44
  | Import | What |
45
45
  | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46
- | `@hirobius/design-system` | All components (primitives, patterns, templates) |
46
+ | `@hirobius/design-system` | The core components and templates (the `pattern` tier is only in `/patterns`) |
47
47
  | `@hirobius/design-system/tokens.css` | Token CSS variables (import once at app root) |
48
48
  | `@hirobius/design-system/tokens` | Token values as TS constants |
49
49
  | `@hirobius/design-system/cn` | `cn()` class-merge helper |
@@ -51,8 +51,8 @@ Available subpaths:
51
51
  | `@hirobius/design-system/contexts` | Theme / language / tenant / font providers |
52
52
  | `@hirobius/design-system/brand` | Palette → HDS-semantic overlay bridge (static / SSR / Astro; see [`docs/CONSUMING.md` §12](docs/CONSUMING.md#12-static-astro-sites--the-brand-overlay-bridge)) |
53
53
  | `@hirobius/design-system/scroll` | Opt-in scroll-motion primitives — `SmoothScroll` (Lenis), `useScrollProgress` (Motion). Requires the optional peer `lenis`. See [`docs/CONSUMING.md` §13](docs/CONSUMING.md#13-scroll-motion-the-scroll-subpath) |
54
- | `@hirobius/design-system/patterns` | The 22 `pattern`-tier components (nav shells, feeds, rails, pickers — see hds#254). Also still re-exported from the root for one minor (`@deprecated`, removed at the next major) — prefer this subpath in new code. |
55
- | `@hirobius/design-system/icons` | Curated Lucide icon set for `IconButton` / `Icon` (`Ellipsis`, `Pencil`, `Trash2`, `X`, …). Names listed in the manifest `iconSet`. |
54
+ | `@hirobius/design-system/patterns` | The `pattern`-tier components (nav shells, feeds, rails, pickers — see hds#254). The only entry that exports them: 0.20.0 removed their root re-exports (the `hds-patterns-subpath` codemod, see MIGRATIONS.md). |
55
+ | `@hirobius/design-system/icons` | Curated Lucide icon set for `Icon`, and so for `Button iconOnly` (`Ellipsis`, `Pencil`, `Trash2`, `X`, …). Names listed in the manifest `iconSet`. |
56
56
  | `@hirobius/design-system/static.css` | CSS-only static-primitive layer — `.hds-badge`/`.hds-card`/`.hds-alert`/`.hds-divider`/`.hds-tag` classes, no React. See [`docs/CONSUMING.md` §14](docs/CONSUMING.md#14-css-only-static-primitives--badgecardalertdividertag-with-no-react) |
57
57
 
58
58
  The package is **ESM-only**, so consume it with a modern bundler (Vite, Next.js,
@@ -113,8 +113,11 @@ Releases follow [semver](https://semver.org/) and are tracked in
113
113
  pnpm update @hirobius/design-system # latest within your version range
114
114
  ```
115
115
 
116
- For a major (breaking) release, bump the version explicitly and review the
117
- CHANGELOG entry. Token or export changes are released as majors.
116
+ For a breaking release, bump the version explicitly and review the CHANGELOG
117
+ entry. Before 1.0 a breaking change (a removed export or token) ships in a 0.x
118
+ minor, which a caret range such as `^0.19` never picks up; from 1.0 on it ships
119
+ in a major. Each removal is listed in [MIGRATIONS.md](MIGRATIONS.md) with its
120
+ replacement and, where one applies, a codemod (ADR-014).
118
121
 
119
122
  ---
120
123
 
@@ -122,7 +125,7 @@ CHANGELOG entry. Token or export changes are released as majors.
122
125
 
123
126
  ```bash
124
127
  pnpm changeset add # record a patch/minor/major bump + notes
125
- pnpm changeset:version # apply bumps + regenerate CHANGELOG.md
128
+ pnpm changeset:version # apply bumps + regenerate CHANGELOG.md + refresh docs/api/api-baseline.json
126
129
  # commit + push to main → the Release workflow publishes to public npm
127
130
  ```
128
131
 
package/DESIGN.md CHANGED
@@ -90,7 +90,7 @@ Depth is communicated through 4 elevation roles bundled by `semantic.elevation.*
90
90
 
91
91
  Cards default to `flat`. They lift to `raised` only on interactive hover or when explicitly elevated above siblings. Never combine `raised` with a border — depth is one mechanism (border OR shadow), not both stacked.
92
92
 
93
- Overlays (Dialog, AlertDialog, Menu, ContextMenu, Popover, Select, HoverCard, Tooltip) portal into the nearest `data-hds` scope, so they inherit its theme (for example `<div data-hds data-theme="dark">`); pass `container` on the Content part to override.
93
+ Overlays (Dialog, AlertDialog, Menu, Popover, Select, Tooltip) portal into the nearest `data-hds` scope, so they inherit its theme (for example `<div data-hds data-theme="dark">`); pass `container` on the Content part to override.
94
94
 
95
95
  ## Motion
96
96
 
@@ -121,7 +121,7 @@ Default most interactive feedback to `productive` (150ms, decelerate). Reserve `
121
121
 
122
122
  | Token | Value | Purpose |
123
123
  | --- | --- | --- |
124
- | `semantic.motion.distance` | `24px` | Travel distance for a scroll-reveal translateY offset. Coincides in value with semantic.space.scale.md today, but names a motion travel distance rather than a spacing tier (hds#242). |
124
+ | `semantic.motion.distance` | `24px` | Scroll-reveal translateY travel distance. Equals semantic.space.scale.md today, but names motion travel, not a spacing tier (hds#242). |
125
125
  <!-- auto:end:motion -->
126
126
 
127
127
  ## Components
@@ -129,7 +129,7 @@ Default most interactive feedback to `productive` (150ms, decelerate). Reserve `
129
129
  <!-- auto:start:components -->
130
130
  | Component | Radius | States | Guidance |
131
131
  | --- | --- | --- | --- |
132
- | **Buttons** (`Button`) | `8px` (`semantic.radius.action`) | default · hover · focus · active · disabled · loading | Three variants: primary (accent-filled), secondary (outline), tertiary (ghost). Primary uses `semantic.accent.*` ramp per state. Icon buttons (`IconButton`) follow the same token surface. Icon-only actions use `IconButton` with an icon from `@hirobius/design-system/icons`. |
132
+ | **Buttons** (`Button`) | `8px` (`semantic.radius.action`) | default · hover · focus · active · disabled · loading | Three variants: primary (accent-filled), secondary (outline), tertiary (ghost). Primary uses `semantic.accent.*` ramp per state. Pressed is a 5% `semantic.color.state.pressed.overlay` wash over the fill (`role.pressed-overlay`: black in light, white in dark), not a brightness filter. Icon-only buttons (`iconOnly`) follow the same token surface. Icon-only actions use `Button iconOnly` with a `label` and an `Icon` from `@hirobius/design-system/icons` in `iconLeft`. |
133
133
  | **Inputs** (`Input`) | `8px` (`semantic.radius.action`) | default · focus · filled · error · disabled · loading | Border-driven treatment; no filled background by default. Focus uses `semantic.color.border.accent` plus a 2px outline offset. Error swaps to `component.input.borderError`. |
134
134
  | **Cards** (`Card`) | `12px` (`rounded-lg`, role radius + 4px) | default · hover (optional parallax) · pressed (when interactive) | Cards default to `elevation.flat` (1px border `border.subtle`, no shadow). Interactive cards lift to `elevation.raised` (shadow.subtle, no border) on hover. Bind via `semantic.elevation.{role}` — never raw box-shadow values. Radius: `rounded-lg` (role radius + 4 px, follows the tenant knob) — never a hard-coded value. Padding: `var(--semantic-space-surface-padding)`. Title: `heading3`. Meta: `caption` + `var(--semantic-color-content-secondary)`. Hover (interactive): `scale(1.02)` transform + lift to raised. Never: gradients, glow, frosted glass, tinted surfaces, decorative overlays, or inner shadows. |
135
135
  | **Badges** (`Badge`) | `8px` (`primitive.radius.4`) | neutral · accent · feedback (error/success/warning/info) | Single-line status markers. Feedback colors come from `semantic.color.feedback.*`. Never used as decorative chrome. |
@@ -176,11 +176,13 @@ Pick by what the thing is. The first four rows are for a headline number or a st
176
176
  | `Stat` | An inline number inside prose or a dense list. | `MetricTiles` when the number stands alone in a row of its own. |
177
177
  | `Card.Metric` | A metric inside an existing `Card` only. | `MetricTiles` when there is no enclosing `Card`. |
178
178
  | `StatusTile` | State with notes and a trailing badge, never a number. | `MetricTiles` when the tile is a number; `StatusTile` never carries a value. |
179
- | `Card` | A titled or interactive content object (header, body, footer, or selectable). | `Surface` when there is no title and nothing to press. |
179
+ | `Card` | A titled or interactive content object (header, body, footer, `selectable`). | `Surface` when there is no title and nothing to press. |
180
180
  | `Surface` | A plain padded background around content that has no header of its own. | `Card` when the content is titled or interactive. |
181
181
  | `DestructiveSection` | The one irreversible action on a screen, with an explanation and a confirm. | `FormActions` `destructive` slot for a delete beside a form's own buttons. |
182
182
  | `DataTableSection` | A titled table with a toolbar, row actions and an empty state. | `Table` alone when there is no heading, toolbar or empty state to show. |
183
183
 
184
+ Three more needs are props on an existing component, not components of their own. A card people pick from a set is a `Card` with `selectable`, `selected` and `onSelectedChange`, announced as a checkbox. A wrapping row of equal tiles that are not headline numbers is a `Grid` with `layout="auto-fill"` and `minItemWidth`. A state shown only as a colored marker, with no notes, is a `Badge` with `dot` and a `label` that names the state.
185
+
184
186
  `Card` and `Surface` share one container radius, `rounded-lg` (`role.radius` + 4 px: 12 px by default, 4 px under `brutalist-demo`). Dialogs and alert dialogs use it too. Never read `--component-card-radius` for a container; it ignores the tenant.
185
187
 
186
188
  `MetricTiles` has `min(tiles, 4)` columns, so a row never leaves an empty column, and every tile has one fixed min-height with the value in `heading2`, the label as an eyebrow and the sub line in `caption`. Its tone comes from the fixed feedback vocabulary: `neutral | success | warning | danger | info`.
@@ -215,6 +217,6 @@ Unresolved rules that the live repo cannot yet answer confidently are tracked in
215
217
  <!-- auto:start:build-meta -->
216
218
  ---
217
219
 
218
- > Generated from `hirobius.tokens.json` (381 tokens) and `public/hds-manifest.json` by `scripts/build-design-md.mjs`.
220
+ > Generated from `hirobius.tokens.json` (385 tokens) and `public/hds-manifest.json` by `scripts/build-design-md.mjs`.
219
221
  > Hand-edit `DESIGN.source.md`; this file (`DESIGN.md`) is overwritten by `pnpm tokens`.
220
222
  <!-- auto:end:build-meta -->
package/README.md CHANGED
@@ -12,9 +12,9 @@ pnpm add @hirobius/design-system
12
12
 
13
13
  <!-- auto:start:front-door-counts -->
14
14
 
15
- - **109** public component modules, exported from `src/index.ts`
16
- - **381** DTCG tokens in `hirobius.tokens.json`, compiled to CSS variables and TypeScript constants
17
- - **484** Storybook stories in **121** story files
15
+ - **54** public component modules, exported from `src/index.ts`
16
+ - **385** DTCG tokens in `hirobius.tokens.json`, compiled to CSS variables and TypeScript constants
17
+ - **340** Storybook stories in **71** story files
18
18
 
19
19
  <!-- auto:end:front-door-counts -->
20
20
 
@@ -29,9 +29,12 @@ Agents: install the consumer skill with `npx skills add hirobius/hds --skill hds
29
29
 
30
30
  <!-- auto:start:consumer-usage -->
31
31
 
32
- The Ops dashboard (`hirobius/ops`) is the only verified component-level consumer: **35** of its source files import from `@hirobius/design-system`, using **25** distinct components. The two screenshots below are Ops pages rendered from its `main` (measured at commit `01fce2f`).
32
+ The Ops dashboard (`hirobius/ops`) is the only product app that uses components: **33** of its source files import from `@hirobius/design-system`, using **25** distinct components. The two screenshots below are Ops pages rendered from its `main` (measured at commit `76ef65e`).
33
33
 
34
- Other consumers: the split into product apps and token-level sites is not yet confirmed, so it is not stated here.
34
+ | Consumer kind | Count | How it uses HDS |
35
+ | ----------------- | ----- | ----------------------------------------- |
36
+ | Product apps | 1 | Components and tokens |
37
+ | Token-level sites | 4 | Tokens and CSS only, no component imports |
35
38
 
36
39
  [![Ops library page](docs/images/ops-library.png)](docs/images/ops-library.png)
37
40
 
@@ -39,7 +42,7 @@ Other consumers: the split into product apps and token-level sites is not yet co
39
42
 
40
43
  <!-- auto:end:consumer-usage -->
41
44
 
42
- The numbers come from `pnpm consumer:usage --root <ops checkout>`, which measures that checkout and records its commit in `docs/data/consumer-usage.json`; without a root it reuses the committed snapshot. `scripts/__tests__/consumer-usage.test.mjs` fails if this section drifts from that snapshot. The product-app and token-level-site split is a declared figure in that file and stays out of this section until `consumersConfirmed` there is set to true.
45
+ The numbers come from `pnpm consumer:usage --root <ops checkout>`, which measures that checkout's `src/` and records its commit in `docs/data/consumer-usage.json` (pass `--commit <sha>` when the root is a `git archive` export); without a root it reuses the committed snapshot. A deprecated alias counts as the component it points to (`HdsCheckbox` as `Checkbox`). The components that Ops's page- and video-clone prompts tell generated code to import are listed there as `promptContracts` and are not counted here. `scripts/__tests__/consumer-usage.test.mjs` fails if this section drifts from that snapshot. The product-app and token-level-site split is a declared figure in that file, not a measurement. It was confirmed on 2026-10-01 (hds#389: there is no second product app), so `consumersConfirmed` there is true and the table above shows it.
43
46
 
44
47
  ## Using the published package
45
48
 
@@ -51,7 +54,7 @@ guide: **[docs/CONSUMING.md](docs/CONSUMING.md)**.
51
54
  > `@hirobius/design-system` entry under GitHub Packages
52
55
  > (`/pkgs/npm/design-system`). That listing is **frozen and no longer updated** —
53
56
  > publishing moved to the public npm registry in
54
- > [#39](https://github.com/hirobius/hirobius-design-system/pull/39). Always
57
+ > [#39](https://github.com/hirobius/hds/pull/39). Always
55
58
  > install from npm:
56
59
  > **[npmjs.com/package/@hirobius/design-system](https://www.npmjs.com/package/@hirobius/design-system)**.
57
60
 
@@ -153,7 +156,7 @@ pnpm build-storybook
153
156
 
154
157
  HDS is built around three structural rules:
155
158
 
156
- - **Strict semantics** — public surfaces prefer system primitives such as `Stack`, `Grid`, `Surface`, and `TextLockup` instead of raw layout divs or ad hoc CSS.
159
+ - **Strict semantics** — public surfaces prefer system primitives such as `Stack`, `Grid`, `Surface`, and `Text` instead of raw layout divs or ad hoc CSS.
157
160
  - **Polymorphism** — primitives preserve semantic HTML while staying composable through governed APIs such as `forwardRef`, `as`, and layout slots.
158
161
  - **12-column grid** — page structure follows a consistent editorial grid: readable center columns, intentional breakout zones, and explicit `gap` ownership rather than one-off spacing math.
159
162
 
@@ -173,10 +176,10 @@ What CI and the hooks check today, and nothing more:
173
176
 
174
177
  - **Contrast:** `scripts/check-contrast.mjs` enforces WCAG AA contrast on the core token pairs, in light and dark. It runs in CI and the pre-commit hook.
175
178
  - **Lint:** `jsx-a11y` rules run inside ESLint with zero warnings allowed.
176
- - **Focus:** `scripts/check-focus-states.mjs` (`pnpm check:focus`) audits focus styles on interactive components. It runs in the agent gate and on demand, not in the main CI workflow, and currently reports 1 violation (`src/app/components/asset-img.tsx`).
177
- - **Storybook:** the `addon-a11y` panel shows axe results for each story while you review it. It reports; it does not gate.
179
+ - **Focus:** `scripts/check-focus-states.mjs` (`pnpm check:focus`) audits focus styles on interactive components. It runs in `pretest` under `pnpm test`, so in the pre-push hook and in CI, and passes with 0 violations.
180
+ - **Storybook:** the `addon-a11y` panel shows axe results for each story while you review it. The gate is `scripts/check-storybook-axe.mjs`: CI scans every built story in light and dark, including the open Select and Combobox, and fails on serious or critical violations outside its allowlist (`scripts/axe-allowlist.json`). The allowlist has one entry: `aria-hidden-focus` on the open Select story, where Radix Select hides the page around its listbox while focus stays trapped inside it (hds#407).
178
181
 
179
- There is no automated screen-reader testing.
182
+ Screen-reader output is checked by `tests/primitive-contracts/screen-reader.contract.test.tsx`, which runs in `pnpm test` and walks Dialog, Menu, Select, Combobox, Table and Alert with a virtual screen-reader (`@guidepup/virtual-screen-reader`) under jsdom, asserting the role, name and state phrases it reads and the alert's live-region announcement; it reads the jsdom accessibility tree, not what a desktop or mobile screen-reader says.
180
183
 
181
184
  ## Visual direction: Editorial Enterprise
182
185
 
@@ -0,0 +1,231 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * hds-prefix codemod (hds#389 R1, follows hds#315)
4
+ *
5
+ * 0.20.0 removed the six `Hds*` aliases from the package root: HdsCheckbox,
6
+ * HdsRadio, HdsSelect, HdsSlider, HdsToggle and HdsTooltip. Each is the same
7
+ * component under its bare name. This rewrites the import specifier only, and
8
+ * keeps the name the file already uses:
9
+ *
10
+ * - `import { HdsCheckbox } from '@hirobius/design-system'` becomes
11
+ * `import { Checkbox as HdsCheckbox } from '@hirobius/design-system'`;
12
+ * - `HdsSelect as Pick` becomes `Select as Pick`; `type` modifiers stay;
13
+ * - `export { HdsToggle } from '…'` becomes `export { Toggle as HdsToggle } from '…'`,
14
+ * so the file's own export name does not change.
15
+ *
16
+ * No other text in the file changes: JSX, values, `typeof`, spreads, string
17
+ * keys, comments (including comments inside the import braces) and imports from
18
+ * any other module stay as written, so every reference still resolves to the
19
+ * same component. Renaming the local binding to the bare name afterwards is
20
+ * optional and a manual edit. Only named imports and re-exports whose source is
21
+ * exactly the package root are rewritten, and running it twice changes nothing.
22
+ *
23
+ * node codemods/hds-prefix.mjs [--root <dir>] [--check] [--dry-run]
24
+ *
25
+ * --root <dir> directory to scan (default: current directory)
26
+ * --check write nothing; exit 1 when a rewrite is needed, or when a root
27
+ * star re-export, or a namespace import or dynamic `import()`,
28
+ * `require()` or `vi.mock`/`jest.mock` of the root that reads an
29
+ * Hds* name off the module, or a file that cannot be read to its
30
+ * end (codemods/unrewritable.mjs), needs a manual look
31
+ * --dry-run write nothing; print each changed line before (-) and after (+), exit 0
32
+ */
33
+ import { readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from 'node:fs';
34
+ import { relative, resolve, join } from 'node:path';
35
+ import { fileURLToPath } from 'node:url';
36
+ import { findUnrewritable as findHidden, maskSource } from './unrewritable.mjs';
37
+
38
+ const ROOT_PKG = '@hirobius/design-system';
39
+ const EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.mts', '.cts']);
40
+ const SKIP_DIRS = new Set([
41
+ 'node_modules',
42
+ 'dist',
43
+ 'build',
44
+ '.git',
45
+ '.next',
46
+ '.vercel',
47
+ 'coverage',
48
+ 'storybook-static',
49
+ ]);
50
+
51
+ /** The aliases 0.19.1 exported from the root, and the bare name each pointed at. */
52
+ export const RENAMES = Object.freeze({
53
+ HdsCheckbox: 'Checkbox',
54
+ HdsRadio: 'Radio',
55
+ HdsSelect: 'Select',
56
+ HdsSlider: 'Slider',
57
+ HdsToggle: 'Toggle',
58
+ HdsTooltip: 'Tooltip',
59
+ });
60
+
61
+ const ID = '[A-Za-z_$][\\w$]*';
62
+ // Matched on the masked source (codemods/unrewritable.mjs), where comments are
63
+ // blank and the specifier is a run of spaces between its quotes:
64
+ // at a line start or after `;`: import|export, type?, default?, { body }, from, quote.
65
+ const namedRe = () =>
66
+ new RegExp(
67
+ `(?:^|(?<=;))[ \\t]*(?:import|export)(?:\\s+type)?\\s*(?:${ID}\\s*,\\s*)?\\{([^}]*)\\}\\s*from\\s*(['"]) {${ROOT_PKG.length}}\\2`,
68
+ 'gm',
69
+ );
70
+ // One specifier, between commas: `type`? name, then `as alias`?
71
+ const specRe = new RegExp(`^(\\s*(?:type\\s+)?)(${ID})(\\s+as\\s+${ID})?\\s*$`);
72
+
73
+ /**
74
+ * Pure transform of one file's source: rewrites the Hds* specifiers of each
75
+ * named import or re-export from the package root, and nothing else.
76
+ * @param {string} source
77
+ * @param {Record<string, string>} [renames]
78
+ * @returns {{ source: string, changed: boolean, sites: number, renamed: string[],
79
+ * edits: { line: number, before: string, after: string }[] }}
80
+ */
81
+ export function transformSource(source, renames = RENAMES) {
82
+ const { code } = maskSource(source);
83
+ const renamed = new Set();
84
+ const changes = [];
85
+ let sites = 0;
86
+
87
+ for (const m of code.matchAll(namedRe())) {
88
+ const quoteAt = m.index + m[0].length - ROOT_PKG.length - 2;
89
+ if (!source.startsWith(ROOT_PKG, quoteAt + 1)) continue;
90
+ const bodyAt = m.index + m[0].indexOf('{') + 1;
91
+ let touched = false;
92
+ let at = bodyAt;
93
+ for (const spec of m[1].split(',')) {
94
+ const s = specRe.exec(spec);
95
+ const bare = s && Object.hasOwn(renames, s[2]) ? renames[s[2]] : undefined;
96
+ if (bare) {
97
+ const name = s[2];
98
+ const nameAt = at + s[1].length;
99
+ // Keep the name the file uses: `HdsCheckbox` -> `Checkbox as HdsCheckbox`.
100
+ changes.push({
101
+ start: nameAt,
102
+ end: nameAt + name.length,
103
+ text: s[3] ? bare : `${bare} as ${name}`,
104
+ });
105
+ renamed.add(name);
106
+ touched = true;
107
+ }
108
+ at += spec.length + 1;
109
+ }
110
+ if (touched) sites++;
111
+ }
112
+
113
+ let out = source;
114
+ for (const r of changes.sort((a, b) => b.start - a.start))
115
+ out = out.slice(0, r.start) + r.text + out.slice(r.end);
116
+
117
+ if (out === source) return { source, changed: false, sites: 0, renamed: [], edits: [] };
118
+ const before = source.split('\n');
119
+ const after = out.split('\n');
120
+ const edits = [];
121
+ before.forEach((line, i) => {
122
+ if (line !== after[i]) edits.push({ line: i + 1, before: line, after: after[i] });
123
+ });
124
+ return { source: out, changed: true, sites, renamed: [...renamed].sort(), edits };
125
+ }
126
+
127
+ /**
128
+ * What the codemod cannot rewrite (codemods/unrewritable.mjs): a star re-export
129
+ * of the root, whose importers are in other files; and a namespace import or a
130
+ * dynamic `import()`, `require()`, `vi.mock` or `jest.mock` of the root in a file
131
+ * that reads an Hds* name off something (`HDS.HdsSlider`, `m['HdsToggle']`,
132
+ * `const { HdsRadio } = …`). The alias binding the codemod writes is not a read.
133
+ */
134
+ export function findUnrewritable(source, renames = RENAMES) {
135
+ return findHidden(source, ROOT_PKG, Object.keys(renames));
136
+ }
137
+
138
+ function* walk(dir) {
139
+ for (const entry of readdirSync(dir)) {
140
+ if (SKIP_DIRS.has(entry)) continue;
141
+ const full = join(dir, entry);
142
+ const st = statSync(full);
143
+ if (st.isDirectory()) yield* walk(full);
144
+ else if (EXTS.has(full.slice(full.lastIndexOf('.')))) yield full;
145
+ }
146
+ }
147
+
148
+ /** Scan a directory. Writes only when `write` is true. */
149
+ export function runCodemod({ root, write = false, renames = RENAMES }) {
150
+ const files = [];
151
+ const manual = [];
152
+ const renamed = new Set();
153
+ let sites = 0;
154
+ for (const file of walk(root)) {
155
+ const src = readFileSync(file, 'utf8');
156
+ if (!src.includes(ROOT_PKG)) continue;
157
+ const r = transformSource(src, renames);
158
+ for (const stmt of findUnrewritable(r.source, renames))
159
+ manual.push({ file: relative(root, file), stmt });
160
+ if (!r.changed) continue;
161
+ files.push({ file: relative(root, file), sites: r.sites, renamed: r.renamed, edits: r.edits });
162
+ r.renamed.forEach((n) => renamed.add(n));
163
+ sites += r.sites;
164
+ if (write) writeFileSync(file, r.source);
165
+ }
166
+ return { files, sites, manual, names: [...renamed].sort() };
167
+ }
168
+
169
+ function main(argv) {
170
+ const args = { root: process.cwd(), check: false, dryRun: false };
171
+ for (let i = 0; i < argv.length; i++) {
172
+ if (argv[i] === '--root') args.root = resolve(argv[++i] ?? '');
173
+ else if (argv[i] === '--check') args.check = true;
174
+ else if (argv[i] === '--dry-run') args.dryRun = true;
175
+ else {
176
+ console.error(
177
+ `unknown argument: ${argv[i]}\nusage: hds-prefix [--root <dir>] [--check] [--dry-run]`,
178
+ );
179
+ return 2;
180
+ }
181
+ }
182
+ const write = !args.check && !args.dryRun;
183
+ const res = runCodemod({ root: args.root, write });
184
+ const names = res.names.map((n) => `${n} -> ${RENAMES[n]}`).join(', ') || 'none';
185
+ const summary = `${res.files.length} files, ${res.sites} import sites, ${res.names.length} names (${names})`;
186
+ // An unreadable file names its own fix; the rest need the bare names by hand.
187
+ const manualLines = res.manual.map(
188
+ (m) =>
189
+ ` ${m.file}: ${m.stmt}${m.stmt.startsWith('unreadable ') ? '' : ' (use the bare names by hand)'}`,
190
+ );
191
+ if (args.check) {
192
+ if (res.files.length > 0 || res.manual.length > 0) {
193
+ console.error(`hds-prefix: rewrite needed: ${summary}`);
194
+ for (const f of res.files) console.error(` ${f.file}: ${f.renamed.join(', ')}`);
195
+ for (const l of manualLines) console.error(l);
196
+ return 1;
197
+ }
198
+ console.log('hds-prefix: nothing to rewrite');
199
+ return 0;
200
+ }
201
+ console.log(`hds-prefix: ${write ? 'rewrote' : 'would rewrite'} ${summary}`);
202
+ for (const f of res.files) {
203
+ console.log(` ${f.file}: ${f.renamed.join(', ')}`);
204
+ if (args.dryRun)
205
+ for (const e of f.edits) {
206
+ console.log(`- ${e.before}`);
207
+ console.log(`+ ${e.after}`);
208
+ }
209
+ }
210
+ if (res.manual.length > 0) {
211
+ console.warn('hds-prefix: cannot rewrite these, do them by hand:');
212
+ for (const l of manualLines) console.warn(l);
213
+ }
214
+ return 0;
215
+ }
216
+
217
+ // Compare real paths: npm/yarn link the bin and pnpm links the package directory, so
218
+ // argv[1] is a symlink while import.meta.url is already resolved.
219
+ const isEntry = (() => {
220
+ try {
221
+ return (
222
+ !!process.argv[1] &&
223
+ realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))
224
+ );
225
+ } catch {
226
+ return false;
227
+ }
228
+ })();
229
+ if (isEntry) {
230
+ process.exit(main(process.argv.slice(2)));
231
+ }