@hirobius/design-system 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +125 -0
- package/CHANGELOG.md +969 -0
- package/CONSUMING.md +27 -11
- package/DESIGN.md +28 -17
- package/MIGRATIONS.md +603 -0
- package/NOTICE.md +1 -1
- package/README.md +39 -5
- package/UPGRADING.md +222 -0
- package/codemods/hds-prefix.mjs +28 -13
- package/codemods/jsx-fold.mjs +28 -13
- package/codemods/lib/atomic-write.mjs +28 -0
- package/codemods/lib/installed-version.mjs +354 -0
- package/codemods/lib/project.mjs +395 -0
- package/codemods/lib/record.mjs +110 -0
- package/codemods/lib/scan.mjs +323 -0
- package/codemods/lib/semver.mjs +57 -0
- package/codemods/not-found-pattern.mjs +3 -3
- package/codemods/patterns-subpath.mjs +28 -12
- package/codemods/registry.mjs +67 -0
- package/codemods/tile-grid.mjs +3 -3
- package/codemods/upgrade.mjs +909 -0
- package/dist/chunks/empty-state-DNoThgfV.js +1227 -0
- package/dist/chunks/form-By82qZcC.js +66 -0
- 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 +876 -877
- package/dist/manifest.js +3 -3
- package/dist/patterns.js +189 -170
- package/dist/styles.css +2 -2
- package/dist/tokens.css +1 -1
- package/dist/tokens.js +136 -57
- package/dist/types/src/app/components/alert-dialog.d.ts +5 -0
- package/dist/types/src/app/components/alert.d.ts +1 -1
- package/dist/types/src/app/components/asset-img.d.ts +1 -0
- package/dist/types/src/app/components/avatar-group.d.ts +1 -0
- package/dist/types/src/app/components/avatar.d.ts +1 -1
- package/dist/types/src/app/components/badge.d.ts +2 -2
- package/dist/types/src/app/components/blockquote.d.ts +1 -0
- package/dist/types/src/app/components/breadcrumb.d.ts +1 -1
- package/dist/types/src/app/components/button.d.ts +3 -1
- package/dist/types/src/app/components/callout.d.ts +1 -1
- package/dist/types/src/app/components/card.d.ts +4 -3
- package/dist/types/src/app/components/checkbox.d.ts +1 -1
- package/dist/types/src/app/components/code-block.d.ts +1 -1
- package/dist/types/src/app/components/combobox.d.ts +1 -1
- package/dist/types/src/app/components/data-table-section.d.ts +1 -0
- package/dist/types/src/app/components/destructive-section.d.ts +1 -0
- package/dist/types/src/app/components/dialog.d.ts +2 -1
- package/dist/types/src/app/components/disclosure.d.ts +1 -1
- package/dist/types/src/app/components/divider.d.ts +1 -1
- package/dist/types/src/app/components/empty-state.d.ts +1 -1
- package/dist/types/src/app/components/error-pattern.d.ts +6 -1
- package/dist/types/src/app/components/field.d.ts +1 -1
- 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/form.d.ts +1 -1
- package/dist/types/src/app/components/grid.d.ts +5 -0
- package/dist/types/src/app/components/hds-tooltip.d.ts +1 -1
- package/dist/types/src/app/components/icon.d.ts +1 -0
- package/dist/types/src/app/components/inline-link.d.ts +1 -1
- package/dist/types/src/app/components/input.d.ts +1 -1
- package/dist/types/src/app/components/kbd.d.ts +1 -0
- package/dist/types/src/app/components/menu.d.ts +1 -1
- package/dist/types/src/app/components/metadata-list.d.ts +1 -0
- package/dist/types/src/app/components/metric-tiles.d.ts +1 -0
- package/dist/types/src/app/components/pagination.d.ts +1 -1
- package/dist/types/src/app/components/popover.d.ts +1 -1
- package/dist/types/src/app/components/progress.d.ts +2 -2
- package/dist/types/src/app/components/radio.d.ts +1 -1
- package/dist/types/src/app/components/select.d.ts +1 -1
- package/dist/types/src/app/components/skeleton.d.ts +2 -2
- package/dist/types/src/app/components/slider.d.ts +1 -1
- package/dist/types/src/app/components/spinner.d.ts +1 -1
- package/dist/types/src/app/components/stat.d.ts +5 -4
- package/dist/types/src/app/components/status-list-item.d.ts +1 -1
- package/dist/types/src/app/components/status-tile.d.ts +4 -3
- package/dist/types/src/app/components/table.d.ts +6 -1
- package/dist/types/src/app/components/tabs.d.ts +1 -1
- package/dist/types/src/app/components/tag.d.ts +1 -1
- package/dist/types/src/app/components/text.d.ts +53 -6
- package/dist/types/src/app/components/textarea.d.ts +1 -1
- package/dist/types/src/app/components/timestamp.d.ts +1 -0
- package/dist/types/src/app/components/toast.d.ts +1 -1
- package/dist/types/src/app/components/toggle.d.ts +1 -1
- package/dist/types/src/app/data/manifest-types.d.ts +4 -0
- package/dist/types/src/app/design-system/generated-token-refs.d.ts +37 -28
- package/dist/types/src/app/design-system/generated-token-values.d.ts +3 -0
- package/dist/types/src/app/design-system/tokens.d.ts +117 -15
- package/dist/types/src/index.d.ts +122 -72
- package/dist/variables.css +41 -33
- package/docs/CONSUMING.md +63 -39
- package/hirobius.tokens.json +163 -112
- package/llms.txt +158 -50
- package/mcp/catalog.mjs +369 -0
- package/mcp/core-set.mjs +66 -0
- package/mcp/guide.mjs +272 -0
- package/mcp/hds-mcp.mjs +49 -0
- package/mcp/server.mjs +94 -0
- package/package.json +53 -11
- package/public/hds-manifest.json +430 -336
- package/public/llms/components.txt +4 -3
- package/public/llms/layout.txt +1 -1
- package/public/llms-full.txt +190 -70
- package/public/llms.txt +158 -50
- 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 +154 -93
- package/upgrade/index.json +208 -0
- package/upgrade/releases/0.17.0.json +962 -0
- package/upgrade/releases/0.18.0.json +8 -0
- package/upgrade/releases/0.19.0.json +233 -0
- package/upgrade/releases/0.19.1.json +8 -0
- package/upgrade/releases/0.20.0.json +5108 -0
- package/upgrade/releases/0.21.0.json +538 -0
- package/upgrade/releases/0.22.0.json +94 -0
- package/upgrade/schema.json +989 -0
- package/dist/chunks/empty-state-BBqhCx0S.js +0 -1217
- package/dist/chunks/form-COP3vK9_.js +0 -66
- package/dist/types/src/app/components/status-dot.d.ts +0 -39
|
@@ -139,7 +139,7 @@ Card — surface container with slot anatomy (12d-card-anatomy). Slot anatomy (D
|
|
|
139
139
|
- metadata: ReactNode
|
|
140
140
|
|
|
141
141
|
### CardMetric
|
|
142
|
-
Card.Metric — one
|
|
142
|
+
Card.Metric — one label, a large value and an optional sub-line inside a Card.
|
|
143
143
|
- label: ReactNode
|
|
144
144
|
- sub: ReactNode
|
|
145
145
|
- tone: 'neutral' | 'danger' | 'success' | 'warning' | 'info' = neutral
|
|
@@ -339,6 +339,7 @@ Presentational label + control slot + helper/error markup. Renders `children` ve
|
|
|
339
339
|
|
|
340
340
|
### Grid
|
|
341
341
|
Grid — responsive grid composition primitive. Enforces semantic gap and column values. No arbitrary CSS grid. - layout='fixed': responsive base (collapses via …
|
|
342
|
+
- align: "center" | "end" | "start" | "stretch" = start
|
|
342
343
|
- as: ElementType<any, keyof IntrinsicElements> = div
|
|
343
344
|
- children: ReactNode
|
|
344
345
|
- className: string
|
|
@@ -571,7 +572,7 @@ Stack — one-dimensional layout primitive.
|
|
|
571
572
|
- wrap: FlexWrap
|
|
572
573
|
|
|
573
574
|
### Stat
|
|
574
|
-
Headline metric — large value,
|
|
575
|
+
Headline metric — large value, caption label, optional sub-line.
|
|
575
576
|
- label: string
|
|
576
577
|
- sub: ReactNode
|
|
577
578
|
- tone: "success" | "danger" | "warning" | "info" | "neutral" | null
|
|
@@ -619,7 +620,7 @@ Table - structured data table primitive for documentation and compact UI matrice
|
|
|
619
620
|
- columns: TableColumn[]
|
|
620
621
|
- density: 'compact' | 'comfortable'
|
|
621
622
|
- description: ReactNode
|
|
622
|
-
- flush: boolean =
|
|
623
|
+
- flush: boolean = true
|
|
623
624
|
- labelledBy: string
|
|
624
625
|
- minWidth: string | number
|
|
625
626
|
- rows: TableRow[]
|
package/public/llms/layout.txt
CHANGED
|
@@ -10,7 +10,7 @@ Canonical skeleton, outermost to innermost:
|
|
|
10
10
|
2. `Stack` (vertical rhythm between sections) or `Grid` (two-dimensional/column layout) for the structural skeleton. One section = one Section/Stack — never add a second wrapper to fake a section boundary.
|
|
11
11
|
3. Reach for a named layout primitive before hand-rolling flex/grid math for a common intent: `Stack direction="row" wrap="wrap"` (wrapping row of same-ish things), `Sidebar` (fixed-width rail + fluid content, no media query), `Switcher` (row that flips to a column below a threshold, no media query). A full-height shell with a centered main region, an aspect-ratio-locked media box or a negative margin that escapes a parent padding is a `Box` with `style` (not `sx`, which applies on the client only).
|
|
12
12
|
4. `Surface` for any background-bearing, padded wrapper (card, panel, inset). Never a raw element with backgroundColor + padding hand-rolled inline.
|
|
13
|
-
5. Use the screen patterns (`@hirobius/design-system/patterns`) for the parts every screen repeats: `PageHeader` once at the top (breadcrumb, `heading2` title, status, actions), `MetricTiles` for any row of headline numbers, `FormActions` for a form footer (primary right-most and last in DOM order, destructive on the far left), `DataTableSection` for a titled table (toolbar slot, consumer row actions, empty state, scrolls on narrow widths without a caller `minWidth`), and `DestructiveSection` last for an irreversible action (danger button, then an `AlertDialog` confirm).
|
|
13
|
+
5. Use the screen patterns (`@hirobius/design-system/patterns`) for the parts every screen repeats: `PageHeader` once at the top (breadcrumb, `heading2` title, status, actions), `MetricTiles` for any row of headline numbers, `FormActions` for a form footer (primary right-most and last in DOM order, destructive on the far left), `DataTableSection` for a titled table (toolbar slot, consumer row actions, empty state, scrolls on narrow widths without a caller `minWidth`), and `DestructiveSection` last for an irreversible action (danger button, then an `AlertDialog` confirm). A row of headline numbers is always `MetricTiles`; `Stat`, `Card.Metric` and `StatusTile` answer other needs (see "Pick By Need" in `llms.txt`). Pick between `Card` and `Surface` with the "Which one, when" table in `DESIGN.md`.
|
|
14
14
|
6. `Box` `sx` LAST — only for genuinely one-off layout that no named primitive covers. `sx` spacing/color keys MUST be HDS token keys, never raw hex/px.
|
|
15
15
|
|
|
16
16
|
Negative rules (apply everywhere, checked by `scripts/audit-tokens.mjs --full`, `check-hardcoded-spacing.mjs`, `check-hardcoded-colors.mjs`):
|
package/public/llms-full.txt
CHANGED
|
@@ -1,8 +1,67 @@
|
|
|
1
1
|
# Hirobius Design System
|
|
2
2
|
|
|
3
|
-
Generated: 2026-10-
|
|
3
|
+
Generated: 2026-10-07T16:09:09.631Z
|
|
4
4
|
Primary sources: `public/hds-manifest.json`, `src/app/data/component-api.json`, `hirobius.tokens.json`
|
|
5
5
|
|
|
6
|
+
## Agents Using The Package: Start Here
|
|
7
|
+
|
|
8
|
+
- Read `AGENTS.md` (package root, `node_modules/@hirobius/design-system/AGENTS.md`) first: which component to use for each screen need, the imports, and the rules.
|
|
9
|
+
- MCP server `hds` ships in the package: run `npx hds-mcp` (stdio). Tools: `list_core`, `search_components`, `get_component`, `search_tokens`; each answer is under 2 KB.
|
|
10
|
+
- Lint before you finish: `import hds from '@hirobius/design-system/eslint-plugin'` and spread `hds.configs.recommended` into `eslint.config.mjs`.
|
|
11
|
+
|
|
12
|
+
## Pick By Need
|
|
13
|
+
|
|
14
|
+
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. Generated from `mcp/guide.mjs`, the same list as `AGENTS.md`.
|
|
15
|
+
|
|
16
|
+
- **Outermost wrapper of a screen** → `Page`, `Stack`. Not: `Container`, `Box`, a bare Stack as the root.
|
|
17
|
+
`<Page><Stack gap="spacious">…one child per section…</Stack></Page>`. `Container` is for a full-bleed surface that is not a page.
|
|
18
|
+
- **Screen title with breadcrumb, status and actions** → `PageHeader`, `Breadcrumb`, `Badge`. Not: Text as the page title, a Stack row of Text and Badge.
|
|
19
|
+
Exactly one per screen: `<PageHeader breadcrumb={<Breadcrumb items={[{ label, href }, { label }]} />} title="…" status={<Badge tone="success">Active</Badge>} />`.
|
|
20
|
+
- **Row of headline numbers (stats, KPIs, counts, totals)** → `MetricTiles`, `MetricTile`. Not: `Stat`, `StatusTile`, `CardMetric`, Card with Text.
|
|
21
|
+
`<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).
|
|
22
|
+
- **Table of records with row actions** → `DataTableSection`, `Badge`, `Button`. Not: Table on its own, Card around a Table.
|
|
23
|
+
`<DataTableSection title="Projects" columns={[{ key, label }]} rows={[{ key, cells: [...], actions: <Stack direction="row" gap="tight">…</Stack> }]} />`; leave the actions column out of `columns`.
|
|
24
|
+
- **Switch between panels of related content** → `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`. Not: `SegmentedControl`, Button rows.
|
|
25
|
+
`<Tabs defaultValue="a"><TabsList><TabsTrigger value="a">A</TabsTrigger></TabsList><TabsContent value="a">…</TabsContent></Tabs>`.
|
|
26
|
+
- **Status label in a header or table cell** → `Badge`. Not: `Tag`, `Text`.
|
|
27
|
+
`<Badge tone="success|info|warning|danger|neutral|inProgress">…</Badge>`; tone is the only styling input.
|
|
28
|
+
- **Form with fields and a submit button** → `Form`, `Textarea`, `Input`, `FormActions`, `Button`. Not: a raw `<form>`, a Stack of Buttons as the footer.
|
|
29
|
+
`<Form onSubmit={…}><Textarea label="Notes" … /><FormActions primary={<Button type="submit">Save note</Button>} /></Form>`; every field takes its own `label`.
|
|
30
|
+
- **Confirm a destructive or irreversible action (delete, archive, remove)** → `AlertDialog`, `Button`. Not: `Dialog`, `window.confirm`.
|
|
31
|
+
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.
|
|
32
|
+
- **Brief confirmation after an action (saved, archived, sent)** → `ToastProvider`, `useToast`. Not: `Badge`, `Alert`, `Text`.
|
|
33
|
+
Wrap the app root once in `<ToastProvider>`, then `toast({ title: "Note saved", tone: "success" })` from `useToast()`.
|
|
34
|
+
- **Message that stays next to its content (error, warning, info)** → `Alert`. Not: Text in a coloured Surface.
|
|
35
|
+
`<Alert tone="warning">…</Alert>`.
|
|
36
|
+
- **Button or row action** → `Button`. Not: a raw `<button>`, InlineLink for an action.
|
|
37
|
+
Row actions: `<Button variant="tertiary" size="sm" aria-label="View Brand refresh">View</Button>`; the destructive one adds `tone="danger"`.
|
|
38
|
+
- **Space between sections or items** → `Stack`, `Grid`. Not: margins on children, raw px gaps such as px8.
|
|
39
|
+
Stack `gap`: `spacious` between page sections, `normal` inside a section, `tight` between buttons. `Grid` for two-dimensional layout.
|
|
40
|
+
- **Padded background or a titled content object** → `Surface`, `Card`. Not: a Box with background and padding.
|
|
41
|
+
`Surface` for a plain padded background; `Card` only when the content has a title or can be pressed.
|
|
42
|
+
- **Progress bar or ring** → `Progress`. Not: a hand-built bar.
|
|
43
|
+
`<Progress value={60} label="Upload" />`; inside an existing `Card`, `CardProgress`.
|
|
44
|
+
- **Body copy** → `Text`. Not: Text as a section heading.
|
|
45
|
+
Section headings come from the pattern that owns the section (`PageHeader` `title`, `DataTableSection` `title`), not from extra Text.
|
|
46
|
+
- **Empty state for a list or section** → `EmptyState`. Not: Text saying "No items".
|
|
47
|
+
`DataTableSection` shows its own through `emptyTitle`; use `EmptyState` elsewhere.
|
|
48
|
+
- **The one irreversible action of a screen (delete the client, close the account)** → `DestructiveSection`. Not: a danger Button that acts without a confirm.
|
|
49
|
+
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.
|
|
50
|
+
- **Read-only details as label and value pairs** → `MetadataList`, `Field`. Not: a Table with two columns, Text pairs in a Stack.
|
|
51
|
+
`<MetadataList items={[{ term: "Owner", description: "Ada" }]} />` for a set; `Field` for one pair.
|
|
52
|
+
- **A person, or a group of people** → `Avatar`, `AvatarGroup`. Not: an image in a rounded Box.
|
|
53
|
+
`<Avatar src alt initials />`; several in a row: `<AvatarGroup max={3}>…Avatars…</AvatarGroup>`.
|
|
54
|
+
- **A date or time** → `Timestamp`. Not: a formatted string in Text.
|
|
55
|
+
`<Timestamp date="2026-10-14" />` renders a `<time dateTime>`; a date in a table cell may stay plain text.
|
|
56
|
+
- **A keyboard key or shortcut hint** → `Kbd`. Not: `InlineCode`, Text in a Box.
|
|
57
|
+
`<Kbd>⌘K</Kbd>`.
|
|
58
|
+
- **A quotation with attribution** → `Blockquote`. Not: Callout for a quote, italic Text.
|
|
59
|
+
`<Blockquote attribution="Ada Lovelace">…</Blockquote>`.
|
|
60
|
+
- **A fixed-width side rail beside fluid content, or a row that stacks when narrow** → `Sidebar`, `Switcher`. Not: a Grid with media queries.
|
|
61
|
+
`Sidebar` (`side`, `sideWidth`) for rail + content; `Switcher` (`threshold`) for a row that flips to a column. Neither needs a media query.
|
|
62
|
+
- **Keep an element stuck while its region scrolls** → `Pin`. Not: position: sticky in style.
|
|
63
|
+
`<Pin top="…">…</Pin>` inside the scrolling region.
|
|
64
|
+
|
|
6
65
|
## System Architecture
|
|
7
66
|
|
|
8
67
|
- Engine: React + TypeScript
|
|
@@ -36,56 +95,105 @@ This file intentionally does not embed prop tables or long-form API docs.
|
|
|
36
95
|
- Detailed prop types, unions, defaults, and descriptions: read `src/app/data/component-api.json`.
|
|
37
96
|
- Runtime behavior: read the actual component source under `src/app/components/` (and associated styles).
|
|
38
97
|
|
|
98
|
+
## Core set
|
|
99
|
+
|
|
100
|
+
The 43 components of the ratified core set (hds#254, ADR-031): brand-neutral, composable, imported from the package root. Build a screen from these first. Each has `core: true` in `public/hds-manifest.json` and is marked `[core]` under "Which one when". Composed pattern modules (page shells, forms, code blocks, page sections) are imported from `@hirobius/design-system/patterns`.
|
|
101
|
+
|
|
102
|
+
- Button (Actions)
|
|
103
|
+
- Avatar (Display)
|
|
104
|
+
- Card (Display)
|
|
105
|
+
- EmptyState (Display)
|
|
106
|
+
- Field (Display)
|
|
107
|
+
- Icon (Display)
|
|
108
|
+
- Kbd (Display)
|
|
109
|
+
- Table (Display)
|
|
110
|
+
- Alert (Feedback)
|
|
111
|
+
- Badge (Feedback)
|
|
112
|
+
- Progress (Feedback)
|
|
113
|
+
- Skeleton (Feedback)
|
|
114
|
+
- Spinner (Feedback)
|
|
115
|
+
- ToastProvider (Feedback)
|
|
116
|
+
- Checkbox (Inputs)
|
|
117
|
+
- Combobox (Inputs)
|
|
118
|
+
- Input (Inputs)
|
|
119
|
+
- Radio (Inputs)
|
|
120
|
+
- SegmentedControl (Inputs)
|
|
121
|
+
- Select (Inputs)
|
|
122
|
+
- Slider (Inputs)
|
|
123
|
+
- Tag (Inputs)
|
|
124
|
+
- Textarea (Inputs)
|
|
125
|
+
- Toggle (Inputs)
|
|
126
|
+
- Box (Layout)
|
|
127
|
+
- Container (Layout)
|
|
128
|
+
- Disclosure (Layout)
|
|
129
|
+
- Divider (Layout)
|
|
130
|
+
- Grid (Layout)
|
|
131
|
+
- Stack (Layout)
|
|
132
|
+
- Surface (Layout)
|
|
133
|
+
- Breadcrumb (Navigation)
|
|
134
|
+
- InlineLink (Navigation)
|
|
135
|
+
- Pagination (Navigation)
|
|
136
|
+
- Tabs (Navigation)
|
|
137
|
+
- Dialog (Overlays)
|
|
138
|
+
- Menu (Overlays)
|
|
139
|
+
- Popover (Overlays)
|
|
140
|
+
- Tooltip (Overlays)
|
|
141
|
+
- HdsRouterProvider (Theming)
|
|
142
|
+
- HdsThemeProvider (Theming)
|
|
143
|
+
- Text (Typography)
|
|
144
|
+
- VisuallyHidden (Utility)
|
|
145
|
+
|
|
39
146
|
## Which one when
|
|
40
147
|
|
|
41
|
-
One line per component that declares when to use it (`@usage` / `@useInstead` in its JSDoc). Full detail, including `usage.whenNot`, `keyboard` and `aiRules`, is in `public/hds-manifest.json` and `src/app/data/component-api.json`.
|
|
42
|
-
|
|
43
|
-
Alert: Show a persistent inline message with a severity, next to the content it concerns. Use instead: ToastProvider, Badge
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
148
|
+
One line per component that declares when to use it (`@usage` / `@useInstead` in its JSDoc); `[core]` marks the core set. Full detail, including `usage.whenNot`, `keyboard` and `aiRules`, is in `public/hds-manifest.json` and `src/app/data/component-api.json`.
|
|
149
|
+
|
|
150
|
+
Alert: [core] Show a persistent inline message with a severity, next to the content it concerns. Use instead: ToastProvider, Badge
|
|
151
|
+
AlertDialog: Confirm a destructive or irreversible action (delete, archive, remove) before it runs, with Cancel and Action buttons. Use instead: Dialog, ToastProvider
|
|
152
|
+
Avatar: [core] Show a person or entity as an image with an initials fallback. Use instead: Icon, Badge
|
|
153
|
+
Badge: [core] Label a status, state or count with a compact neutral or semantic chip, or a dot. Use instead: Tag, Alert
|
|
154
|
+
Box: [core] Build one-off layout that no named layout primitive covers, using token keys through `sx`. Use instead: Stack, Grid, Container
|
|
155
|
+
Breadcrumb: [core] Show where the current page sits in a hierarchy and link back up it. Use instead: Tabs, Pagination
|
|
156
|
+
Button: [core] Trigger an action (submit, save, open a dialog) with a text label and optional icons; for an icon-only control pass iconOnly with iconLeft and label; for an on/off toggle pass pressed. Use instead: InlineLink
|
|
157
|
+
Card: [core] Group related content on a raised surface with header, body, footer and metric slots, or as one selectable option. Use instead: Surface, Card.Metric
|
|
158
|
+
CardMetric: Show one headline figure (a KPI, a count, a total) with its label inside a Card. Use instead: MetricTiles
|
|
159
|
+
Checkbox: [core] Let people tick one or more independent options, or a parent choice that is partly selected. Use instead: Toggle, Radio
|
|
160
|
+
Combobox: [core] Pick one value, or several with `multiple`, from a long list of options, with a search field that filters as you type. Use instead: Select, Menu
|
|
161
|
+
Container: [core] Constrain a page region to the content or maximum width and centre it. Use instead: Stack, Grid, Box
|
|
162
|
+
Dialog: [core] Interrupt the page with a modal that needs a decision or focused input before people continue. Use instead: Tooltip, Alert, ToastProvider, AlertDialog
|
|
163
|
+
Disclosure: [core] Hide optional explanatory content behind a heading that people can expand. Use instead: Tabs
|
|
164
|
+
Divider: [core] Separate two regions of content with a thin semantic rule. Use instead: Stack, Surface
|
|
165
|
+
EmptyState: [core] Say that a list, grid or section has no data yet, with a title and an optional description. Use instead: Skeleton, Alert
|
|
166
|
+
Field: [core] Show a labelled read-only value, such as a name and its value in a detail view. Use instead: Input, Card.Metric
|
|
167
|
+
Grid: [core] Lay out content in responsive columns with token-governed gaps, or align nested content with subgrid. Use instead: Stack, Box
|
|
168
|
+
HdsRouterProvider: [core] Wrap the app root once so HDS links and navigation use your client router; with no provider they stay plain anchors.
|
|
169
|
+
HdsThemeProvider: [core] Wrap an app or region once to set theme, density and other root attributes for everything inside it. Use instead: Surface
|
|
170
|
+
Icon: [core] Draw a Lucide glyph at a size and colour from the design tokens. Use instead: Button, Avatar
|
|
171
|
+
InlineLink: [core] Link to a route or external URL from inside body copy. Use instead: Button, Text
|
|
172
|
+
Input: [core] Collect a single line of text, a number, an email, a password, a date or a time, with label, helper and error slots. Use instead: Textarea, Select, Combobox
|
|
173
|
+
Kbd: [core] Show a keyboard key or shortcut hint inline, as a native kbd element. Use instead: Text
|
|
174
|
+
Menu: [core] Offer a list of actions from a trigger, closed until opened. Use instead: Select
|
|
175
|
+
Pagination: [core] Move between pages of a long result set, with truncated page numbers. Use instead: Breadcrumb
|
|
176
|
+
Popover: [core] Anchor interactive content, such as a short form, a filter set or a picker, to a trigger people click to open. Use instead: Tooltip, Menu, Dialog
|
|
177
|
+
Progress: [core] Show how far a task has got, as a bar or ring that is determinate or indeterminate. Use instead: Spinner, Skeleton
|
|
178
|
+
Radio: [core] Choose exactly one option from a small set that should stay visible together. Use instead: Checkbox, Select, SegmentedControl
|
|
179
|
+
SegmentedControl: [core] Switch between two to five mutually exclusive views or modes in a compact row. Use instead: Radio, Tabs, Select
|
|
180
|
+
Select: [core] Choose one value from a short list of options in a form field. Use instead: Menu, Combobox
|
|
181
|
+
Skeleton: [core] Reserve the shape of content that is still loading, so the layout does not jump. Use instead: Progress, Spinner
|
|
182
|
+
Slider: [core] Pick a number within a known range by dragging, when the exact value matters less than the position. Use instead: Input
|
|
183
|
+
Spinner: [core] Show that something is loading when the wait is short or has no measurable end. Use instead: Progress, Skeleton
|
|
184
|
+
Stack: [core] Space a row or column of content with token gaps and simple flex alignment. Use instead: Grid, Surface
|
|
185
|
+
Stat: Show one headline figure inline, inside prose or a dense list, with its label and an optional sub-line. Use instead: MetricTiles, Card.Metric, StatusTile
|
|
186
|
+
StatusTile: Show a titled status block with optional notes and a trailing badge or affordance. Use instead: MetricTiles, Card.Metric, StatusListItem
|
|
187
|
+
Surface: [core] Wrap content in a padded, background-bearing inset surface with no header or footer anatomy. Use instead: Card, Stack
|
|
188
|
+
Table: [core] Show rows of structured data in aligned columns, with optional sorting. Use instead: Field, Stack
|
|
189
|
+
Tabs: [core] Switch between panels of related content in the same place, one visible at a time. Use instead: SegmentedControl, Disclosure
|
|
190
|
+
Tag: [core] Show an interactive filter or category chip that people can toggle. Use instead: Badge
|
|
191
|
+
Text: [core] Set any text on the type ramp, with the element chosen by `as`. Use instead: InlineLink, Kbd
|
|
192
|
+
Textarea: [core] Collect multi-line free text such as a message, note or description. Use instead: Input
|
|
193
|
+
ToastProvider: [core] Confirm an action with a brief message that appears and dismisses itself, through useToast. Use instead: Alert, Dialog
|
|
194
|
+
Toggle: [core] Switch one setting on or off, with the effect applying immediately. Use instead: Checkbox, Radio
|
|
195
|
+
Tooltip: [core] Name an icon-only control, or add a short hint that appears on hover and keyboard focus. Use instead: Popover, Text
|
|
196
|
+
VisuallyHidden: [core] Give assistive technology a label or description that sighted people do not need to see. Use instead: Text
|
|
89
197
|
|
|
90
198
|
## Tokens (Primary Source Of Truth)
|
|
91
199
|
|
|
@@ -114,7 +222,7 @@ Canonical skeleton, outermost to innermost:
|
|
|
114
222
|
2. `Stack` (vertical rhythm between sections) or `Grid` (two-dimensional/column layout) for the structural skeleton. One section = one Section/Stack — never add a second wrapper to fake a section boundary.
|
|
115
223
|
3. Reach for a named layout primitive before hand-rolling flex/grid math for a common intent: `Stack direction="row" wrap="wrap"` (wrapping row of same-ish things), `Sidebar` (fixed-width rail + fluid content, no media query), `Switcher` (row that flips to a column below a threshold, no media query). A full-height shell with a centered main region, an aspect-ratio-locked media box or a negative margin that escapes a parent padding is a `Box` with `style` (not `sx`, which applies on the client only).
|
|
116
224
|
4. `Surface` for any background-bearing, padded wrapper (card, panel, inset). Never a raw element with backgroundColor + padding hand-rolled inline.
|
|
117
|
-
5. Use the screen patterns (`@hirobius/design-system/patterns`) for the parts every screen repeats: `PageHeader` once at the top (breadcrumb, `heading2` title, status, actions), `MetricTiles` for any row of headline numbers, `FormActions` for a form footer (primary right-most and last in DOM order, destructive on the far left), `DataTableSection` for a titled table (toolbar slot, consumer row actions, empty state, scrolls on narrow widths without a caller `minWidth`), and `DestructiveSection` last for an irreversible action (danger button, then an `AlertDialog` confirm).
|
|
225
|
+
5. Use the screen patterns (`@hirobius/design-system/patterns`) for the parts every screen repeats: `PageHeader` once at the top (breadcrumb, `heading2` title, status, actions), `MetricTiles` for any row of headline numbers, `FormActions` for a form footer (primary right-most and last in DOM order, destructive on the far left), `DataTableSection` for a titled table (toolbar slot, consumer row actions, empty state, scrolls on narrow widths without a caller `minWidth`), and `DestructiveSection` last for an irreversible action (danger button, then an `AlertDialog` confirm). A row of headline numbers is always `MetricTiles`; `Stat`, `Card.Metric` and `StatusTile` answer other needs (see "Pick By Need" in `llms.txt`). Pick between `Card` and `Surface` with the "Which one, when" table in `DESIGN.md`.
|
|
118
226
|
6. `Box` `sx` LAST — only for genuinely one-off layout that no named primitive covers. `sx` spacing/color keys MUST be HDS token keys, never raw hex/px.
|
|
119
227
|
|
|
120
228
|
Negative rules (apply everywhere, checked by `scripts/audit-tokens.mjs --full`, `check-hardcoded-spacing.mjs`, `check-hardcoded-colors.mjs`):
|
|
@@ -293,28 +401,29 @@ Feedback hues are never decorative; do not use them as accents. Light and dark m
|
|
|
293
401
|
<!-- auto:start:typography -->
|
|
294
402
|
HDS ships three typefaces — each with a distinct and exclusive role:
|
|
295
403
|
|
|
296
|
-
- **Display / Heading font**: Satoshi. Bound exclusively to `display
|
|
404
|
+
- **Display / Heading font**: Satoshi. Bound exclusively to the `display` and `title` styles. Never used for body copy or UI labels.
|
|
297
405
|
- **Body / UI font**: Satoshi. All prose, labels, small text, captions, and UI copy.
|
|
298
406
|
- **Mono font**: Geist Mono. Reserved for tokens, code, technical callouts, and metric readouts.
|
|
299
407
|
|
|
300
|
-
Weights declared: `400` regular, `500` medium, `700` bold. Heading styles (display ·
|
|
408
|
+
Weights declared: `400` regular, `500` medium, `700` bold. Heading styles (display · title) use `700` bold; body, UI, and caption use `400` regular / `500` medium.
|
|
301
409
|
|
|
302
410
|
### Type ramp
|
|
303
411
|
|
|
304
412
|
| Role | Size (desktop max) | Weight | Use |
|
|
305
413
|
| --- | --- | --- | --- |
|
|
306
|
-
| `semantic.typography.display` |
|
|
307
|
-
| `semantic.typography.
|
|
308
|
-
| `semantic.typography.
|
|
309
|
-
| `semantic.typography.
|
|
310
|
-
| `semantic.typography.
|
|
311
|
-
| `semantic.typography.
|
|
312
|
-
| `semantic.typography.
|
|
313
|
-
| `semantic.typography.
|
|
314
|
-
| `semantic.typography.
|
|
414
|
+
| `semantic.typography.display` | 48px | 700 | One per page, hero only |
|
|
415
|
+
| `semantic.typography.title` | 24px | 700 | Section and card headings |
|
|
416
|
+
| `semantic.typography.body` | 16px | 400 | Prose |
|
|
417
|
+
| `semantic.typography.ui` | 14px | 500 | Controls, nav, table text, values |
|
|
418
|
+
| `semantic.typography.caption` | 12px | 500 | Metadata, help text and labels, in sentence case |
|
|
419
|
+
| `semantic.typography.mono` | 13px | 400 | Code and ids |
|
|
420
|
+
| `semantic.typography.h1` | 24px | 700 | deprecated: use semantic.typography.title |
|
|
421
|
+
| `semantic.typography.h2` | 24px | 700 | deprecated: use semantic.typography.title |
|
|
422
|
+
| `semantic.typography.h3` | 24px | 700 | deprecated: use semantic.typography.title |
|
|
423
|
+
| `semantic.typography.eyebrow` | 12px | 500 | deprecated: use semantic.typography.caption |
|
|
315
424
|
| `semantic.typography.lineHeight.none` | | | Semantic alias for leading-none (1) |
|
|
316
425
|
|
|
317
|
-
>
|
|
426
|
+
> 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.
|
|
318
427
|
<!-- auto:end:typography -->
|
|
319
428
|
|
|
320
429
|
## Spacing
|
|
@@ -324,9 +433,19 @@ Weights declared: `400` regular, `500` medium, `700` bold. Heading styles (displ
|
|
|
324
433
|
|
|
325
434
|
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`)
|
|
326
435
|
|
|
327
|
-
|
|
436
|
+
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.
|
|
328
437
|
<!-- auto:end:spacing -->
|
|
329
438
|
|
|
439
|
+
## Layout contract (v1)
|
|
440
|
+
|
|
441
|
+
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`).
|
|
442
|
+
|
|
443
|
+
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.
|
|
444
|
+
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"`.
|
|
445
|
+
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`.
|
|
446
|
+
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.
|
|
447
|
+
5. **Forms.** A form control in a form context has a max width (`40rem` by default) and siblings share one width.
|
|
448
|
+
|
|
330
449
|
## Corner-Radius Policy
|
|
331
450
|
|
|
332
451
|
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.
|
|
@@ -417,7 +536,7 @@ Every HDS card surface must conform to this anatomy exactly. No creative interpr
|
|
|
417
536
|
| 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 |
|
|
418
537
|
| Padding | `var(--semantic-space-component-padding)` or `<HdsSurface padding="component">` | Raw pixel values or ad hoc insets |
|
|
419
538
|
| 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 |
|
|
420
|
-
| Title | `hds.typeStyles.
|
|
539
|
+
| Title | `hds.typeStyles.title` / `<Text variant="title">` | Any other type style for the primary card heading |
|
|
421
540
|
| Subtitle / meta | `hds.typeStyles.caption` + `var(--semantic-color-content-secondary)` | Primary content color or body size for secondary text |
|
|
422
541
|
| Hover (interactive only) | `transform: scale(1.02)` | Background fill change, border color shift, or opacity fade on hover |
|
|
423
542
|
|
|
@@ -429,7 +548,7 @@ Five patterns sit above the primitives so that every screen is composed the same
|
|
|
429
548
|
|
|
430
549
|
### Page title rule
|
|
431
550
|
|
|
432
|
-
Every screen has exactly one `PageHeader`. The page title is `
|
|
551
|
+
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`.
|
|
433
552
|
|
|
434
553
|
### Which one, when
|
|
435
554
|
|
|
@@ -450,7 +569,7 @@ Three more needs are props on an existing component, not components of their own
|
|
|
450
569
|
|
|
451
570
|
`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.
|
|
452
571
|
|
|
453
|
-
`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 `
|
|
572
|
+
`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`.
|
|
454
573
|
|
|
455
574
|
### Destructive and table sections
|
|
456
575
|
|
|
@@ -482,7 +601,7 @@ Unresolved rules that the live repo cannot yet answer confidently are tracked in
|
|
|
482
601
|
<!-- auto:start:build-meta -->
|
|
483
602
|
---
|
|
484
603
|
|
|
485
|
-
> Generated from `hirobius.tokens.json` (
|
|
604
|
+
> Generated from `hirobius.tokens.json` (389 tokens) and `public/hds-manifest.json` by `scripts/build-design-md.mjs`.
|
|
486
605
|
> Hand-edit `DESIGN.source.md`; this file (`DESIGN.md`) is overwritten by `pnpm tokens`.
|
|
487
606
|
<!-- auto:end:build-meta -->
|
|
488
607
|
|
|
@@ -611,7 +730,7 @@ Card — surface container with slot anatomy (12d-card-anatomy). Slot anatomy (D
|
|
|
611
730
|
- metadata: ReactNode
|
|
612
731
|
|
|
613
732
|
### CardMetric
|
|
614
|
-
Card.Metric — one
|
|
733
|
+
Card.Metric — one label, a large value and an optional sub-line inside a Card.
|
|
615
734
|
- label: ReactNode
|
|
616
735
|
- sub: ReactNode
|
|
617
736
|
- tone: 'neutral' | 'danger' | 'success' | 'warning' | 'info' = neutral
|
|
@@ -811,6 +930,7 @@ Presentational label + control slot + helper/error markup. Renders `children` ve
|
|
|
811
930
|
|
|
812
931
|
### Grid
|
|
813
932
|
Grid — responsive grid composition primitive. Enforces semantic gap and column values. No arbitrary CSS grid. - layout='fixed': responsive base (collapses via …
|
|
933
|
+
- align: "center" | "end" | "start" | "stretch" = start
|
|
814
934
|
- as: ElementType<any, keyof IntrinsicElements> = div
|
|
815
935
|
- children: ReactNode
|
|
816
936
|
- className: string
|
|
@@ -1043,7 +1163,7 @@ Stack — one-dimensional layout primitive.
|
|
|
1043
1163
|
- wrap: FlexWrap
|
|
1044
1164
|
|
|
1045
1165
|
### Stat
|
|
1046
|
-
Headline metric — large value,
|
|
1166
|
+
Headline metric — large value, caption label, optional sub-line.
|
|
1047
1167
|
- label: string
|
|
1048
1168
|
- sub: ReactNode
|
|
1049
1169
|
- tone: "success" | "danger" | "warning" | "info" | "neutral" | null
|
|
@@ -1091,7 +1211,7 @@ Table - structured data table primitive for documentation and compact UI matrice
|
|
|
1091
1211
|
- columns: TableColumn[]
|
|
1092
1212
|
- density: 'compact' | 'comfortable'
|
|
1093
1213
|
- description: ReactNode
|
|
1094
|
-
- flush: boolean =
|
|
1214
|
+
- flush: boolean = true
|
|
1095
1215
|
- labelledBy: string
|
|
1096
1216
|
- minWidth: string | number
|
|
1097
1217
|
- rows: TableRow[]
|