@hirobius/design-system 0.16.0 → 0.18.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 (91) hide show
  1. package/README.md +41 -69
  2. package/codemods/patterns-subpath.mjs +254 -0
  3. package/codemods/patterns-subpath.names.json +26 -0
  4. package/dist/chunks/LanguageContext-CH_bwzCq.js +83 -0
  5. package/dist/chunks/TokenDisplayContext-evZi_214.js +18 -0
  6. package/dist/chunks/activity-feed-C8_e-Btq.js +19955 -0
  7. package/dist/contexts.js +17 -16
  8. package/dist/hirobius-ui.js +3652 -23311
  9. package/dist/manifest.js +5 -5
  10. package/dist/patterns.js +423 -0
  11. package/dist/static.css +4 -4
  12. package/dist/styles.css +1 -1
  13. package/dist/tokens.css +1 -1
  14. package/dist/tokens.js +34 -13
  15. package/dist/types/src/app/components/activity-feed.d.ts +6 -4
  16. package/dist/types/src/app/components/app-shell.d.ts +6 -4
  17. package/dist/types/src/app/components/asset-img.d.ts +3 -1
  18. package/dist/types/src/app/components/avatar-group.d.ts +3 -3
  19. package/dist/types/src/app/components/avatar.d.ts +3 -3
  20. package/dist/types/src/app/components/badge.d.ts +3 -3
  21. package/dist/types/src/app/components/blockquote.d.ts +3 -3
  22. package/dist/types/src/app/components/breadcrumb.d.ts +3 -3
  23. package/dist/types/src/app/components/button-group.d.ts +3 -3
  24. package/dist/types/src/app/components/button.d.ts +6 -6
  25. package/dist/types/src/app/components/calendar.d.ts +3 -1
  26. package/dist/types/src/app/components/callout.d.ts +4 -4
  27. package/dist/types/src/app/components/card.d.ts +5 -5
  28. package/dist/types/src/app/components/carousel.d.ts +6 -4
  29. package/dist/types/src/app/components/checkbox.d.ts +3 -3
  30. package/dist/types/src/app/components/circular-progress.d.ts +3 -3
  31. package/dist/types/src/app/components/code-block.d.ts +20 -18
  32. package/dist/types/src/app/components/command-palette.d.ts +9 -9
  33. package/dist/types/src/app/components/component-instance-matrix.d.ts +1 -1
  34. package/dist/types/src/app/components/disclosure.d.ts +3 -3
  35. package/dist/types/src/app/components/divider.d.ts +4 -4
  36. package/dist/types/src/app/components/doc-link-card.d.ts +6 -4
  37. package/dist/types/src/app/components/error-pattern.d.ts +3 -1
  38. package/dist/types/src/app/components/field.d.ts +4 -4
  39. package/dist/types/src/app/components/file-input.d.ts +7 -5
  40. package/dist/types/src/app/components/hds-tooltip.d.ts +11 -11
  41. package/dist/types/src/app/components/heading-stack.d.ts +6 -6
  42. package/dist/types/src/app/components/hover-card.d.ts +1 -1
  43. package/dist/types/src/app/components/image-lightbox.d.ts +2 -0
  44. package/dist/types/src/app/components/inline-code.d.ts +3 -3
  45. package/dist/types/src/app/components/input-group.d.ts +3 -3
  46. package/dist/types/src/app/components/input.d.ts +5 -5
  47. package/dist/types/src/app/components/kbd.d.ts +3 -3
  48. package/dist/types/src/app/components/metadata-list.d.ts +3 -3
  49. package/dist/types/src/app/components/nav-group.d.ts +3 -3
  50. package/dist/types/src/app/components/nav-item.d.ts +10 -8
  51. package/dist/types/src/app/components/overflow-list.d.ts +3 -1
  52. package/dist/types/src/app/components/page.d.ts +3 -1
  53. package/dist/types/src/app/components/progress.d.ts +3 -3
  54. package/dist/types/src/app/components/radio.d.ts +3 -3
  55. package/dist/types/src/app/components/reveal.d.ts +3 -1
  56. package/dist/types/src/app/components/segmented-control.d.ts +31 -31
  57. package/dist/types/src/app/components/select.d.ts +3 -3
  58. package/dist/types/src/app/components/selectable-card.d.ts +3 -3
  59. package/dist/types/src/app/components/side-nav.d.ts +6 -4
  60. package/dist/types/src/app/components/skeleton.d.ts +3 -3
  61. package/dist/types/src/app/components/slider.d.ts +3 -3
  62. package/dist/types/src/app/components/spinner.d.ts +3 -3
  63. package/dist/types/src/app/components/stacked-card-rail.d.ts +32 -0
  64. package/dist/types/src/app/components/stat.d.ts +3 -3
  65. package/dist/types/src/app/components/status-dot.d.ts +4 -4
  66. package/dist/types/src/app/components/status-list-item.d.ts +3 -3
  67. package/dist/types/src/app/components/stepper.d.ts +6 -4
  68. package/dist/types/src/app/components/surface.d.ts +4 -4
  69. package/dist/types/src/app/components/table.d.ts +22 -11
  70. package/dist/types/src/app/components/tag.d.ts +4 -4
  71. package/dist/types/src/app/components/text.d.ts +3 -3
  72. package/dist/types/src/app/components/textarea.d.ts +4 -4
  73. package/dist/types/src/app/components/time-input.d.ts +3 -3
  74. package/dist/types/src/app/components/toast.d.ts +3 -3
  75. package/dist/types/src/app/components/toggle-button.d.ts +4 -4
  76. package/dist/types/src/app/components/toggle.d.ts +3 -3
  77. package/dist/types/src/app/components/token.d.ts +16 -16
  78. package/dist/types/src/app/components/toolbar.d.ts +1 -1
  79. package/dist/types/src/app/components/tooltip.d.ts +2 -2
  80. package/dist/types/src/app/components/top-nav.d.ts +3 -1
  81. package/dist/types/src/app/components/tree-list.d.ts +3 -1
  82. package/dist/types/src/app/design-system/generated-token-refs.d.ts +36 -0
  83. package/dist/types/src/app/design-system/generated-token-values.d.ts +10 -10
  84. package/dist/types/src/app/design-system/tokens.d.ts +16 -0
  85. package/dist/types/src/index.d.ts +193 -8
  86. package/dist/types/src/patterns.d.ts +37 -0
  87. package/dist/variables.css +42 -22
  88. package/hirobius.tokens.json +178 -57
  89. package/package.json +21 -11
  90. package/public/hds-manifest.json +473 -445
  91. package/dist/chunks/TokenDisplayContext-B3gxyQN7.js +0 -96
package/README.md CHANGED
@@ -1,20 +1,20 @@
1
1
  # Hirobius Design System
2
2
 
3
- A publishable React + TypeScript component library backed by a governed design-token pipeline, published to npm as `@hirobius/design-system`.
3
+ [![CI](https://github.com/hirobius/hds/actions/workflows/ci.yml/badge.svg)](https://github.com/hirobius/hds/actions/workflows/ci.yml) [![npm](https://img.shields.io/npm/v/@hirobius/design-system)](https://www.npmjs.com/package/@hirobius/design-system) [![Storybook](https://img.shields.io/badge/Storybook-live-ff4785)](https://hirobius-design-system.vercel.app)
4
4
 
5
- <!--
6
- PLACEHOLDER — ADRIAN TO WRITE (hds front-door PR).
7
- If you want the README to mention the July–September 2026 pause, put one or
8
- two sentences here in your own words, then delete this comment. Leaving the
9
- comment in place publishes nothing. Do not restore the old status line this
10
- PR removed: CI, release, and Chromatic triggers were restored in #204.
11
- -->
5
+ React + TypeScript component library on a governed design-token pipeline. Live Storybook: **<https://hirobius-design-system.vercel.app>**
6
+
7
+ ```bash
8
+ pnpm add @hirobius/design-system
9
+ ```
10
+
11
+ [![Buttons under the Brand and Theme dials: base and accent-lilac, light and dark](docs/images/storybook-brand-theme-dials.png)](https://hirobius-design-system.vercel.app)
12
12
 
13
13
  <!-- auto:start:front-door-counts -->
14
14
 
15
15
  - **109** public component modules, exported from `src/index.ts`
16
- - **359** DTCG tokens in `hirobius.tokens.json`, compiled to CSS variables and TypeScript constants
17
- - **454** Storybook stories in **115** story files, reviewed visually in Chromatic
16
+ - **379** DTCG tokens in `hirobius.tokens.json`, compiled to CSS variables and TypeScript constants
17
+ - **455** Storybook stories in **115** story files
18
18
 
19
19
  <!-- auto:end:front-door-counts -->
20
20
 
@@ -23,6 +23,22 @@ A publishable React + TypeScript component library backed by a governed design-t
23
23
 
24
24
  The counts are generated from source by `pnpm readme:counts`, which `pnpm tokens` also runs. `scripts/__tests__/front-door.test.mjs` fails if this README claims more than the source has.
25
25
 
26
+ ## In use
27
+
28
+ <!-- auto:start:consumer-usage -->
29
+
30
+ 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`).
31
+
32
+ Other consumers: the split into product apps and token-level sites is not yet confirmed, so it is not stated here.
33
+
34
+ [![Ops library page](docs/images/ops-library.png)](docs/images/ops-library.png)
35
+
36
+ [![Ops fleet audit page](docs/images/ops-fleet-audit.png)](docs/images/ops-fleet-audit.png)
37
+
38
+ <!-- auto:end:consumer-usage -->
39
+
40
+ 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.
41
+
26
42
  ## Using the published package
27
43
 
28
44
  Installing HDS in another project? It ships to the **public npm registry** as
@@ -125,7 +141,7 @@ Core verification commands:
125
141
  ```bash
126
142
  pnpm typecheck
127
143
  pnpm lint
128
- pnpm exec vitest run # unit + contract tests, as the pre-push hook and CI run them
144
+ pnpm test # pretest gates + unit + contract tests, exactly as the pre-push hook and CI run them
129
145
  pnpm tokens:verify
130
146
  pnpm check:size
131
147
  pnpm build-storybook
@@ -147,62 +163,18 @@ Source-of-truth files:
147
163
  - `DESIGN.md` — lean visual spec for agents and engineers.
148
164
  - `DESIGN-HANDOFF.md` — verbose visual mirror for handoff and review.
149
165
 
150
- <!-- design-links:start (generated by `pnpm figma:links`; edit the @figma JSDoc tag, not this section) -->
151
-
152
- ## Design ↔ Code links
153
-
154
- Each component has one Figma source: `figmaUrl` in `public/hds-manifest.json`, which `pnpm manifest:generate` sets from the `@figma` tag in the component JSDoc. `pnpm figma:links` keeps this table current and Storybook reads the same field (`parameters.design`). The Figma-side links (dev resources and component descriptions) exist only once someone runs their steps in [`figma/README.md`](figma/README.md).
155
-
156
- **44 of 140** components link a Figma node.
157
-
158
- | Component | Figma node | Story | Source |
159
- | ------------------ | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------- |
160
- | `Alert` | [33:34](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=33-34) | [alert.stories.tsx](src/stories/alert.stories.tsx) | [alert.tsx](src/app/components/alert.tsx) |
161
- | `Avatar` | [34:18](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=34-18) | [avatar.stories.tsx](src/stories/avatar.stories.tsx) | [avatar.tsx](src/app/components/avatar.tsx) |
162
- | `Badge` | [31:15](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=31-15) | [badge.stories.tsx](src/stories/badge.stories.tsx) | [badge.tsx](src/app/components/badge.tsx) |
163
- | `Breadcrumb` | [86:159](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=86-159) | [breadcrumb.stories.tsx](src/stories/breadcrumb.stories.tsx) | [breadcrumb.tsx](src/app/components/breadcrumb.tsx) |
164
- | `Button` | [28:138](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=28-138) | [button.stories.tsx](src/stories/button.stories.tsx) | [button.tsx](src/app/components/button.tsx) |
165
- | `Callout` | [88:85](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=88-85) | [callout.stories.tsx](src/stories/callout.stories.tsx) | [callout.tsx](src/app/components/callout.tsx) |
166
- | `Card` | [39:11](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=39-11) | [card.stories.tsx](src/stories/card.stories.tsx) | [card.tsx](src/app/components/card.tsx) |
167
- | `CodeBlock` | [89:154](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=89-154) | [code-block.stories.tsx](src/stories/code-block.stories.tsx) | [code-block.tsx](src/app/components/code-block.tsx) |
168
- | `Combobox` | [82:237](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=82-237) | [combobox.stories.tsx](src/stories/combobox.stories.tsx) | [combobox.tsx](src/app/components/combobox.tsx) |
169
- | `Dialog` | [93:27](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=93-27) | [dialog.stories.tsx](src/stories/dialog.stories.tsx) | [dialog.tsx](src/app/components/dialog.tsx) |
170
- | `Disclosure` | [88:453](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=88-453) | [disclosure.stories.tsx](src/stories/disclosure.stories.tsx) | [disclosure.tsx](src/app/components/disclosure.tsx) |
171
- | `Divider` | [89:26](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=89-26) | [divider.stories.tsx](src/stories/divider.stories.tsx) | [divider.tsx](src/app/components/divider.tsx) |
172
- | `EmptyState` | [88:352](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=88-352) | [empty-state.stories.tsx](src/stories/empty-state.stories.tsx) | [empty-state.tsx](src/app/components/empty-state.tsx) |
173
- | `Field` | [85:81](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=85-81) | [field.stories.tsx](src/stories/field.stories.tsx) | [field.tsx](src/app/components/field.tsx) |
174
- | `Form` | [85:94](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=85-94) | [form.stories.tsx](src/stories/form.stories.tsx) | [form.tsx](src/app/components/form.tsx) |
175
- | `HdsCheckbox` | [35:29](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=35-29) | [checkbox.stories.tsx](src/stories/checkbox.stories.tsx) | [checkbox.tsx](src/app/components/checkbox.tsx) |
176
- | `HdsRadio` | [36:17](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=36-17) | [radio.stories.tsx](src/stories/radio.stories.tsx) | [radio.tsx](src/app/components/radio.tsx) |
177
- | `HdsSelect` | [82:49](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=82-49) | [select.stories.tsx](src/stories/select.stories.tsx) | [select.tsx](src/app/components/select.tsx) |
178
- | `HdsSlider` | [82:265](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=82-265) | [slider.stories.tsx](src/stories/slider.stories.tsx) | [slider.tsx](src/app/components/slider.tsx) |
179
- | `HdsToggle` | [37:19](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=37-19) | [toggle.stories.tsx](src/stories/toggle.stories.tsx) | [toggle.tsx](src/app/components/toggle.tsx) |
180
- | `IconButton` | [40:39](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=40-39) | [icon-button.stories.tsx](src/stories/icon-button.stories.tsx) | [icon-button.tsx](src/app/components/icon-button.tsx) |
181
- | `InlineCode` | [89:155](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=89-155) | [inline-code.stories.tsx](src/stories/inline-code.stories.tsx) | [inline-code.tsx](src/app/components/inline-code.tsx) |
182
- | `InlineLink` | [89:161](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=89-161) | [inline-link.stories.tsx](src/stories/inline-link.stories.tsx) | [inline-link.tsx](src/app/components/inline-link.tsx) |
183
- | `Input` | [38:20](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=38-20) | [input.stories.tsx](src/stories/input.stories.tsx) | [input.tsx](src/app/components/input.tsx) |
184
- | `Menu` | [93:59](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=93-59) | [menu.stories.tsx](src/stories/menu.stories.tsx) | [menu.tsx](src/app/components/menu.tsx) |
185
- | `NavGroup` | [86:304](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=86-304) | [nav-group.stories.tsx](src/stories/nav-group.stories.tsx) | [nav-group.tsx](src/app/components/nav-group.tsx) |
186
- | `NavItem` | [86:258](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=86-258) | [nav-item.stories.tsx](src/stories/nav-item.stories.tsx) | [nav-item.tsx](src/app/components/nav-item.tsx) |
187
- | `Pagination` | [86:194](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=86-194) | [pagination.stories.tsx](src/stories/pagination.stories.tsx) | [pagination.tsx](src/app/components/pagination.tsx) |
188
- | `Popover` | [93:32](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=93-32) | [popover.stories.tsx](src/stories/popover.stories.tsx) | [popover.tsx](src/app/components/popover.tsx) |
189
- | `Progress` | [88:91](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=88-91) | [progress.stories.tsx](src/stories/progress.stories.tsx) | [progress.tsx](src/app/components/progress.tsx) |
190
- | `SegmentedControl` | [82:334](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=82-334) | [segmented-control.stories.tsx](src/stories/segmented-control.stories.tsx) | [segmented-control.tsx](src/app/components/segmented-control.tsx) |
191
- | `SideNav` | [86:332](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=86-332) | [side-nav.stories.tsx](src/stories/side-nav.stories.tsx) | [side-nav.tsx](src/app/components/side-nav.tsx) |
192
- | `Skeleton` | [88:95](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=88-95) | [skeleton.stories.tsx](src/stories/skeleton.stories.tsx) | [skeleton.tsx](src/app/components/skeleton.tsx) |
193
- | `Spinner` | [88:106](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=88-106) | [spinner.stories.tsx](src/stories/spinner.stories.tsx) | [spinner.tsx](src/app/components/spinner.tsx) |
194
- | `Stat` | [88:247](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=88-247) | [stat.stories.tsx](src/stories/stat.stories.tsx) | [stat.tsx](src/app/components/stat.tsx) |
195
- | `StatusListItem` | [88:350](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=88-350) | [status-list-item.stories.tsx](src/stories/status-list-item.stories.tsx) | [status-list-item.tsx](src/app/components/status-list-item.tsx) |
196
- | `StatusTile` | [88:292](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=88-292) | [status-tile.stories.tsx](src/stories/status-tile.stories.tsx) | [status-tile.tsx](src/app/components/status-tile.tsx) |
197
- | `StepperField` | [82:425](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=82-425) | [stepper-field.stories.tsx](src/stories/stepper-field.stories.tsx) | [stepper-field.tsx](src/app/components/stepper-field.tsx) |
198
- | `Surface` | [89:27](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=89-27) | [surface.stories.tsx](src/stories/surface.stories.tsx) | [surface.tsx](src/app/components/surface.tsx) |
199
- | `Table` | [89:300](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=89-300) | [table.stories.tsx](src/stories/table.stories.tsx) | [table.tsx](src/app/components/table.tsx) |
200
- | `Tabs` | [86:89](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=86-89) | [tabs.stories.tsx](src/stories/tabs.stories.tsx) | [tabs.tsx](src/app/components/tabs.tsx) |
201
- | `Tag` | [32:11](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=32-11) | [tag.stories.tsx](src/stories/tag.stories.tsx) | [tag.tsx](src/app/components/tag.tsx) |
202
- | `Textarea` | [85:14](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=85-14) | [textarea.stories.tsx](src/stories/textarea.stories.tsx) | [textarea.tsx](src/app/components/textarea.tsx) |
203
- | `ToastProvider` | [93:367](https://www.figma.com/design/c8MaVgwxOlxm4wr8wnH0Z4/HDS-Tokens-Components?node-id=93-367) | [toast.stories.tsx](src/stories/toast.stories.tsx) | [toast.tsx](src/app/components/toast.tsx) |
204
-
205
- <!-- design-links:end -->
166
+ Component-by-component Figma links live in [docs/DESIGN_LINKS.md](docs/DESIGN_LINKS.md), generated by `pnpm figma:links`.
167
+
168
+ ## Accessibility
169
+
170
+ What CI and the hooks check today, and nothing more:
171
+
172
+ - **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.
173
+ - **Lint:** `jsx-a11y` rules run inside ESLint with zero warnings allowed.
174
+ - **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`).
175
+ - **Storybook:** the `addon-a11y` panel shows axe results for each story while you review it. It reports; it does not gate.
176
+
177
+ There is no automated screen-reader testing.
206
178
 
207
179
  ## Visual direction: Editorial Enterprise
208
180
 
@@ -218,9 +190,9 @@ The governing direction is "Editorial Enterprise" — enterprise rigor with edit
218
190
  The gates are deterministic and need no browser or live site:
219
191
 
220
192
  - **pre-commit** (`.husky/pre-commit`): secrets scan, Prettier on staged files, typecheck, zero-warning ESLint, and token validity and contrast.
221
- - **pre-push** (`.husky/pre-push`): Vitest unit and contract tests, then the consumer smoke build (library build, subpath resolution, publint, consumer typecheck).
193
+ - **pre-push** (`.husky/pre-push`): `pnpm test` (the pretest gates, then Vitest unit and contract tests), then the consumer smoke build (library build, subpath resolution, publint, consumer typecheck).
222
194
  - **CI** (`.github/workflows/ci.yml`): typecheck, zero-warning ESLint, token validity and contrast, Vitest, and the consumer smoke build, plus bundle budgets and a Storybook build.
223
- - **Visual review:** Storybook is the visual verification surface, and Chromatic (`.github/workflows/chromatic.yml`) runs it on pull requests. The earlier browser test suite drove a docs site that no longer exists; it is archived in `tests-archive/`.
195
+ - **Visual review:** Storybook is the visual verification surface, reviewed by hand. The earlier browser test suite drove a docs site that no longer exists and was removed (ADR-018).
224
196
 
225
197
  `CLAUDE.md` is the operating contract for agents working in this repo.
226
198
 
@@ -0,0 +1,254 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * patterns-subpath codemod (hds#316, follows hds#254)
4
+ *
5
+ * Rewrites `import { Page } from '@hirobius/design-system'` to
6
+ * `import { Page } from '@hirobius/design-system/patterns'` for every
7
+ * pattern-tier component whose root re-export is deprecated. Other named
8
+ * imports stay on the root. Aliases, `type` modifiers and multi-line layout
9
+ * are preserved; an existing `/patterns` import of the same kind is extended
10
+ * instead of duplicated.
11
+ *
12
+ * node codemods/patterns-subpath.mjs [--root <dir>] [--check] [--dry-run]
13
+ *
14
+ * --root <dir> directory to scan (default: current directory)
15
+ * --check write nothing; exit 1 when a rewrite is needed or a root namespace
16
+ * import (`import * as X`, `export *`) needs a manual look
17
+ * --dry-run write nothing; print each import line before (-) and after (+), exit 0
18
+ *
19
+ * The name list is codemods/patterns-subpath.names.json, generated from
20
+ * src/index.ts by `pnpm codemod:names`.
21
+ */
22
+ import { readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from 'node:fs';
23
+ import { dirname, join, relative, resolve } from 'node:path';
24
+ import { fileURLToPath } from 'node:url';
25
+
26
+ const ROOT_PKG = '@hirobius/design-system';
27
+ const SUBPATH = `${ROOT_PKG}/patterns`;
28
+ const HERE = dirname(fileURLToPath(import.meta.url));
29
+ const EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.mts', '.cts']);
30
+ const SKIP_DIRS = new Set([
31
+ 'node_modules',
32
+ 'dist',
33
+ 'build',
34
+ '.git',
35
+ '.next',
36
+ '.vercel',
37
+ 'coverage',
38
+ 'storybook-static',
39
+ ]);
40
+
41
+ export function loadPatternNames() {
42
+ const { names } = JSON.parse(readFileSync(join(HERE, 'patterns-subpath.names.json'), 'utf8'));
43
+ return new Set(names);
44
+ }
45
+
46
+ const esc = (pkg) => pkg.replace(/[/@]/g, '\\$&');
47
+ // indent, import|export, type?, default?, { body }, quote, semicolon
48
+ const namedRe = (pkg) =>
49
+ new RegExp(
50
+ `^([ \\t]*)(import|export)(\\s+type)?\\s*(?:([\\w$]+)\\s*,\\s*)?\\{([^}]*)\\}\\s*from\\s*(['"])${esc(pkg)}\\6(;?)`,
51
+ 'gm',
52
+ );
53
+ // `import * as X`, `import D, * as X`, `export * from`, `export * as X from` on the root
54
+ const namespaceRe = new RegExp(
55
+ `^[ \\t]*(?:import\\s+(?:[\\w$]+\\s*,\\s*)?\\*\\s*as\\s+[\\w$]+|export\\s+\\*(?:\\s*as\\s+[\\w$]+)?)\\s*from\\s*['"]${esc(ROOT_PKG)}['"]`,
56
+ 'gm',
57
+ );
58
+
59
+ const specName = (spec) =>
60
+ spec
61
+ .replace(/^type\s+/, '')
62
+ .split(/\s+as\s+/)[0]
63
+ .trim();
64
+ const splitSpecs = (body) =>
65
+ body
66
+ .split(',')
67
+ .map((s) => s.trim())
68
+ .filter(Boolean);
69
+
70
+ /** Namespace imports and star re-exports of the root: names are not visible, so no rewrite. */
71
+ export function findUnrewritable(source) {
72
+ return [...source.matchAll(namespaceRe)].map((m) => m[0].trim());
73
+ }
74
+
75
+ /** Renders one import/export statement. */
76
+ function render({ indent, kw, typeKw, def, list, pkg, multiline, quote, semi }) {
77
+ const head = `${indent}${kw}${typeKw ? ' type' : ''} `;
78
+ const tail = ` from ${quote}${pkg}${quote}${semi}`;
79
+ if (list.length === 0) return `${head}${def}${tail}`;
80
+ const d = def ? `${def}, ` : '';
81
+ if (multiline)
82
+ return `${head}${d}{\n${list.map((s) => `${indent} ${s},`).join('\n')}\n${indent}}${tail}`;
83
+ return `${head}${d}{ ${list.join(', ')} }${tail}`;
84
+ }
85
+
86
+ /**
87
+ * Pure transform of one file's source.
88
+ * @returns {{ source: string, changed: boolean, sites: number, moved: string[],
89
+ * edits: {before: string, after: string}[] }}
90
+ */
91
+ export function transformSource(source, names) {
92
+ const moved = [];
93
+ const edits = [];
94
+ const isType = (m) => !!m[3];
95
+ const parse = (m) => ({
96
+ indent: m[1],
97
+ kw: m[2],
98
+ typeKw: m[3],
99
+ def: m[4],
100
+ body: m[5],
101
+ quote: m[6],
102
+ semi: m[7],
103
+ });
104
+
105
+ // An existing `import { … } from '/patterns'` of each kind absorbs moved names.
106
+ const targets = {};
107
+ for (const m of source.matchAll(namedRe(SUBPATH))) {
108
+ if (m[2] !== 'import' || m[4]) continue;
109
+ targets[isType(m) ? 'type' : 'value'] ??= { m, add: [] };
110
+ }
111
+
112
+ const replacements = [];
113
+ for (const m of source.matchAll(namedRe(ROOT_PKG))) {
114
+ const p = parse(m);
115
+ const specs = splitSpecs(p.body);
116
+ const go = specs.filter((s) => names.has(specName(s)));
117
+ if (go.length === 0) continue;
118
+ moved.push(...go.map(specName));
119
+ const keep = specs.filter((s) => !go.includes(s));
120
+ const multiline = p.body.includes('\n');
121
+ const lines = [];
122
+ if (keep.length > 0 || p.def)
123
+ lines.push(render({ ...p, list: keep, pkg: ROOT_PKG, multiline }));
124
+ const target = p.kw === 'import' && targets[p.typeKw ? 'type' : 'value'];
125
+ if (target) target.add.push(...go);
126
+ else lines.push(render({ ...p, def: undefined, list: go, pkg: SUBPATH, multiline }));
127
+ replacements.push({ start: m.index, end: m.index + m[0].length, before: m[0], lines });
128
+ }
129
+
130
+ if (replacements.length === 0) return { source, changed: false, sites: 0, moved: [], edits: [] };
131
+
132
+ const sites = replacements.length;
133
+ for (const { m, add } of Object.values(targets)) {
134
+ if (add.length === 0) continue;
135
+ const p = parse(m);
136
+ const have = splitSpecs(p.body);
137
+ const merged = [...have, ...add.filter((n) => !have.includes(n))];
138
+ replacements.push({
139
+ start: m.index,
140
+ end: m.index + m[0].length,
141
+ before: m[0],
142
+ lines: [
143
+ render({
144
+ ...p,
145
+ def: undefined,
146
+ list: merged,
147
+ pkg: SUBPATH,
148
+ multiline: p.body.includes('\n'),
149
+ }),
150
+ ],
151
+ });
152
+ }
153
+
154
+ let out = source;
155
+ for (const r of replacements.sort((x, y) => y.start - x.start)) {
156
+ const after = r.lines.join('\n');
157
+ // A statement folded entirely into an existing import leaves nothing behind, not a blank line.
158
+ const end = after === '' && out[r.end] === '\n' ? r.end + 1 : r.end;
159
+ out = out.slice(0, r.start) + after + out.slice(end);
160
+ edits.unshift({ before: r.before, after });
161
+ }
162
+ return { source: out, changed: out !== source, sites, moved, edits };
163
+ }
164
+
165
+ function* walk(dir) {
166
+ for (const entry of readdirSync(dir)) {
167
+ if (SKIP_DIRS.has(entry)) continue;
168
+ const full = join(dir, entry);
169
+ const st = statSync(full);
170
+ if (st.isDirectory()) yield* walk(full);
171
+ else if (EXTS.has(full.slice(full.lastIndexOf('.')))) yield full;
172
+ }
173
+ }
174
+
175
+ /** Scan a directory. Writes only when `write` is true. */
176
+ export function runCodemod({ root, write = false, names = loadPatternNames() }) {
177
+ const files = [];
178
+ const manual = [];
179
+ const moved = new Set();
180
+ let sites = 0;
181
+ for (const file of walk(root)) {
182
+ const src = readFileSync(file, 'utf8');
183
+ if (!src.includes(ROOT_PKG)) continue;
184
+ for (const stmt of findUnrewritable(src)) manual.push({ file: relative(root, file), stmt });
185
+ const r = transformSource(src, names);
186
+ if (!r.changed) continue;
187
+ files.push({ file: relative(root, file), sites: r.sites, moved: r.moved, edits: r.edits });
188
+ r.moved.forEach((n) => moved.add(n));
189
+ sites += r.sites;
190
+ if (write) writeFileSync(file, r.source);
191
+ }
192
+ return { files, sites, manual, names: [...moved].sort() };
193
+ }
194
+
195
+ function main(argv) {
196
+ const args = { root: process.cwd(), check: false, dryRun: false };
197
+ for (let i = 0; i < argv.length; i++) {
198
+ if (argv[i] === '--root') args.root = resolve(argv[++i] ?? '');
199
+ else if (argv[i] === '--check') args.check = true;
200
+ else if (argv[i] === '--dry-run') args.dryRun = true;
201
+ else {
202
+ console.error(
203
+ `unknown argument: ${argv[i]}\nusage: patterns-subpath [--root <dir>] [--check] [--dry-run]`,
204
+ );
205
+ return 2;
206
+ }
207
+ }
208
+ const write = !args.check && !args.dryRun;
209
+ const res = runCodemod({ root: args.root, write });
210
+ const summary = `${res.files.length} files, ${res.sites} import sites, ${res.names.length} names (${res.names.join(', ') || 'none'})`;
211
+ const manualLines = res.manual.map(
212
+ (m) => ` ${m.file}: ${m.stmt} (namespace import: move pattern names to '${SUBPATH}' by hand)`,
213
+ );
214
+ if (args.check) {
215
+ if (res.sites > 0 || res.manual.length > 0) {
216
+ console.error(`patterns-subpath: rewrite needed: ${summary}`);
217
+ for (const f of res.files) console.error(` ${f.file}: ${f.moved.join(', ')}`);
218
+ for (const l of manualLines) console.error(l);
219
+ return 1;
220
+ }
221
+ console.log('patterns-subpath: nothing to rewrite');
222
+ return 0;
223
+ }
224
+ console.log(`patterns-subpath: ${write ? 'rewrote' : 'would rewrite'} ${summary}`);
225
+ for (const f of res.files) {
226
+ console.log(` ${f.file}: ${f.moved.join(', ')}`);
227
+ if (args.dryRun)
228
+ for (const e of f.edits) {
229
+ for (const l of e.before.split('\n')) console.log(`- ${l}`);
230
+ for (const l of e.after.split('\n')) console.log(`+ ${l}`);
231
+ }
232
+ }
233
+ if (res.manual.length > 0) {
234
+ console.warn('patterns-subpath: cannot rewrite these, do them by hand:');
235
+ for (const l of manualLines) console.warn(l);
236
+ }
237
+ return 0;
238
+ }
239
+
240
+ // Compare real paths: npm/yarn link the bin and pnpm links the package directory, so
241
+ // argv[1] is a symlink while import.meta.url is already resolved.
242
+ const isEntry = (() => {
243
+ try {
244
+ return (
245
+ !!process.argv[1] &&
246
+ realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))
247
+ );
248
+ } catch {
249
+ return false;
250
+ }
251
+ })();
252
+ if (isEntry) {
253
+ process.exit(main(process.argv.slice(2)));
254
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "subpath": "@hirobius/design-system/patterns",
3
+ "names": [
4
+ "ActivityFeed",
5
+ "AppShell",
6
+ "AssetImg",
7
+ "Calendar",
8
+ "Carousel",
9
+ "CodeBlock",
10
+ "CommandPalette",
11
+ "DocLinkCard",
12
+ "ErrorPattern",
13
+ "FileInput",
14
+ "Form",
15
+ "Lightbox",
16
+ "NavItem",
17
+ "OverflowList",
18
+ "Page",
19
+ "Reveal",
20
+ "SideNav",
21
+ "Stepper",
22
+ "Toolbar",
23
+ "TopNav",
24
+ "TreeList"
25
+ ]
26
+ }
@@ -0,0 +1,83 @@
1
+ 'use client';
2
+ import { jsx as r } from "react/jsx-runtime";
3
+ import { useContext as c, createContext as s, forwardRef as p, useState as v, useEffect as w, useCallback as u } from "react";
4
+ const l = s(null);
5
+ function E({ state: e, children: t }) {
6
+ return /* @__PURE__ */ r(l.Provider, { value: e, children: t });
7
+ }
8
+ function S() {
9
+ return c(l);
10
+ }
11
+ const x = p(
12
+ function({ to: t, children: n, ...i }, a) {
13
+ return /* @__PURE__ */ r("a", { ref: a, href: t, ...i, children: n });
14
+ }
15
+ ), d = s(null);
16
+ function A({ adapter: e, children: t }) {
17
+ return /* @__PURE__ */ r(d.Provider, { value: e, children: t });
18
+ }
19
+ function P() {
20
+ const e = c(d);
21
+ return e || {
22
+ navigate: (t) => {
23
+ typeof window < "u" && window.location.assign(t);
24
+ },
25
+ currentPath: typeof window < "u" ? window.location.pathname : "/",
26
+ LinkComponent: x
27
+ };
28
+ }
29
+ const g = {
30
+ en: {
31
+ language: "en",
32
+ dir: "ltr",
33
+ label: "English",
34
+ nativeLabel: "English"
35
+ }
36
+ }, f = s({
37
+ language: "en",
38
+ direction: "ltr",
39
+ isRtl: !1,
40
+ setLanguage: () => {
41
+ },
42
+ setDirection: () => {
43
+ },
44
+ toggleDirection: () => {
45
+ },
46
+ options: g
47
+ });
48
+ function b() {
49
+ try {
50
+ const e = localStorage.getItem("hds-direction");
51
+ if (e === "ltr" || e === "rtl") return e;
52
+ } catch {
53
+ }
54
+ return "ltr";
55
+ }
56
+ function R({ children: e }) {
57
+ const [t, n] = v(b), i = "en", a = t === "rtl";
58
+ w(() => {
59
+ try {
60
+ localStorage.setItem("hds-direction", t);
61
+ } catch {
62
+ }
63
+ document.documentElement.setAttribute("lang", "en"), document.documentElement.setAttribute("data-language", "en"), document.documentElement.setAttribute("dir", t), document.documentElement.setAttribute("data-reading-direction", t);
64
+ }, [t]);
65
+ const m = u((o) => {
66
+ }, []), L = u((o) => {
67
+ n(o);
68
+ }, []), h = u(() => {
69
+ n((o) => o === "ltr" ? "rtl" : "ltr");
70
+ }, []);
71
+ return /* @__PURE__ */ r(f.Provider, { value: { language: i, direction: t, isRtl: a, setLanguage: m, setDirection: L, toggleDirection: h, options: g }, children: e });
72
+ }
73
+ function H() {
74
+ return c(f);
75
+ }
76
+ export {
77
+ E as F,
78
+ A as H,
79
+ R as L,
80
+ P as a,
81
+ H as b,
82
+ S as u
83
+ };
@@ -0,0 +1,18 @@
1
+ 'use client';
2
+ import { jsx as r } from "react/jsx-runtime";
3
+ import { useState as i, useContext as a, createContext as u } from "react";
4
+ const o = u(void 0);
5
+ function c({ children: s }) {
6
+ const [t, e] = i(!1);
7
+ return /* @__PURE__ */ r(o.Provider, { value: { showCss: t, setShowCss: e, toggleCss: () => e((n) => !n) }, children: s });
8
+ }
9
+ function f() {
10
+ const s = a(o);
11
+ return s || { showCss: !1, setShowCss: () => {
12
+ }, toggleCss: () => {
13
+ } };
14
+ }
15
+ export {
16
+ c as T,
17
+ f as u
18
+ };