@hirobius/design-system 0.19.1 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +123 -0
- package/CONSUMING.md +11 -6
- package/DESIGN.md +34 -21
- package/NOTICE.md +1 -1
- package/README.md +48 -13
- package/codemods/hds-prefix.mjs +231 -0
- package/codemods/jsx-fold.mjs +515 -0
- package/codemods/not-found-pattern.mjs +71 -0
- package/codemods/patterns-subpath.mjs +242 -81
- package/codemods/patterns-subpath.names.json +41 -15
- package/codemods/removed-0.20.json +194 -0
- package/codemods/tile-grid.mjs +283 -0
- package/codemods/unrewritable.mjs +291 -0
- package/dist/chunks/DemoStateContext-Cz5TmrYu.js +32 -0
- package/dist/chunks/empty-state-DNoThgfV.js +1227 -0
- package/dist/chunks/form-By82qZcC.js +66 -0
- package/dist/contexts.js +157 -78
- package/dist/fonts/geist-mono-400.woff2 +0 -0
- package/dist/fonts/satoshi-400.woff2 +0 -0
- package/dist/fonts/satoshi-500.woff2 +0 -0
- package/dist/fonts/satoshi-700.woff2 +0 -0
- package/dist/fonts.css +53 -0
- package/dist/form.js +1 -1
- package/dist/hirobius-ui.js +1600 -5354
- package/dist/manifest.js +12 -13
- package/dist/patterns.js +796 -510
- package/dist/styles.css +2 -2
- package/dist/tokens.css +1 -1
- package/dist/tokens.js +165 -65
- package/dist/types/src/app/components/alert-dialog.d.ts +5 -0
- package/dist/types/src/app/components/asset-img.d.ts +1 -0
- package/dist/types/src/app/components/avatar-group.d.ts +2 -2
- package/dist/types/src/app/components/badge.d.ts +8 -3
- package/dist/types/src/app/components/blockquote.d.ts +2 -2
- package/dist/types/src/app/components/box-sx.d.ts +110 -13
- package/dist/types/src/app/components/box.d.ts +6 -3
- package/dist/types/src/app/components/breadcrumb.d.ts +0 -5
- package/dist/types/src/app/components/button.d.ts +35 -4
- package/dist/types/src/app/components/card.d.ts +10 -10
- package/dist/types/src/app/components/combobox.d.ts +47 -11
- package/dist/types/src/app/components/container.d.ts +2 -2
- package/dist/types/src/app/components/data-table-section.d.ts +2 -1
- package/dist/types/src/app/components/destructive-section.d.ts +1 -0
- package/dist/types/src/app/components/dialog.d.ts +8 -1
- package/dist/types/src/app/components/disclosure.d.ts +1 -5
- package/dist/types/src/app/components/error-pattern.d.ts +6 -1
- package/dist/types/src/app/components/field.d.ts +1 -2
- package/dist/types/src/app/components/form-actions.d.ts +1 -0
- package/dist/types/src/app/components/form-control.d.ts +10 -0
- package/dist/types/src/app/components/grid.d.ts +15 -6
- package/dist/types/src/app/components/hds-tooltip.d.ts +12 -2
- package/dist/types/src/app/components/icon.d.ts +1 -0
- package/dist/types/src/app/components/inline-code.d.ts +1 -2
- package/dist/types/src/app/components/input.d.ts +8 -5
- package/dist/types/src/app/components/kbd.d.ts +2 -2
- package/dist/types/src/app/components/metadata-list.d.ts +2 -2
- package/dist/types/src/app/components/metric-tiles.d.ts +1 -0
- package/dist/types/src/app/components/page-header.d.ts +0 -2
- package/dist/types/src/app/components/popover.d.ts +8 -0
- package/dist/types/src/app/components/progress.d.ts +33 -6
- package/dist/types/src/app/components/segmented-control.d.ts +2 -26
- package/dist/types/src/app/components/select.d.ts +10 -1
- package/dist/types/src/app/components/skeleton.d.ts +3 -4
- package/dist/types/src/app/components/spinner.d.ts +1 -2
- package/dist/types/src/app/components/stack.d.ts +4 -1
- package/dist/types/src/app/components/stat.d.ts +5 -5
- package/dist/types/src/app/components/status-list-item.d.ts +1 -2
- package/dist/types/src/app/components/status-tile.d.ts +3 -2
- package/dist/types/src/app/components/surface.d.ts +2 -3
- package/dist/types/src/app/components/switcher.d.ts +1 -1
- package/dist/types/src/app/components/table.d.ts +6 -15
- package/dist/types/src/app/components/tag.d.ts +0 -6
- package/dist/types/src/app/components/text.d.ts +54 -8
- package/dist/types/src/app/components/textarea.d.ts +1 -2
- package/dist/types/src/app/components/timestamp.d.ts +1 -0
- package/dist/types/src/app/components/toast.d.ts +1 -2
- package/dist/types/src/app/components/tooltip.d.ts +1 -1
- package/dist/types/src/app/context/DemoStateContext.d.ts +1 -1
- package/dist/types/src/app/context/RouterContext.d.ts +5 -5
- package/dist/types/src/app/data/manifest-types.d.ts +10 -5
- package/dist/types/src/app/design-system/generated-token-refs.d.ts +45 -28
- package/dist/types/src/app/design-system/generated-token-values.d.ts +4 -0
- package/dist/types/src/app/design-system/tokens.d.ts +139 -18
- package/dist/types/src/icons.d.ts +1 -1
- package/dist/types/src/index.d.ts +76 -164
- package/dist/types/src/patterns.d.ts +20 -25
- package/dist/variables.css +47 -33
- package/docs/CONSUMING.md +80 -48
- package/hirobius.tokens.json +125 -88
- package/llms.txt +166 -59
- package/mcp/catalog.mjs +366 -0
- package/mcp/guide.mjs +315 -0
- package/mcp/hds-mcp.mjs +49 -0
- package/mcp/server.mjs +94 -0
- package/package.json +48 -27
- package/public/hds-manifest.json +2667 -6705
- package/public/llms/components.txt +45 -426
- package/public/llms/layout.txt +5 -5
- package/public/llms-full.txt +245 -506
- package/public/llms.txt +166 -59
- package/scripts/eslint-plugin-hds/index.d.mts +29 -0
- package/scripts/eslint-plugin-hds/index.mjs +55 -0
- package/scripts/eslint-plugin-hds/package.json +30 -0
- package/scripts/eslint-plugin-hds/rules/no-raw-controls.mjs +47 -0
- package/scripts/eslint-plugin-hds/rules/no-raw-hex.mjs +70 -0
- package/scripts/eslint-plugin-hds/rules/no-raw-px-spacing.mjs +125 -0
- package/scripts/eslint-plugin-hds/rules/prefer-hds-layout-primitive.mjs +54 -0
- package/scripts/eslint-plugin-hds/rules/sx-token-first.mjs +111 -0
- package/scripts/eslint-plugin-hds/rules/utils.mjs +66 -0
- package/src/app/data/component-api.json +1082 -2913
- package/dist/chunks/LanguageContext-BxdCwiFg.js +0 -95
- package/dist/chunks/TokenDisplayContext-DKYJMVWk.js +0 -26
- package/dist/chunks/activity-feed-BGQ6v3wc.js +0 -22040
- package/dist/chunks/form-COP3vK9_.js +0 -66
- package/dist/types/src/app/components/activity-feed.d.ts +0 -73
- package/dist/types/src/app/components/app-shell.d.ts +0 -40
- package/dist/types/src/app/components/aspect-ratio.d.ts +0 -25
- package/dist/types/src/app/components/bleed.d.ts +0 -39
- package/dist/types/src/app/components/button-group.d.ts +0 -22
- package/dist/types/src/app/components/calendar.d.ts +0 -22
- package/dist/types/src/app/components/carousel.d.ts +0 -26
- package/dist/types/src/app/components/center.d.ts +0 -36
- package/dist/types/src/app/components/cinematic-link.d.ts +0 -18
- package/dist/types/src/app/components/circular-progress.d.ts +0 -28
- package/dist/types/src/app/components/cluster.d.ts +0 -39
- package/dist/types/src/app/components/command-palette.d.ts +0 -46
- package/dist/types/src/app/components/component-instance-matrix.d.ts +0 -27
- package/dist/types/src/app/components/context-menu.d.ts +0 -78
- package/dist/types/src/app/components/cover.d.ts +0 -43
- package/dist/types/src/app/components/date-input.d.ts +0 -31
- package/dist/types/src/app/components/date-range-input.d.ts +0 -34
- package/dist/types/src/app/components/date-time-input.d.ts +0 -30
- package/dist/types/src/app/components/doc-link-card.d.ts +0 -32
- package/dist/types/src/app/components/error-boundary.d.ts +0 -25
- package/dist/types/src/app/components/file-input.d.ts +0 -30
- package/dist/types/src/app/components/foundation-swatch.d.ts +0 -45
- package/dist/types/src/app/components/frame.d.ts +0 -38
- package/dist/types/src/app/components/heading-stack.d.ts +0 -52
- package/dist/types/src/app/components/history-card.d.ts +0 -12
- package/dist/types/src/app/components/hover-card.d.ts +0 -46
- package/dist/types/src/app/components/icon-button.d.ts +0 -22
- package/dist/types/src/app/components/image-lightbox.d.ts +0 -14
- package/dist/types/src/app/components/input-group.d.ts +0 -23
- package/dist/types/src/app/components/multi-selector.d.ts +0 -40
- package/dist/types/src/app/components/nav-group.d.ts +0 -29
- package/dist/types/src/app/components/nav-item.d.ts +0 -33
- package/dist/types/src/app/components/not-found-pattern.d.ts +0 -1
- package/dist/types/src/app/components/overflow-list.d.ts +0 -21
- package/dist/types/src/app/components/selectable-card.d.ts +0 -29
- package/dist/types/src/app/components/side-nav.d.ts +0 -23
- package/dist/types/src/app/components/sketch.d.ts +0 -19
- package/dist/types/src/app/components/stacked-card-rail.d.ts +0 -32
- package/dist/types/src/app/components/status-dot.d.ts +0 -25
- package/dist/types/src/app/components/stepper-field.d.ts +0 -12
- package/dist/types/src/app/components/stepper.d.ts +0 -33
- package/dist/types/src/app/components/text-lockup.d.ts +0 -38
- package/dist/types/src/app/components/tile-grid.d.ts +0 -17
- package/dist/types/src/app/components/time-input.d.ts +0 -24
- package/dist/types/src/app/components/toggle-button.d.ts +0 -32
- package/dist/types/src/app/components/token.d.ts +0 -90
- package/dist/types/src/app/components/tokenUtils.d.ts +0 -38
- package/dist/types/src/app/components/tokenizer.d.ts +0 -24
- package/dist/types/src/app/components/toolbar.d.ts +0 -36
- package/dist/types/src/app/components/top-nav.d.ts +0 -23
- package/dist/types/src/app/components/tree-list.d.ts +0 -30
- package/dist/types/src/app/data/nav-model.d.ts +0 -39
- package/dist/types/src/app/hooks/useFocusVisible.d.ts +0 -9
- package/dist/types/src/app/layouts/CaseStudyLayout.d.ts +0 -20
- package/dist/types/src/app/layouts/DocLayout.d.ts +0 -14
- package/dist/types/src/app/layouts/EmbeddedDocLayoutContext.d.ts +0 -6
- package/dist/types/src/app/layouts/HdsDocsShell.d.ts +0 -72
- package/dist/types/src/app/layouts/HdsSystemDocLayout.d.ts +0 -14
- package/dist/types/src/app/lib/hds-search.d.ts +0 -45
- package/dist/types/src/app/lib/nav-active.d.ts +0 -16
- package/dist/types/src/app/lib/navLevels.d.ts +0 -4
package/AGENTS.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# AGENTS.md — @hirobius/design-system
|
|
2
|
+
|
|
3
|
+
<!-- Generated by scripts/generate-agents-md.mjs from mcp/guide.mjs and src/app/data/component-api.json. Do not hand-edit above the repo-notes marker: pnpm agents:generate -->
|
|
4
|
+
|
|
5
|
+
For agents building UI with `@hirobius/design-system`. Read this first. It settles the choices the rest of the docs leave open, so two agents given the same screen build it the same way.
|
|
6
|
+
|
|
7
|
+
## Look things up, do not guess
|
|
8
|
+
|
|
9
|
+
- **MCP server `hds`** (stdio, ships in this package, no network): `npx hds-mcp` from an app that has the package installed. Client config:
|
|
10
|
+
`{ "mcpServers": { "hds": { "command": "npx", "args": ["hds-mcp"] } } }`
|
|
11
|
+
- Tools: `list_core` (call first), `search_components` (by name or need), `get_component` (props, usage contract, compound parts), `search_tokens` (by path, prefix or words). Each answer is under 2 KB.
|
|
12
|
+
- Without the server, the same data is in `src/app/data/component-api.json` (props, `usage`), `public/hds-manifest.json` (tokens, compound parts), `llms.txt` and `DESIGN.md` in this package.
|
|
13
|
+
|
|
14
|
+
## Imports
|
|
15
|
+
|
|
16
|
+
- `@hirobius/design-system`: `Alert`, `AlertDialog`, `Avatar`, `AvatarGroup`, `Badge`, `Blockquote`, `Box`, `Breadcrumb`, `Button`, `Card`, `Checkbox`, `Combobox`, `Container`, `Dialog`, `Disclosure`, `Divider`, `EmptyState`, `Field`, `Grid`, `HdsRouterProvider`, `HdsThemeProvider`, `Icon`, `InlineLink`, `Input`, `Kbd`, `Menu`, `MetadataList`, `Pagination`, `Pin`, `Popover`, `Progress`, `Radio`, `SegmentedControl`, `Select`, `Sidebar`, `Skeleton`, `Slider`, `Spinner`, `Stack`, `Surface`, `Switcher`, `Table`, `Tabs`, `TabsContent`, `TabsList`, `TabsTrigger`, `Tag`, `Text`, `Textarea`, `Timestamp`, `ToastProvider`, `Toggle`, `Tooltip`, `VisuallyHidden`, `useToast`.
|
|
17
|
+
- `@hirobius/design-system/patterns`: `DataTableSection`, `DestructiveSection`, `Form`, `FormActions`, `MetricTile`, `MetricTiles`, `Page`, `PageHeader`. These are not in the root barrel.
|
|
18
|
+
- Load one stylesheet once at the app root and put `data-hds` on the root element (`CONSUMING.md`).
|
|
19
|
+
|
|
20
|
+
## Pick by need
|
|
21
|
+
|
|
22
|
+
One answer per need. Use the components after the arrow; what follows "Not:" is what agents reached for instead, which is the wrong answer for that need even where it has a purpose of its own.
|
|
23
|
+
|
|
24
|
+
- **Outermost wrapper of a screen** → `Page`, `Stack`. Not: `Container`, `Box`, a bare Stack as the root.
|
|
25
|
+
`<Page><Stack gap="spacious">…one child per section…</Stack></Page>`. `Container` is for a full-bleed surface that is not a page.
|
|
26
|
+
- **Screen title with breadcrumb, status and actions** → `PageHeader`, `Breadcrumb`, `Badge`. Not: Text as the page title, a Stack row of Text and Badge.
|
|
27
|
+
Exactly one per screen: `<PageHeader breadcrumb={<Breadcrumb items={[{ label, href }, { label }]} />} title="…" status={<Badge tone="success">Active</Badge>} />`.
|
|
28
|
+
- **Row of headline numbers (stats, KPIs, counts, totals)** → `MetricTiles`, `MetricTile`. Not: `Stat`, `StatusTile`, `CardMetric`, Card with Text.
|
|
29
|
+
`<MetricTiles><MetricTile label="Open projects" value="5" />…</MetricTiles>`; one MetricTile per number. `Stat` is for a number inside prose or a dense list, `Card.Metric` for one inside an existing `Card`, `StatusTile` for a state with notes (never a number).
|
|
30
|
+
- **Table of records with row actions** → `DataTableSection`, `Badge`, `Button`. Not: Table on its own, Card around a Table.
|
|
31
|
+
`<DataTableSection title="Projects" columns={[{ key, label }]} rows={[{ key, cells: [...], actions: <Stack direction="row" gap="tight">…</Stack> }]} />`; leave the actions column out of `columns`.
|
|
32
|
+
- **Switch between panels of related content** → `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`. Not: `SegmentedControl`, Button rows.
|
|
33
|
+
`<Tabs defaultValue="a"><TabsList><TabsTrigger value="a">A</TabsTrigger></TabsList><TabsContent value="a">…</TabsContent></Tabs>`.
|
|
34
|
+
- **Status label in a header or table cell** → `Badge`. Not: `Tag`, `Text`.
|
|
35
|
+
`<Badge tone="success|info|warning|danger|neutral|inProgress">…</Badge>`; tone is the only styling input.
|
|
36
|
+
- **Form with fields and a submit button** → `Form`, `Textarea`, `Input`, `FormActions`, `Button`. Not: a raw `<form>`, a Stack of Buttons as the footer.
|
|
37
|
+
`<Form onSubmit={…}><Textarea label="Notes" … /><FormActions primary={<Button type="submit">Save note</Button>} /></Form>`; every field takes its own `label`.
|
|
38
|
+
- **Confirm a destructive or irreversible action (delete, archive, remove)** → `AlertDialog`, `Button`. Not: `Dialog`, `window.confirm`.
|
|
39
|
+
Trigger, title, description, Cancel and Action parts: `<AlertDialog><AlertDialog.Trigger asChild><Button …>Archive</Button></AlertDialog.Trigger><AlertDialog.Content><AlertDialog.Header><AlertDialog.Title>…</AlertDialog.Title><AlertDialog.Description>…</AlertDialog.Description></AlertDialog.Header><AlertDialog.Footer><AlertDialog.Cancel asChild><Button variant="secondary">Cancel</Button></AlertDialog.Cancel><AlertDialog.Action asChild><Button tone="danger" onClick={…}>Archive</Button></AlertDialog.Action></AlertDialog.Footer></AlertDialog.Content></AlertDialog>`. Focus moves in and returns to the trigger for you.
|
|
40
|
+
- **Brief confirmation after an action (saved, archived, sent)** → `ToastProvider`, `useToast`. Not: `Badge`, `Alert`, `Text`.
|
|
41
|
+
Wrap the app root once in `<ToastProvider>`, then `toast({ title: "Note saved", tone: "success" })` from `useToast()`.
|
|
42
|
+
- **Message that stays next to its content (error, warning, info)** → `Alert`. Not: Text in a coloured Surface.
|
|
43
|
+
`<Alert tone="warning">…</Alert>`.
|
|
44
|
+
- **Button or row action** → `Button`. Not: a raw `<button>`, InlineLink for an action.
|
|
45
|
+
Row actions: `<Button variant="tertiary" size="sm" aria-label="View Brand refresh">View</Button>`; the destructive one adds `tone="danger"`.
|
|
46
|
+
- **Space between sections or items** → `Stack`, `Grid`. Not: margins on children, raw px gaps such as px8.
|
|
47
|
+
Stack `gap`: `spacious` between page sections, `normal` inside a section, `tight` between buttons. `Grid` for two-dimensional layout.
|
|
48
|
+
- **Padded background or a titled content object** → `Surface`, `Card`. Not: a Box with background and padding.
|
|
49
|
+
`Surface` for a plain padded background; `Card` only when the content has a title or can be pressed.
|
|
50
|
+
- **Progress bar or ring** → `Progress`. Not: a hand-built bar.
|
|
51
|
+
`<Progress value={60} label="Upload" />`; inside an existing `Card`, `CardProgress`.
|
|
52
|
+
- **Body copy** → `Text`. Not: Text as a section heading.
|
|
53
|
+
Section headings come from the pattern that owns the section (`PageHeader` `title`, `DataTableSection` `title`), not from extra Text.
|
|
54
|
+
- **Empty state for a list or section** → `EmptyState`. Not: Text saying "No items".
|
|
55
|
+
`DataTableSection` shows its own through `emptyTitle`; use `EmptyState` elsewhere.
|
|
56
|
+
- **The one irreversible action of a screen (delete the client, close the account)** → `DestructiveSection`. Not: a danger Button that acts without a confirm.
|
|
57
|
+
Last on the screen: `<DestructiveSection title="Delete client" description="…" actionLabel="Delete" onConfirm={…} />`; it opens its own AlertDialog. A per-row destructive action uses AlertDialog directly.
|
|
58
|
+
- **Read-only details as label and value pairs** → `MetadataList`, `Field`. Not: a Table with two columns, Text pairs in a Stack.
|
|
59
|
+
`<MetadataList items={[{ term: "Owner", description: "Ada" }]} />` for a set; `Field` for one pair.
|
|
60
|
+
- **A person, or a group of people** → `Avatar`, `AvatarGroup`. Not: an image in a rounded Box.
|
|
61
|
+
`<Avatar src alt initials />`; several in a row: `<AvatarGroup max={3}>…Avatars…</AvatarGroup>`.
|
|
62
|
+
- **A date or time** → `Timestamp`. Not: a formatted string in Text.
|
|
63
|
+
`<Timestamp date="2026-10-14" />` renders a `<time dateTime>`; a date in a table cell may stay plain text.
|
|
64
|
+
- **A keyboard key or shortcut hint** → `Kbd`. Not: `InlineCode`, Text in a Box.
|
|
65
|
+
`<Kbd>⌘K</Kbd>`.
|
|
66
|
+
- **A quotation with attribution** → `Blockquote`. Not: Callout for a quote, italic Text.
|
|
67
|
+
`<Blockquote attribution="Ada Lovelace">…</Blockquote>`.
|
|
68
|
+
- **A fixed-width side rail beside fluid content, or a row that stacks when narrow** → `Sidebar`, `Switcher`. Not: a Grid with media queries.
|
|
69
|
+
`Sidebar` (`side`, `sideWidth`) for rail + content; `Switcher` (`threshold`) for a row that flips to a column. Neither needs a media query.
|
|
70
|
+
- **Keep an element stuck while its region scrolls** → `Pin`. Not: position: sticky in style.
|
|
71
|
+
`<Pin top="…">…</Pin>` inside the scrolling region.
|
|
72
|
+
|
|
73
|
+
## Rules
|
|
74
|
+
|
|
75
|
+
- Tokens only: no raw hex or rgb, no raw `px`, no Tailwind classes, no custom CSS files. Spacing comes from component props (`Stack` `gap`), colour from component `tone` props or `semantic.*` tokens (`search_tokens`).
|
|
76
|
+
- No raw `<button>`, `<input>`, `<select>`, `<textarea>` or `<form>`: use `Button`, `Input`, `Select`, `Textarea`, `Form`.
|
|
77
|
+
- Every control has an accessible name: fields take `label`, a repeated row action takes an `aria-label` naming its row.
|
|
78
|
+
- Section headings come from the pattern that owns the section (`title` props). Do not add a `Text` heading the screen does not ask for.
|
|
79
|
+
|
|
80
|
+
## Finish with the lint plugin
|
|
81
|
+
|
|
82
|
+
The ESLint plugin ships in this package. Pass its `recommended` rules before you report done:
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
// eslint.config.mjs
|
|
86
|
+
import hds from '@hirobius/design-system/eslint-plugin';
|
|
87
|
+
|
|
88
|
+
export default [...hds.configs.recommended];
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
<!-- hds:repo-notes — everything below is hand-written, for agents working in the hds repo itself; consumers can stop here. -->
|
|
92
|
+
|
|
93
|
+
## Working in the hds repo
|
|
94
|
+
|
|
95
|
+
Notes for agents working on this repository, not with the package. Read `CLAUDE.md` for
|
|
96
|
+
the full engineering protocol; this section is the home for autonomous-loop rules.
|
|
97
|
+
|
|
98
|
+
## Ralph quality bar
|
|
99
|
+
|
|
100
|
+
REPO_TYPE: library
|
|
101
|
+
QUALITY BAR: Public npm package. Backward compatibility matters. Tests + Storybook stories required.
|
|
102
|
+
|
|
103
|
+
Rules for any autonomous loop in this repo:
|
|
104
|
+
|
|
105
|
+
- One issue per PR. Small steps. Never push to main.
|
|
106
|
+
- The gate (ralph/gate.sh) must pass before any PR. No exceptions.
|
|
107
|
+
- Fight entropy: leave the code better than you found it.
|
|
108
|
+
- No shortcut that creates debt someone else pays for.
|
|
109
|
+
- Merges are HUMAN-approved: `ralph-approved` on the PR, or the issue
|
|
110
|
+
pre-tagged `ralph-auto` (batch approval). Never merge yourself.
|
|
111
|
+
|
|
112
|
+
Loop mechanics (see `ralph/README.md` for the full contract):
|
|
113
|
+
|
|
114
|
+
- Labels: `ralph-ready` (queue, human) · `p0`–`p3` (priority, human) ·
|
|
115
|
+
`ralph-auto`/`ralph-approved` (merge approval, human) · `ralph-wip`
|
|
116
|
+
(claimed, loop) · `ralph-parked`/`needs-adrian` (parked with reason, loop) ·
|
|
117
|
+
`ralph-selfheal-attempted` (gate's one bounded repair, spent).
|
|
118
|
+
- Selection is deterministic (`ralph/next.sh`: priority label, then issue #);
|
|
119
|
+
claims are atomic (`refs/heads/ralph/claim-<n>`); the loop's exit codes are
|
|
120
|
+
a contract (`ralph/run.sh` header). Agents never touch `ralph-*` labels,
|
|
121
|
+
claim refs, or `ralph/{.lock,runs.jsonl,logs/}` — the harness owns those.
|
|
122
|
+
- The engine is org-level: workflows are thin callers into
|
|
123
|
+
`hirobius/ralph/.github/workflows/ralph-{run,gate}-reusable.yml@v1`.
|
package/CONSUMING.md
CHANGED
|
@@ -29,6 +29,7 @@ version keeps the `LucideIcon` type identical).
|
|
|
29
29
|
```tsx
|
|
30
30
|
import { Button, Card, Dialog } from '@hirobius/design-system';
|
|
31
31
|
import '@hirobius/design-system/tokens.css'; // required — design tokens as CSS vars
|
|
32
|
+
import '@hirobius/design-system/fonts.css'; // optional — the HDS brand fonts (Satoshi, Geist Mono)
|
|
32
33
|
|
|
33
34
|
export function Example() {
|
|
34
35
|
return (
|
|
@@ -43,7 +44,8 @@ Available subpaths:
|
|
|
43
44
|
|
|
44
45
|
| Import | What |
|
|
45
46
|
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
46
|
-
| `@hirobius/design-system` |
|
|
47
|
+
| `@hirobius/design-system` | The 43-component core set ([README → What belongs in the system](README.md#what-belongs-in-the-system)), the rest of the allow-list and the templates; the `pattern` tier is only in `/patterns` |
|
|
48
|
+
| `@hirobius/design-system/fonts.css` | Optional brand fonts (Satoshi, Geist Mono); woff2 files ship in the package, relative URLs, `font-display: swap` |
|
|
47
49
|
| `@hirobius/design-system/tokens.css` | Token CSS variables (import once at app root) |
|
|
48
50
|
| `@hirobius/design-system/tokens` | Token values as TS constants |
|
|
49
51
|
| `@hirobius/design-system/cn` | `cn()` class-merge helper |
|
|
@@ -51,8 +53,8 @@ Available subpaths:
|
|
|
51
53
|
| `@hirobius/design-system/contexts` | Theme / language / tenant / font providers |
|
|
52
54
|
| `@hirobius/design-system/brand` | Palette → HDS-semantic overlay bridge (static / SSR / Astro; see [`docs/CONSUMING.md` §12](docs/CONSUMING.md#12-static-astro-sites--the-brand-overlay-bridge)) |
|
|
53
55
|
| `@hirobius/design-system/scroll` | Opt-in scroll-motion primitives — `SmoothScroll` (Lenis), `useScrollProgress` (Motion). Requires the optional peer `lenis`. See [`docs/CONSUMING.md` §13](docs/CONSUMING.md#13-scroll-motion-the-scroll-subpath) |
|
|
54
|
-
| `@hirobius/design-system/patterns` | The
|
|
55
|
-
| `@hirobius/design-system/icons` | Curated Lucide icon set for `
|
|
56
|
+
| `@hirobius/design-system/patterns` | The 12 pattern modules: the hds#254 `pattern` tier (page shells, forms, code blocks, page sections). The only entry that exports them: 0.20.0 removed their root re-exports (the `hds-patterns-subpath` codemod, see MIGRATIONS.md). |
|
|
57
|
+
| `@hirobius/design-system/icons` | Curated Lucide icon set for `Icon`, and so for `Button iconOnly` (`Ellipsis`, `Pencil`, `Trash2`, `X`, …). Names listed in the manifest `iconSet`. |
|
|
56
58
|
| `@hirobius/design-system/static.css` | CSS-only static-primitive layer — `.hds-badge`/`.hds-card`/`.hds-alert`/`.hds-divider`/`.hds-tag` classes, no React. See [`docs/CONSUMING.md` §14](docs/CONSUMING.md#14-css-only-static-primitives--badgecardalertdividertag-with-no-react) |
|
|
57
59
|
|
|
58
60
|
The package is **ESM-only**, so consume it with a modern bundler (Vite, Next.js,
|
|
@@ -113,8 +115,11 @@ Releases follow [semver](https://semver.org/) and are tracked in
|
|
|
113
115
|
pnpm update @hirobius/design-system # latest within your version range
|
|
114
116
|
```
|
|
115
117
|
|
|
116
|
-
For a
|
|
117
|
-
|
|
118
|
+
For a breaking release, bump the version explicitly and review the CHANGELOG
|
|
119
|
+
entry. Before 1.0 a breaking change (a removed export or token) ships in a 0.x
|
|
120
|
+
minor, which a caret range such as `^0.19` never picks up; from 1.0 on it ships
|
|
121
|
+
in a major. Each removal is listed in [MIGRATIONS.md](MIGRATIONS.md) with its
|
|
122
|
+
replacement and, where one applies, a codemod (ADR-014).
|
|
118
123
|
|
|
119
124
|
---
|
|
120
125
|
|
|
@@ -122,7 +127,7 @@ CHANGELOG entry. Token or export changes are released as majors.
|
|
|
122
127
|
|
|
123
128
|
```bash
|
|
124
129
|
pnpm changeset add # record a patch/minor/major bump + notes
|
|
125
|
-
pnpm changeset:version # apply bumps + regenerate CHANGELOG.md
|
|
130
|
+
pnpm changeset:version # apply bumps + regenerate CHANGELOG.md + refresh docs/api/api-baseline.json
|
|
126
131
|
# commit + push to main → the Release workflow publishes to public npm
|
|
127
132
|
```
|
|
128
133
|
|
package/DESIGN.md
CHANGED
|
@@ -28,28 +28,29 @@ Feedback hues are never decorative; do not use them as accents. Light and dark m
|
|
|
28
28
|
<!-- auto:start:typography -->
|
|
29
29
|
HDS ships three typefaces — each with a distinct and exclusive role:
|
|
30
30
|
|
|
31
|
-
- **Display / Heading font**: Satoshi. Bound exclusively to `display
|
|
31
|
+
- **Display / Heading font**: Satoshi. Bound exclusively to the `display` and `title` styles. Never used for body copy or UI labels.
|
|
32
32
|
- **Body / UI font**: Satoshi. All prose, labels, small text, captions, and UI copy.
|
|
33
33
|
- **Mono font**: Geist Mono. Reserved for tokens, code, technical callouts, and metric readouts.
|
|
34
34
|
|
|
35
|
-
Weights declared: `400` regular, `500` medium, `700` bold. Heading styles (display ·
|
|
35
|
+
Weights declared: `400` regular, `500` medium, `700` bold. Heading styles (display · title) use `700` bold; body, UI, and caption use `400` regular / `500` medium.
|
|
36
36
|
|
|
37
37
|
### Type ramp
|
|
38
38
|
|
|
39
39
|
| Role | Size (desktop max) | Weight | Use |
|
|
40
40
|
| --- | --- | --- | --- |
|
|
41
|
-
| `semantic.typography.display` |
|
|
42
|
-
| `semantic.typography.
|
|
43
|
-
| `semantic.typography.
|
|
44
|
-
| `semantic.typography.
|
|
45
|
-
| `semantic.typography.
|
|
46
|
-
| `semantic.typography.
|
|
47
|
-
| `semantic.typography.
|
|
48
|
-
| `semantic.typography.
|
|
49
|
-
| `semantic.typography.
|
|
41
|
+
| `semantic.typography.display` | 48px | 700 | One per page, hero only |
|
|
42
|
+
| `semantic.typography.title` | 24px | 700 | Section and card headings |
|
|
43
|
+
| `semantic.typography.body` | 16px | 400 | Prose |
|
|
44
|
+
| `semantic.typography.ui` | 14px | 500 | Controls, nav, table text, values |
|
|
45
|
+
| `semantic.typography.caption` | 12px | 500 | Metadata, help text and labels, in sentence case |
|
|
46
|
+
| `semantic.typography.mono` | 13px | 400 | Code and ids |
|
|
47
|
+
| `semantic.typography.h1` | 24px | 700 | deprecated: use semantic.typography.title |
|
|
48
|
+
| `semantic.typography.h2` | 24px | 700 | deprecated: use semantic.typography.title |
|
|
49
|
+
| `semantic.typography.h3` | 24px | 700 | deprecated: use semantic.typography.title |
|
|
50
|
+
| `semantic.typography.eyebrow` | 12px | 500 | deprecated: use semantic.typography.caption |
|
|
50
51
|
| `semantic.typography.lineHeight.none` | | | Semantic alias for leading-none (1) |
|
|
51
52
|
|
|
52
|
-
>
|
|
53
|
+
> The ramp is static: tokens store the value every surface renders. `h1`, `h2`, `h3` and `eyebrow` are deprecated aliases and go in 1.0.0.
|
|
53
54
|
<!-- auto:end:typography -->
|
|
54
55
|
|
|
55
56
|
## Spacing
|
|
@@ -59,9 +60,19 @@ Weights declared: `400` regular, `500` medium, `700` bold. Heading styles (displ
|
|
|
59
60
|
|
|
60
61
|
Scale: `0px` (`primitive.space.0`) · `1px` (`primitive.space.px1`) · `2px` (`primitive.space.px2`) · `4px` (`primitive.space.1`) · `6px` (`primitive.space.px6`) · `8px` (`primitive.space.2`) · `10px` (`primitive.space.px10`) · `12px` (`primitive.space.3`) · `16px` (`primitive.space.4`) · `20px` (`primitive.space.5`) · `24px` (`primitive.space.6`) · `28px` (`primitive.space.7`) · `32px` (`primitive.space.8`) · `40px` (`primitive.space.10`) · `48px` (`primitive.space.12`) · `64px` (`primitive.space.16`) · `80px` (`primitive.space.20`) · `96px` (`primitive.space.24`) · `128px` (`primitive.space.32`)
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
Layout rhythm, padding, and gaps use the `semantic.space.scale.{xs…xl}` steps (and `none`), never a raw `primitive.space.*` value or a numeric Tailwind class; `primitive.space.*` is the ramp behind the scale. Use `semantic.space.*` aliases (e.g. `semantic.space.surface.padding`) when the purpose is established. `data-density="compact"` (on `<html>` or the `[data-hds]` scope element) remaps `semantic.space.scale.*`, `semantic.space.surface.padding` and `semantic.space.region.gutter` one step down the scale, and `Table` follows it. The `--hds-space-{xs…4xl}` vars are a legacy bridge that no component reads.
|
|
63
64
|
<!-- auto:end:spacing -->
|
|
64
65
|
|
|
66
|
+
## Layout contract (v1)
|
|
67
|
+
|
|
68
|
+
Approved 2026-10-07. Data: `docs/guardrails/layout-contract.json`; gates: `pnpm check:layout-contract` and `pnpm check:spacing-scale` (warn mode; baseline in `docs/audits/layout-contract-baseline.md`).
|
|
69
|
+
|
|
70
|
+
1. **Sizing.** Every component declares `fill` or `hug` on each axis. Leaves hug. Input, Textarea, Select, Combobox, Table, Progress and Divider fill width. One override prop, `width="hug|fill"`, on every component. Height always hugs; stretching is the parent's job (`align="stretch"`). No component sets `h-full`, `height: 100%` or `self-*` on its root.
|
|
71
|
+
2. **Padding.** Only containers pad: Card, Surface, Alert, Callout, Dialog, Page. Compound parts (`Card.Header`, `Body`, `Footer`, `Metric`) have zero padding and the container's `gap` spaces them. Padding is `none | sm | md`, default `md`, density-aware. A padded container directly inside a padded container is an error unless the inner one is `padding="none"`.
|
|
72
|
+
3. **Spacing.** One scale, `xs…xl` (plus `none`), for every `gap` and `padding` prop. Every gap defaults to `md`. No outer margins on any component; space between siblings is the parent's `gap`.
|
|
73
|
+
4. **Primitives.** Stack, Grid, Page (absorbs Container) and Surface compose layouts. Box and Pin are escape hatches. Sidebar and Switcher fold into Stack and Grid.
|
|
74
|
+
5. **Forms.** A form control in a form context has a max width (`40rem` by default) and siblings share one width.
|
|
75
|
+
|
|
65
76
|
## Corner-Radius Policy
|
|
66
77
|
|
|
67
78
|
Shape is one knob. `semantic.radius.action` is the system's single shape value — interactive controls (buttons, inputs, badges, alerts, segmented items) all resolve to it, and Tailwind's `rounded-md` maps to it exactly so the utility and the token cannot disagree. Containers sit one step above at `rounded-lg` (`action + 4px`); `rounded-sm` (`action - 2px`) is for chrome nested inside a control. `full` is reserved for pills and circular forms, and `0px` only for intentional outer canvas or substrate boundaries, never everyday UI controls. Reshaping the whole system means overriding `role.radius` in one tenant overlay — that is how `brutalist-demo` goes square in a single value.
|
|
@@ -90,7 +101,7 @@ Depth is communicated through 4 elevation roles bundled by `semantic.elevation.*
|
|
|
90
101
|
|
|
91
102
|
Cards default to `flat`. They lift to `raised` only on interactive hover or when explicitly elevated above siblings. Never combine `raised` with a border — depth is one mechanism (border OR shadow), not both stacked.
|
|
92
103
|
|
|
93
|
-
Overlays (Dialog, AlertDialog, Menu,
|
|
104
|
+
Overlays (Dialog, AlertDialog, Menu, Popover, Select, Tooltip) portal into the nearest `data-hds` scope, so they inherit its theme (for example `<div data-hds data-theme="dark">`); pass `container` on the Content part to override.
|
|
94
105
|
|
|
95
106
|
## Motion
|
|
96
107
|
|
|
@@ -121,7 +132,7 @@ Default most interactive feedback to `productive` (150ms, decelerate). Reserve `
|
|
|
121
132
|
|
|
122
133
|
| Token | Value | Purpose |
|
|
123
134
|
| --- | --- | --- |
|
|
124
|
-
| `semantic.motion.distance` | `24px` |
|
|
135
|
+
| `semantic.motion.distance` | `24px` | Scroll-reveal translateY travel distance. Equals semantic.space.scale.md today, but names motion travel, not a spacing tier (hds#242). |
|
|
125
136
|
<!-- auto:end:motion -->
|
|
126
137
|
|
|
127
138
|
## Components
|
|
@@ -129,7 +140,7 @@ Default most interactive feedback to `productive` (150ms, decelerate). Reserve `
|
|
|
129
140
|
<!-- auto:start:components -->
|
|
130
141
|
| Component | Radius | States | Guidance |
|
|
131
142
|
| --- | --- | --- | --- |
|
|
132
|
-
| **Buttons** (`Button`) | `8px` (`semantic.radius.action`) | default · hover · focus · active · disabled · loading | Three variants: primary (accent-filled), secondary (outline), tertiary (ghost). Primary uses `semantic.accent.*` ramp per state. Icon buttons (`
|
|
143
|
+
| **Buttons** (`Button`) | `8px` (`semantic.radius.action`) | default · hover · focus · active · disabled · loading | Three variants: primary (accent-filled), secondary (outline), tertiary (ghost). Primary uses `semantic.accent.*` ramp per state. Pressed is a 5% `semantic.color.state.pressed.overlay` wash over the fill (`role.pressed-overlay`: black in light, white in dark), not a brightness filter. Icon-only buttons (`iconOnly`) follow the same token surface. Icon-only actions use `Button iconOnly` with a `label` and an `Icon` from `@hirobius/design-system/icons` in `iconLeft`. |
|
|
133
144
|
| **Inputs** (`Input`) | `8px` (`semantic.radius.action`) | default · focus · filled · error · disabled · loading | Border-driven treatment; no filled background by default. Focus uses `semantic.color.border.accent` plus a 2px outline offset. Error swaps to `component.input.borderError`. |
|
|
134
145
|
| **Cards** (`Card`) | `12px` (`rounded-lg`, role radius + 4px) | default · hover (optional parallax) · pressed (when interactive) | Cards default to `elevation.flat` (1px border `border.subtle`, no shadow). Interactive cards lift to `elevation.raised` (shadow.subtle, no border) on hover. Bind via `semantic.elevation.{role}` — never raw box-shadow values. Radius: `rounded-lg` (role radius + 4 px, follows the tenant knob) — never a hard-coded value. Padding: `var(--semantic-space-surface-padding)`. Title: `heading3`. Meta: `caption` + `var(--semantic-color-content-secondary)`. Hover (interactive): `scale(1.02)` transform + lift to raised. Never: gradients, glow, frosted glass, tinted surfaces, decorative overlays, or inner shadows. |
|
|
135
146
|
| **Badges** (`Badge`) | `8px` (`primitive.radius.4`) | neutral · accent · feedback (error/success/warning/info) | Single-line status markers. Feedback colors come from `semantic.color.feedback.*`. Never used as decorative chrome. |
|
|
@@ -152,7 +163,7 @@ Every HDS card surface must conform to this anatomy exactly. No creative interpr
|
|
|
152
163
|
| Border radius | `rounded-lg` (`role.radius` + 4 px: 12 px by default, follows the tenant) | `var(--component-card-radius)`, `rounded-full`, or any hard-coded value |
|
|
153
164
|
| Padding | `var(--semantic-space-component-padding)` or `<HdsSurface padding="component">` | Raw pixel values or ad hoc insets |
|
|
154
165
|
| Shadow | Resting cards: none (`elevation.flat`). Interactive lifted state: `shadow.subtle` via `elevation.raised` | Raw `box-shadow` values, `drop-shadow`, glow, or any depth effect not bound to a role token |
|
|
155
|
-
| Title | `hds.typeStyles.
|
|
166
|
+
| Title | `hds.typeStyles.title` / `<Text variant="title">` | Any other type style for the primary card heading |
|
|
156
167
|
| Subtitle / meta | `hds.typeStyles.caption` + `var(--semantic-color-content-secondary)` | Primary content color or body size for secondary text |
|
|
157
168
|
| Hover (interactive only) | `transform: scale(1.02)` | Background fill change, border color shift, or opacity fade on hover |
|
|
158
169
|
|
|
@@ -164,7 +175,7 @@ Five patterns sit above the primitives so that every screen is composed the same
|
|
|
164
175
|
|
|
165
176
|
### Page title rule
|
|
166
177
|
|
|
167
|
-
Every screen has exactly one `PageHeader`. The page title is `
|
|
178
|
+
Every screen has exactly one `PageHeader`. The page title is `title` (24px), a fixed size with no size prop; `level` changes only the DOM heading element. `display` is reserved for marketing and landing surfaces and is never a screen title. Breadcrumb goes in the `breadcrumb` slot, one status `Badge` in `status`, screen-level actions in `actions`.
|
|
168
179
|
|
|
169
180
|
### Which one, when
|
|
170
181
|
|
|
@@ -176,14 +187,16 @@ Pick by what the thing is. The first four rows are for a headline number or a st
|
|
|
176
187
|
| `Stat` | An inline number inside prose or a dense list. | `MetricTiles` when the number stands alone in a row of its own. |
|
|
177
188
|
| `Card.Metric` | A metric inside an existing `Card` only. | `MetricTiles` when there is no enclosing `Card`. |
|
|
178
189
|
| `StatusTile` | State with notes and a trailing badge, never a number. | `MetricTiles` when the tile is a number; `StatusTile` never carries a value. |
|
|
179
|
-
| `Card` | A titled or interactive content object (header, body, footer,
|
|
190
|
+
| `Card` | A titled or interactive content object (header, body, footer, `selectable`). | `Surface` when there is no title and nothing to press. |
|
|
180
191
|
| `Surface` | A plain padded background around content that has no header of its own. | `Card` when the content is titled or interactive. |
|
|
181
192
|
| `DestructiveSection` | The one irreversible action on a screen, with an explanation and a confirm. | `FormActions` `destructive` slot for a delete beside a form's own buttons. |
|
|
182
193
|
| `DataTableSection` | A titled table with a toolbar, row actions and an empty state. | `Table` alone when there is no heading, toolbar or empty state to show. |
|
|
183
194
|
|
|
195
|
+
Three more needs are props on an existing component, not components of their own. A card people pick from a set is a `Card` with `selectable`, `selected` and `onSelectedChange`, announced as a checkbox. A wrapping row of equal tiles that are not headline numbers is a `Grid` with `layout="auto-fill"` and `minItemWidth`. A state shown only as a colored marker, with no notes, is a `Badge` with `dot` and a `label` that names the state.
|
|
196
|
+
|
|
184
197
|
`Card` and `Surface` share one container radius, `rounded-lg` (`role.radius` + 4 px: 12 px by default, 4 px under `brutalist-demo`). Dialogs and alert dialogs use it too. Never read `--component-card-radius` for a container; it ignores the tenant.
|
|
185
198
|
|
|
186
|
-
`MetricTiles` has `min(tiles, 4)` columns, so a row never leaves an empty column, and every tile has one fixed min-height with the value in `
|
|
199
|
+
`MetricTiles` has `min(tiles, 4)` columns, so a row never leaves an empty column, and every tile has one fixed min-height with the value in `title`, the label in `caption` and the sub line in `caption`. Its tone comes from the fixed feedback vocabulary: `neutral | success | warning | danger | info`.
|
|
187
200
|
|
|
188
201
|
### Destructive and table sections
|
|
189
202
|
|
|
@@ -215,6 +228,6 @@ Unresolved rules that the live repo cannot yet answer confidently are tracked in
|
|
|
215
228
|
<!-- auto:start:build-meta -->
|
|
216
229
|
---
|
|
217
230
|
|
|
218
|
-
> Generated from `hirobius.tokens.json` (
|
|
231
|
+
> Generated from `hirobius.tokens.json` (389 tokens) and `public/hds-manifest.json` by `scripts/build-design-md.mjs`.
|
|
219
232
|
> Hand-edit `DESIGN.source.md`; this file (`DESIGN.md`) is overwritten by `pnpm tokens`.
|
|
220
233
|
<!-- auto:end:build-meta -->
|
package/NOTICE.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
`@hirobius/design-system` is not under a single license.
|
|
4
4
|
|
|
5
5
|
- **HDS code** (components, compiled JavaScript, types, CSS, and token files): MIT. See [LICENSE](LICENSE).
|
|
6
|
-
- **Satoshi** (weights 500 and 700, by Indian Type Foundry, distributed through [Fontshare](https://www.fontshare.com/fonts/satoshi)): ITF Free Font License.
|
|
6
|
+
- **Satoshi** (weights 400, 500 and 700, by Indian Type Foundry, distributed through [Fontshare](https://www.fontshare.com/fonts/satoshi)): ITF Free Font License.
|
|
7
7
|
- **Geist Mono** (weight 400, by Vercel, [source](https://github.com/vercel/geist-font)): SIL Open Font License 1.1.
|
|
8
8
|
|
|
9
9
|
Both fonts are embedded as base64 `@font-face` data in `dist/tokens.css` and `dist/styles.css`. The MIT license does not cover them; each font keeps its own license. `dist/variables.css` embeds no fonts.
|
package/README.md
CHANGED
|
@@ -12,9 +12,9 @@ pnpm add @hirobius/design-system
|
|
|
12
12
|
|
|
13
13
|
<!-- auto:start:front-door-counts -->
|
|
14
14
|
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
15
|
+
- **53** public component modules, exported from `src/index.ts`
|
|
16
|
+
- **389** DTCG tokens in `hirobius.tokens.json`, compiled to CSS variables and TypeScript constants
|
|
17
|
+
- **341** Storybook stories in **71** story files
|
|
18
18
|
|
|
19
19
|
<!-- auto:end:front-door-counts -->
|
|
20
20
|
|
|
@@ -29,9 +29,12 @@ Agents: install the consumer skill with `npx skills add hirobius/hds --skill hds
|
|
|
29
29
|
|
|
30
30
|
<!-- auto:start:consumer-usage -->
|
|
31
31
|
|
|
32
|
-
The Ops dashboard (`hirobius/ops`) is the only
|
|
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
|
-
|
|
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
|
[](docs/images/ops-library.png)
|
|
37
40
|
|
|
@@ -39,7 +42,36 @@ Other consumers: the split into product apps and token-level sites is not yet co
|
|
|
39
42
|
|
|
40
43
|
<!-- auto:end:consumer-usage -->
|
|
41
44
|
|
|
42
|
-
The numbers come from `pnpm consumer:usage --root <ops checkout>`, which measures that checkout and records its commit in `docs/data/consumer-usage.json
|
|
45
|
+
The numbers come from `pnpm consumer:usage --root <ops checkout>`, which measures that checkout's `src/` and records its commit in `docs/data/consumer-usage.json` (pass `--commit <sha>` when the root is a `git archive` export); without a root it reuses the committed snapshot. A deprecated alias counts as the component it points to (`HdsCheckbox` as `Checkbox`). The components that Ops's page- and video-clone prompts tell generated code to import are listed there as `promptContracts` and are not counted here. `scripts/__tests__/consumer-usage.test.mjs` fails if this section drifts from that snapshot. The product-app and token-level-site split is a declared figure in that file, not a measurement. It was confirmed on 2026-10-01 (hds#389: there is no second product app), so `consumersConfirmed` there is true and the table above shows it.
|
|
46
|
+
|
|
47
|
+
## What belongs in the system
|
|
48
|
+
|
|
49
|
+
<!-- auto:start:core-set -->
|
|
50
|
+
|
|
51
|
+
**43** components are the core set: the brand-neutral, composable surface every HDS screen is built from, and the part of the system that has to be excellent. Import them from the package root; each carries `core: true` in `public/hds-manifest.json`.
|
|
52
|
+
|
|
53
|
+
- **Actions:** `Button`
|
|
54
|
+
- **Display:** `Avatar`, `Card`, `EmptyState`, `Field`, `Icon`, `Kbd`, `Table`
|
|
55
|
+
- **Feedback:** `Alert`, `Badge`, `Progress`, `Skeleton`, `Spinner`, `ToastProvider`
|
|
56
|
+
- **Inputs:** `Checkbox`, `Combobox`, `Input`, `Radio`, `SegmentedControl`, `Select`, `Slider`, `Tag`, `Textarea`, `Toggle`
|
|
57
|
+
- **Layout:** `Box`, `Container`, `Disclosure`, `Divider`, `Grid`, `Stack`, `Surface`
|
|
58
|
+
- **Navigation:** `Breadcrumb`, `InlineLink`, `Pagination`, `Tabs`
|
|
59
|
+
- **Overlays:** `Dialog`, `Menu`, `Popover`, `Tooltip`
|
|
60
|
+
- **Theming:** `HdsRouterProvider`, `HdsThemeProvider`
|
|
61
|
+
- **Typography:** `Text`
|
|
62
|
+
- **Utility:** `VisuallyHidden`
|
|
63
|
+
|
|
64
|
+
**12** pattern modules ship from `@hirobius/design-system/patterns`, and only from there: composed surfaces that assume a product (page shells, forms, code blocks, page sections), built from the core set. One name per module; its parts and props types come with it.
|
|
65
|
+
|
|
66
|
+
`AssetImg`, `CodeBlock`, `DataTableSection`, `DestructiveSection`, `ErrorPattern`, `Form`, `FormActions`, `MetricTiles`, `Page`, `PageHeader`, `Reveal`, `StatusTile`
|
|
67
|
+
|
|
68
|
+
The package root also exports the rest of the allow-list (compound parts, and the components outside the core set; the [consumer skill](skills/hds-consumer/SKILL.md) lists them by category): reach for the core set first. 0.20.0 removed the root re-exports of the pattern modules, the docs internals and 13 components that fold into a survivor ([MIGRATIONS.md](MIGRATIONS.md#0200-removals-2026-10-01)).
|
|
69
|
+
|
|
70
|
+
Every component's disposition (core, pattern, fold or internal) is in the [architecture doc](docs/hds-architecture-2026-09-18.html), ratified 2026-09-26 in [hds#254](https://github.com/hirobius/hds/issues/254); [ADR-031](docs/adr/031-core-set-and-dispositions.md) records the decision and what has changed since.
|
|
71
|
+
|
|
72
|
+
<!-- auto:end:core-set -->
|
|
73
|
+
|
|
74
|
+
This section is generated by `pnpm readme:counts` (which `pnpm tokens` also runs) from `scripts/lib/core-components.mjs`, `src/patterns.ts` and the manifest. `scripts/__tests__/core-set.test.mjs` fails if it drifts from them.
|
|
43
75
|
|
|
44
76
|
## Using the published package
|
|
45
77
|
|
|
@@ -51,7 +83,7 @@ guide: **[docs/CONSUMING.md](docs/CONSUMING.md)**.
|
|
|
51
83
|
> `@hirobius/design-system` entry under GitHub Packages
|
|
52
84
|
> (`/pkgs/npm/design-system`). That listing is **frozen and no longer updated** —
|
|
53
85
|
> publishing moved to the public npm registry in
|
|
54
|
-
> [#39](https://github.com/hirobius/
|
|
86
|
+
> [#39](https://github.com/hirobius/hds/pull/39). Always
|
|
55
87
|
> install from npm:
|
|
56
88
|
> **[npmjs.com/package/@hirobius/design-system](https://www.npmjs.com/package/@hirobius/design-system)**.
|
|
57
89
|
|
|
@@ -63,8 +95,9 @@ npm install @hirobius/design-system react react-dom
|
|
|
63
95
|
```
|
|
64
96
|
|
|
65
97
|
```tsx
|
|
66
|
-
// once at the app root — full bundle: tokens + theme + utilities
|
|
98
|
+
// once at the app root — full bundle: tokens + theme + utilities
|
|
67
99
|
import '@hirobius/design-system/tokens.css';
|
|
100
|
+
import '@hirobius/design-system/fonts.css'; // optional: the HDS brand fonts
|
|
68
101
|
import { Button } from '@hirobius/design-system';
|
|
69
102
|
|
|
70
103
|
// add data-hds to the root (or any section) so the scoped base styles apply
|
|
@@ -153,7 +186,7 @@ pnpm build-storybook
|
|
|
153
186
|
|
|
154
187
|
HDS is built around three structural rules:
|
|
155
188
|
|
|
156
|
-
- **Strict semantics** — public surfaces prefer system primitives such as `Stack`, `Grid`, `Surface`, and `
|
|
189
|
+
- **Strict semantics** — public surfaces prefer system primitives such as `Stack`, `Grid`, `Surface`, and `Text` instead of raw layout divs or ad hoc CSS.
|
|
157
190
|
- **Polymorphism** — primitives preserve semantic HTML while staying composable through governed APIs such as `forwardRef`, `as`, and layout slots.
|
|
158
191
|
- **12-column grid** — page structure follows a consistent editorial grid: readable center columns, intentional breakout zones, and explicit `gap` ownership rather than one-off spacing math.
|
|
159
192
|
|
|
@@ -165,6 +198,8 @@ Source-of-truth files:
|
|
|
165
198
|
- `DESIGN.md` — lean visual spec for agents and engineers.
|
|
166
199
|
- `DESIGN-HANDOFF.md` — verbose visual mirror for handoff and review.
|
|
167
200
|
|
|
201
|
+
Which components belong in the system, and what happens to the rest, is decided in the [architecture doc](docs/hds-architecture-2026-09-18.html): every component is core, pattern, fold or internal, ratified 2026-09-26 in hds#254 and recorded in [ADR-031](docs/adr/031-core-set-and-dispositions.md). [What belongs in the system](#what-belongs-in-the-system) above lists the result.
|
|
202
|
+
|
|
168
203
|
Component-by-component Figma links live in [docs/DESIGN_LINKS.md](docs/DESIGN_LINKS.md), generated by `pnpm figma:links`.
|
|
169
204
|
|
|
170
205
|
## Accessibility
|
|
@@ -173,10 +208,10 @@ What CI and the hooks check today, and nothing more:
|
|
|
173
208
|
|
|
174
209
|
- **Contrast:** `scripts/check-contrast.mjs` enforces WCAG AA contrast on the core token pairs, in light and dark. It runs in CI and the pre-commit hook.
|
|
175
210
|
- **Lint:** `jsx-a11y` rules run inside ESLint with zero warnings allowed.
|
|
176
|
-
- **Focus:** `scripts/check-focus-states.mjs` (`pnpm check:focus`) audits focus styles on interactive components. It runs in
|
|
177
|
-
- **Storybook:** the `addon-a11y` panel shows axe results for each story while you review it.
|
|
211
|
+
- **Focus:** `scripts/check-focus-states.mjs` (`pnpm check:focus`) audits focus styles on interactive components. It runs in `pretest` under `pnpm test`, so in the pre-push hook and in CI, and passes with 0 violations.
|
|
212
|
+
- **Storybook:** the `addon-a11y` panel shows axe results for each story while you review it. The gate is `scripts/check-storybook-axe.mjs`: CI scans every built story in light and dark, including the open Select and Combobox, and fails on serious or critical violations outside its allowlist (`scripts/axe-allowlist.json`). The allowlist has one entry: `aria-hidden-focus` on the open Select story, where Radix Select hides the page around its listbox while focus stays trapped inside it (hds#407).
|
|
178
213
|
|
|
179
|
-
|
|
214
|
+
Screen-reader output is checked by `tests/primitive-contracts/screen-reader.contract.test.tsx`, which runs in `pnpm test` and walks Dialog, Menu, Select, Combobox, Table and Alert with a virtual screen-reader (`@guidepup/virtual-screen-reader`) under jsdom, asserting the role, name and state phrases it reads and the alert's live-region announcement; it reads the jsdom accessibility tree, not what a desktop or mobile screen-reader says.
|
|
180
215
|
|
|
181
216
|
## Visual direction: Editorial Enterprise
|
|
182
217
|
|
|
@@ -201,7 +236,7 @@ The gates are deterministic and need no browser or live site:
|
|
|
201
236
|
Three agents build the same Client-detail screen from the public docs, and `pnpm eval:consistency` measures how alike the results are: builds, token violations, axe, component-set overlap (Jaccard) and pixel diff. Today it runs the offline half over a directory of apps (`pnpm eval:consistency -- --apps <dir> --offline`, violations and Jaccard from source only). The ledger, the thresholds and the screen spec live in [`eval/consistency/`](eval/consistency/README.md). The latest recorded run, as printed by `pnpm eval:consistency -- --summary`:
|
|
202
237
|
|
|
203
238
|
```text
|
|
204
|
-
Agent consistency 2026-
|
|
239
|
+
Agent consistency 2026-10-05 (harness, design-system 0.20.0): FAIL - builds 3/3, violations 0, axe 0, Jaccard min 0.4091 (limit >= 0.85), light diff max 3.2096% (limit <= 1.5%)
|
|
205
240
|
```
|
|
206
241
|
|
|
207
242
|
The baseline misses its thresholds on purpose: they are not softened to make it pass.
|