@hirobius/design-system 0.17.0 → 0.19.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/CONSUMING.md +150 -0
- package/DESIGN.md +220 -0
- package/README.md +30 -2
- package/codemods/patterns-subpath.mjs +254 -0
- package/codemods/patterns-subpath.names.json +26 -0
- package/dist/chunks/{activity-feed-C8_e-Btq.js → activity-feed-DIMeUJpW.js} +4907 -2912
- package/dist/chunks/{form-DnGRy27o.js → form-BvNNnJ0t.js} +2 -2
- package/dist/form.js +5 -5
- package/dist/hirobius-ui.js +1986 -2529
- package/dist/icons.js +53 -0
- package/dist/manifest.js +15 -14
- package/dist/patterns.js +292 -101
- package/dist/static.css +3 -3
- package/dist/styles.css +1 -1
- package/dist/tokens.css +1 -1
- package/dist/types/src/app/components/alert-dialog.d.ts +9 -1
- package/dist/types/src/app/components/alert.d.ts +4 -0
- package/dist/types/src/app/components/avatar.d.ts +4 -0
- package/dist/types/src/app/components/badge.d.ts +4 -0
- package/dist/types/src/app/components/box.d.ts +5 -0
- package/dist/types/src/app/components/breadcrumb.d.ts +4 -0
- package/dist/types/src/app/components/button-group.d.ts +4 -0
- package/dist/types/src/app/components/button.d.ts +3 -0
- package/dist/types/src/app/components/card.d.ts +9 -0
- package/dist/types/src/app/components/checkbox.d.ts +4 -0
- package/dist/types/src/app/components/combobox.d.ts +10 -0
- package/dist/types/src/app/components/command-palette.d.ts +3 -1
- package/dist/types/src/app/components/container.d.ts +5 -0
- package/dist/types/src/app/components/context-menu.d.ts +25 -2
- package/dist/types/src/app/components/data-table-section.d.ts +45 -0
- package/dist/types/src/app/components/destructive-section.d.ts +32 -0
- package/dist/types/src/app/components/dialog.d.ts +20 -2
- package/dist/types/src/app/components/disclosure.d.ts +3 -0
- package/dist/types/src/app/components/divider.d.ts +4 -0
- package/dist/types/src/app/components/empty-state.d.ts +4 -0
- package/dist/types/src/app/components/field.d.ts +4 -0
- package/dist/types/src/app/components/form-actions.d.ts +26 -0
- package/dist/types/src/app/components/grid.d.ts +4 -0
- package/dist/types/src/app/components/hds-tooltip.d.ts +14 -2
- package/dist/types/src/app/components/hover-card.d.ts +16 -1
- package/dist/types/src/app/components/icon.d.ts +4 -0
- package/dist/types/src/app/components/inline-link.d.ts +4 -0
- package/dist/types/src/app/components/input.d.ts +5 -0
- package/dist/types/src/app/components/kbd.d.ts +3 -0
- package/dist/types/src/app/components/menu.d.ts +26 -2
- package/dist/types/src/app/components/metric-tiles.d.ts +40 -0
- package/dist/types/src/app/components/page-header.d.ts +34 -0
- package/dist/types/src/app/components/pagination.d.ts +3 -0
- package/dist/types/src/app/components/popover.d.ts +14 -2
- package/dist/types/src/app/components/progress.d.ts +4 -0
- package/dist/types/src/app/components/radio.d.ts +5 -0
- package/dist/types/src/app/components/select.d.ts +16 -0
- package/dist/types/src/app/components/skeleton.d.ts +4 -0
- package/dist/types/src/app/components/slider.d.ts +3 -0
- package/dist/types/src/app/components/spinner.d.ts +4 -0
- package/dist/types/src/app/components/stack.d.ts +4 -0
- package/dist/types/src/app/components/stat.d.ts +4 -0
- package/dist/types/src/app/components/status-tile.d.ts +4 -0
- package/dist/types/src/app/components/table.d.ts +36 -4
- package/dist/types/src/app/components/tabs.d.ts +4 -0
- package/dist/types/src/app/components/tag.d.ts +3 -0
- package/dist/types/src/app/components/text.d.ts +4 -0
- package/dist/types/src/app/components/textarea.d.ts +3 -0
- package/dist/types/src/app/components/toast.d.ts +4 -0
- package/dist/types/src/app/components/toggle.d.ts +4 -0
- package/dist/types/src/app/components/tooltip.d.ts +1 -1
- package/dist/types/src/app/components/visually-hidden.d.ts +3 -0
- package/dist/types/src/app/context/hds-portal.d.ts +52 -0
- package/dist/types/src/app/context/hds-theme.d.ts +10 -0
- package/dist/types/src/app/data/manifest-types.d.ts +27 -0
- package/dist/types/src/app/design-system/generated-token-refs.d.ts +2 -0
- package/dist/types/src/icons.d.ts +13 -0
- package/dist/types/src/index.d.ts +20 -1
- package/dist/types/src/patterns.d.ts +5 -0
- package/dist/variables.css +4 -0
- package/docs/CONSUMING.md +758 -0
- package/hirobius.tokens.json +17 -0
- package/llms.txt +261 -0
- package/package.json +30 -6
- package/public/hds-manifest.json +1545 -89
- package/public/llms/components.txt +1090 -0
- package/public/llms/layout.txt +54 -0
- package/public/llms/scroll.txt +22 -0
- package/public/llms/tokens.txt +32 -0
- package/public/llms-full.txt +1561 -0
- package/public/llms.txt +261 -0
- package/src/app/data/component-api.json +6156 -0
package/CONSUMING.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Consuming `@hirobius/design-system`
|
|
2
|
+
|
|
3
|
+
The design system ships as an ESM package published to the **public npm
|
|
4
|
+
registry**. This guide is for apps that want to use the components, tokens, and
|
|
5
|
+
helpers.
|
|
6
|
+
|
|
7
|
+
> For the full consumption guide (styles, `data-hds` scoping, routing seam, MUI
|
|
8
|
+
> interop, forms, troubleshooting) see [`docs/CONSUMING.md`](docs/CONSUMING.md).
|
|
9
|
+
|
|
10
|
+
## 1. Install
|
|
11
|
+
|
|
12
|
+
No registry config, `.npmrc`, or auth token is required — it's public npm:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @hirobius/design-system
|
|
16
|
+
# peer dependencies (the consuming app provides these singletons):
|
|
17
|
+
pnpm add react react-dom
|
|
18
|
+
# react-router is an OPTIONAL peer — only if you drive links through your router
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The package also pulls in its own runtime deps (Radix, lucide-react, motion,
|
|
22
|
+
clsx, class-variance-authority, tailwind-merge) automatically. Import icons
|
|
23
|
+
from `@hirobius/design-system/icons` (a curated Lucide set, nothing extra to
|
|
24
|
+
install); for an icon outside it, install `lucide-react@0.487.0` (the same
|
|
25
|
+
version keeps the `LucideIcon` type identical).
|
|
26
|
+
|
|
27
|
+
## 2. Use
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { Button, Card, Dialog } from '@hirobius/design-system';
|
|
31
|
+
import '@hirobius/design-system/tokens.css'; // required — design tokens as CSS vars
|
|
32
|
+
|
|
33
|
+
export function Example() {
|
|
34
|
+
return (
|
|
35
|
+
<Card>
|
|
36
|
+
<Button variant="primary">Hello</Button>
|
|
37
|
+
</Card>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Available subpaths:
|
|
43
|
+
|
|
44
|
+
| Import | What |
|
|
45
|
+
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
46
|
+
| `@hirobius/design-system` | All components (primitives, patterns, templates) |
|
|
47
|
+
| `@hirobius/design-system/tokens.css` | Token CSS variables (import once at app root) |
|
|
48
|
+
| `@hirobius/design-system/tokens` | Token values as TS constants |
|
|
49
|
+
| `@hirobius/design-system/cn` | `cn()` class-merge helper |
|
|
50
|
+
| `@hirobius/design-system/manifest` | The HDS manifest JSON |
|
|
51
|
+
| `@hirobius/design-system/contexts` | Theme / language / tenant / font providers |
|
|
52
|
+
| `@hirobius/design-system/brand` | Palette → HDS-semantic overlay bridge (static / SSR / Astro; see [`docs/CONSUMING.md` §12](docs/CONSUMING.md#12-static-astro-sites--the-brand-overlay-bridge)) |
|
|
53
|
+
| `@hirobius/design-system/scroll` | Opt-in scroll-motion primitives — `SmoothScroll` (Lenis), `useScrollProgress` (Motion). Requires the optional peer `lenis`. See [`docs/CONSUMING.md` §13](docs/CONSUMING.md#13-scroll-motion-the-scroll-subpath) |
|
|
54
|
+
| `@hirobius/design-system/patterns` | The 22 `pattern`-tier components (nav shells, feeds, rails, pickers — see hds#254). Also still re-exported from the root for one minor (`@deprecated`, removed at the next major) — prefer this subpath in new code. |
|
|
55
|
+
| `@hirobius/design-system/icons` | Curated Lucide icon set for `IconButton` / `Icon` (`Ellipsis`, `Pencil`, `Trash2`, `X`, …). Names listed in the manifest `iconSet`. |
|
|
56
|
+
| `@hirobius/design-system/static.css` | CSS-only static-primitive layer — `.hds-badge`/`.hds-card`/`.hds-alert`/`.hds-divider`/`.hds-tag` classes, no React. See [`docs/CONSUMING.md` §14](docs/CONSUMING.md#14-css-only-static-primitives--badgecardalertdividertag-with-no-react) |
|
|
57
|
+
|
|
58
|
+
The package is **ESM-only**, so consume it with a modern bundler (Vite, Next.js,
|
|
59
|
+
Remix, Webpack 5+) or a Node ≥ 20 ESM runtime. Built `.d.ts` declarations ship in
|
|
60
|
+
`dist/types`, so TypeScript consumers get full types with no extra config.
|
|
61
|
+
|
|
62
|
+
Every component's props type is exported under its own name — `AlertProps`,
|
|
63
|
+
`ButtonProps`, `GridProps`, and so on — so wrapping one needs no
|
|
64
|
+
`React.ComponentProps<typeof X>`:
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import { Alert, type AlertProps } from '@hirobius/design-system';
|
|
68
|
+
|
|
69
|
+
export const Notice = (props: AlertProps) => <Alert tone="info" {...props} />;
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Types that are implementation detail stay internal on purpose: the cva variant
|
|
73
|
+
aliases, composition bases, and the props of sub-components the package does not
|
|
74
|
+
export.
|
|
75
|
+
|
|
76
|
+
**Next.js App Router / React Server Components.** Every React-bearing entry — the
|
|
77
|
+
main barrel, `contexts`, `form`, `scroll`, `patterns` — ships with `'use client'`, so you can
|
|
78
|
+
import components straight into a Server Component and they render as client
|
|
79
|
+
components with no wrapper. The framework-free subpaths — `tokens`, `cn`,
|
|
80
|
+
`manifest`, `brand`, `mui` — deliberately carry **no** directive: they have no
|
|
81
|
+
React in them, and marking them would turn their exports into opaque client
|
|
82
|
+
references when you use them on the server (`tokens.color.primary` in a layout,
|
|
83
|
+
`brand` at the edge). Import those from server code freely.
|
|
84
|
+
|
|
85
|
+
**Density.** Put `data-density="compact"` on the same `[data-hds]` scope element (or `<html>`) to tighten `semantic.space.scale.*`, surface padding and region gutter; `Table` follows it unless given a `density` prop.
|
|
86
|
+
|
|
87
|
+
## 2.5. Lint discipline (optional)
|
|
88
|
+
|
|
89
|
+
`@hirobius/eslint-plugin-hds` flags raw hex/px values in `style`, `className`,
|
|
90
|
+
and `Box` `sx` props so token discipline shows up in your editor, not just at
|
|
91
|
+
review time. See [`docs/CONSUMING.md` §11](docs/CONSUMING.md#11-lint-discipline--the-consumer-eslint-plugin)
|
|
92
|
+
and [`scripts/eslint-plugin-hds/README.md`](scripts/eslint-plugin-hds/README.md).
|
|
93
|
+
|
|
94
|
+
## 2.6. Agent context
|
|
95
|
+
|
|
96
|
+
The package ships the docs written for agents, so an agent in your repo can read
|
|
97
|
+
them without network access. Under `node_modules/@hirobius/design-system/`:
|
|
98
|
+
|
|
99
|
+
- `llms.txt` (also `public/llms.txt`) - the system map and index of topic slices in `public/llms/`
|
|
100
|
+
- `public/llms-full.txt` - the map, the full `DESIGN.md` and a props digest for every component
|
|
101
|
+
- `DESIGN.md` - the lean visual spec
|
|
102
|
+
- `src/app/data/component-api.json` - full prop reference
|
|
103
|
+
|
|
104
|
+
The same files are served at <https://hirobius-design-system.vercel.app/llms.txt>
|
|
105
|
+
(and `/llms-full.txt`, `/llms/components.txt`, `/DESIGN.md`, `/component-api.json`).
|
|
106
|
+
|
|
107
|
+
## 3. Receiving updates
|
|
108
|
+
|
|
109
|
+
Releases follow [semver](https://semver.org/) and are tracked in
|
|
110
|
+
`CHANGELOG.md`. To update:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
pnpm update @hirobius/design-system # latest within your version range
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
For a major (breaking) release, bump the version explicitly and review the
|
|
117
|
+
CHANGELOG entry. Token or export changes are released as majors.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### Maintainers: cutting a release
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pnpm changeset add # record a patch/minor/major bump + notes
|
|
125
|
+
pnpm changeset:version # apply bumps + regenerate CHANGELOG.md
|
|
126
|
+
# commit + push to main → the Release workflow publishes to public npm
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
CI (`.github/workflows/release.yml`) automates steps 2–3 on merge to `main`.
|
|
130
|
+
|
|
131
|
+
Publishing to npm uses **Trusted Publishing (OIDC)**: no npm token exists. The
|
|
132
|
+
package's Trusted Publisher entry on npmjs.com (org `hirobius`, repo `hds`,
|
|
133
|
+
workflow `release.yml`, no environment) lets the workflow mint a short-lived
|
|
134
|
+
publish credential per run. If a publish fails with `ENEEDAUTH`/`E404`, that
|
|
135
|
+
entry is missing or no longer matches the workflow filename.
|
|
136
|
+
|
|
137
|
+
One repo secret is required, and the workflow fails loudly naming it if it is
|
|
138
|
+
missing or expired:
|
|
139
|
+
|
|
140
|
+
| Secret | What it does | If it lapses |
|
|
141
|
+
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
|
|
142
|
+
| `RELEASE_PAT` | Authors the "Version Packages" PR and pushes tags. A **fine-grained** PAT scoped to `hirobius/hds` with Contents + Pull requests set to Read and write. | The run fails at the guard step, before anything builds. |
|
|
143
|
+
|
|
144
|
+
`RELEASE_PAT` exists because GitHub does not start workflow runs from events
|
|
145
|
+
created with `GITHUB_TOKEN`. With `GITHUB_TOKEN`, `ci.yml` never fires on the
|
|
146
|
+
release PR, so the required `Lean gate set` check cannot run and the PR is
|
|
147
|
+
unmergeable. A user-owned token is not subject to that guard, so the release PR
|
|
148
|
+
is tested like any other PR. Fine-grained tokens expire — when this one does,
|
|
149
|
+
the failure names the secret and the fix rather than silently reverting to the
|
|
150
|
+
deadlock.
|
package/DESIGN.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
<!-- GENERATED FILE — DO NOT EDIT. Source: DESIGN.source.md. Regenerate with `pnpm tokens`. -->
|
|
2
|
+
|
|
3
|
+
# DESIGN.md
|
|
4
|
+
|
|
5
|
+
A machine- and human-readable specification of the Hirobius Design System for AI agents, design tools, and humans generating or reviewing UI. Values are sourced from `hirobius.tokens.json`; narrative rules are hand-authored. For the full tokens reference see `DESIGN-HANDOFF.md`.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
Hirobius is a clean, systems-first visual language that bridges digital precision and physical fabrication through a high-contrast monochromatic palette and a single accent that each tenant sets — neutral by default (#208). Its personality is disciplined, tactile, and quietly technical: structure is explicit, motion is purposeful, and visual noise is stripped away. Whitespace is treated as material, not leftover space, so every screen should feel deliberate, breathable, and exact.
|
|
10
|
+
|
|
11
|
+
## Colors
|
|
12
|
+
|
|
13
|
+
<!-- auto:start:colors -->
|
|
14
|
+
**One accent — one neutral system.**
|
|
15
|
+
|
|
16
|
+
- **Accent** (`#111111`): CTAs, active states, selected focus rings, and the single brand accent. `semantic.accent.rest` — a per-tenant knob, not a fixed hue (#208).
|
|
17
|
+
- **Neutral**: backgrounds, surfaces, borders, text. True monochromatic — `primitive.color.neutral.50` through `950`. Use `semantic.color.surface.*`, `semantic.color.content.*`, `semantic.color.border.*`; never reach directly for primitives in components.
|
|
18
|
+
- **Feedback — Error** (`#B91C1C`): destructive confirms, error banners, validation failures. `semantic.color.feedback.error`.
|
|
19
|
+
- **Feedback — Success** (`#047857`): positive confirmations, completed states. `semantic.color.feedback.success`.
|
|
20
|
+
- **Feedback — Warning** (`#92400E`): cautions, recoverable issues. `semantic.color.feedback.warning`.
|
|
21
|
+
- **Feedback — Info** (`#1E2EFD`): neutral announcements and inline guidance. `semantic.color.feedback.info`.
|
|
22
|
+
|
|
23
|
+
Feedback hues are never decorative; do not use them as accents. Light and dark mode values are defined per-token in `hirobius.tokens.json`.
|
|
24
|
+
<!-- auto:end:colors -->
|
|
25
|
+
|
|
26
|
+
## Typography
|
|
27
|
+
|
|
28
|
+
<!-- auto:start:typography -->
|
|
29
|
+
HDS ships three typefaces — each with a distinct and exclusive role:
|
|
30
|
+
|
|
31
|
+
- **Display / Heading font**: Satoshi. Bound exclusively to `display`, `h1`, `h2`, and `h3` styles. Never used for body copy or UI labels.
|
|
32
|
+
- **Body / UI font**: Satoshi. All prose, labels, small text, captions, and UI copy.
|
|
33
|
+
- **Mono font**: Geist Mono. Reserved for tokens, code, technical callouts, and metric readouts.
|
|
34
|
+
|
|
35
|
+
Weights declared: `400` regular, `500` medium, `700` bold. Heading styles (display · h1 · h2 · h3) use `700` bold; body, UI, and caption use `500` medium. Declared but unused by any semantic role: `400` regular.
|
|
36
|
+
|
|
37
|
+
### Type ramp
|
|
38
|
+
|
|
39
|
+
| Role | Size (desktop max) | Weight | Use |
|
|
40
|
+
| --- | --- | --- | --- |
|
|
41
|
+
| `semantic.typography.display` | 60px | 700 | Display headline |
|
|
42
|
+
| `semantic.typography.h1` | 48px | 700 | Primary section headings (h1) |
|
|
43
|
+
| `semantic.typography.h2` | 30px | 700 | Secondary section headings (h2) |
|
|
44
|
+
| `semantic.typography.h3` | 20px | 700 | Component and card headers (h3) |
|
|
45
|
+
| `semantic.typography.body` | 16px | 500 | Body prose |
|
|
46
|
+
| `semantic.typography.ui` | 14px | 500 | UI text for nav, labels, captions |
|
|
47
|
+
| `semantic.typography.caption` | 12px | 500 | Caption / helper text |
|
|
48
|
+
| `semantic.typography.eyebrow` | 12px | 500 | Eyebrow / kicker label |
|
|
49
|
+
| `semantic.typography.mono` | 14px | 400 | Monospace for code, token names, shortcuts |
|
|
50
|
+
| `semantic.typography.lineHeight.none` | | | Semantic alias for leading-none (1) |
|
|
51
|
+
|
|
52
|
+
> Responsive `clamp()` overrides live in `src/styles/theme.css`; tokens store the desktop-max static value.
|
|
53
|
+
<!-- auto:end:typography -->
|
|
54
|
+
|
|
55
|
+
## Spacing
|
|
56
|
+
|
|
57
|
+
<!-- auto:start:spacing -->
|
|
58
|
+
**Base unit: 4px.** All spacing snaps to the primitive scale; never introduce half-steps or arbitrary px values.
|
|
59
|
+
|
|
60
|
+
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
|
+
Use `primitive.space.*` for layout rhythm, padding, and gaps. 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
|
+
<!-- auto:end:spacing -->
|
|
64
|
+
|
|
65
|
+
## Corner-Radius Policy
|
|
66
|
+
|
|
67
|
+
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.
|
|
68
|
+
|
|
69
|
+
<!-- auto:start:radius -->
|
|
70
|
+
| Tier | Value | Token | Applies to |
|
|
71
|
+
| --- | --- | --- | --- |
|
|
72
|
+
| Action | `8px` | `semantic.radius.action` | Buttons, inputs, badges, alerts, disclosures, segmented control items |
|
|
73
|
+
| Container | `12px` | `role.radius` + 4px (`rounded-lg`) | Cards, surfaces, segmented control surface, modal/sheet containers |
|
|
74
|
+
| Full | `9999px` | `primitive.radius.full` | Pills, avatars, indicator dots, any intentionally circular form |
|
|
75
|
+
| Zero | `0px` | `primitive.radius.0` | Outer canvas / substrate boundaries only — never on everyday UI controls |
|
|
76
|
+
<!-- auto:end:radius -->
|
|
77
|
+
|
|
78
|
+
## Elevation
|
|
79
|
+
|
|
80
|
+
Depth is communicated through 4 elevation roles bundled by `semantic.elevation.*`. Each role bundles a surface, shadow, and border so primitives can't mismatch them.
|
|
81
|
+
|
|
82
|
+
### Elevation roles
|
|
83
|
+
|
|
84
|
+
| Surface | Role token | Background | Shadow | Border |
|
|
85
|
+
| -------------------------------------- | ----------------------------- | ----------------- | ----------------- | ------------------- |
|
|
86
|
+
| Card / panel resting | `semantic.elevation.flat` | `surface.page` | none | `border.subtle` 1px |
|
|
87
|
+
| Card / panel lifted (interactive only) | `semantic.elevation.raised` | `surface.raised` | `shadow.subtle` | none |
|
|
88
|
+
| Popover / dropdown / tooltip | `semantic.elevation.floating` | `surface.raised` | `shadow.floating` | none |
|
|
89
|
+
| Dialog / sheet / modal | `semantic.elevation.overlay` | `surface.overlay` | `shadow.overlay` | none |
|
|
90
|
+
|
|
91
|
+
Cards default to `flat`. They lift to `raised` only on interactive hover or when explicitly elevated above siblings. Never combine `raised` with a border — depth is one mechanism (border OR shadow), not both stacked.
|
|
92
|
+
|
|
93
|
+
Overlays (Dialog, AlertDialog, Menu, ContextMenu, Popover, Select, HoverCard, Tooltip) portal into the nearest `data-hds` scope, so they inherit its theme (for example `<div data-hds data-theme="dark">`); pass `container` on the Content part to override.
|
|
94
|
+
|
|
95
|
+
## Motion
|
|
96
|
+
|
|
97
|
+
<!-- auto:start:motion -->
|
|
98
|
+
Motion (lift on hover, parallax) is the interaction-affordance layer; static depth comes from the `semantic.elevation.*` role-token bundles.
|
|
99
|
+
|
|
100
|
+
### Duration tiers
|
|
101
|
+
|
|
102
|
+
| Tier | Value | When to use |
|
|
103
|
+
| --- | --- | --- |
|
|
104
|
+
| `primitive.duration.instant` | `100ms` | Immediate dismissals, binary toggles |
|
|
105
|
+
| `primitive.duration.short` | `150ms` | Productive micro-interactions (default for hover/focus/press) |
|
|
106
|
+
| `primitive.duration.medium` | `250ms` | Expressive entrances, teaching moments |
|
|
107
|
+
| `primitive.duration.long` | `400ms` | Spatial movement, page travel, parallax |
|
|
108
|
+
|
|
109
|
+
### Semantic intents
|
|
110
|
+
|
|
111
|
+
| Intent | Duration | Purpose |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| `semantic.motion.productive` | `150ms` | For micro-interactions and status changes. No deformation. |
|
|
114
|
+
| `semantic.motion.expressive` | `250ms` | For teaching moments and significant UI entries. Includes physics-based squish. |
|
|
115
|
+
| `semantic.motion.spatial` | `400ms` | For elements traveling long distances across the viewport. |
|
|
116
|
+
| `semantic.motion.exit` | `100ms` | For elements being removed from the DOM. |
|
|
117
|
+
|
|
118
|
+
Default most interactive feedback to `productive` (150ms, decelerate). Reserve `expressive` (250ms, spring) for teaching moments where the motion itself carries meaning. `spatial` (400ms) is for travel, not decoration.
|
|
119
|
+
|
|
120
|
+
### Other semantic motion tokens
|
|
121
|
+
|
|
122
|
+
| Token | Value | Purpose |
|
|
123
|
+
| --- | --- | --- |
|
|
124
|
+
| `semantic.motion.distance` | `24px` | Travel distance for a scroll-reveal translateY offset. Coincides in value with semantic.space.scale.md today, but names a motion travel distance rather than a spacing tier (hds#242). |
|
|
125
|
+
<!-- auto:end:motion -->
|
|
126
|
+
|
|
127
|
+
## Components
|
|
128
|
+
|
|
129
|
+
<!-- auto:start:components -->
|
|
130
|
+
| Component | Radius | States | Guidance |
|
|
131
|
+
| --- | --- | --- | --- |
|
|
132
|
+
| **Buttons** (`Button`) | `8px` (`semantic.radius.action`) | default · hover · focus · active · disabled · loading | Three variants: primary (accent-filled), secondary (outline), tertiary (ghost). Primary uses `semantic.accent.*` ramp per state. Icon buttons (`IconButton`) follow the same token surface. Icon-only actions use `IconButton` with an icon from `@hirobius/design-system/icons`. |
|
|
133
|
+
| **Inputs** (`Input`) | `8px` (`semantic.radius.action`) | default · focus · filled · error · disabled · loading | Border-driven treatment; no filled background by default. Focus uses `semantic.color.border.accent` plus a 2px outline offset. Error swaps to `component.input.borderError`. |
|
|
134
|
+
| **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
|
+
| **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. |
|
|
136
|
+
| **Alerts** (`Alert`) | `8px` (via `hds.borderRadius.4`) | info · success · warning · error | Inline banner pattern with icon + message + optional action. Tone is carried by left-border color, not by tinted fills. |
|
|
137
|
+
| **Disclosures** (`Disclosure`) | `8px` (`hds.borderRadius.action`) | collapsed · expanded · hover · focus | Accordion primitive. Expansion uses `semantic.motion.productive`; no spring bounce. Dividers follow `semantic.color.border.subtle`. |
|
|
138
|
+
| **Toggles** (`Toggle`) | `full` (pill track + circular thumb) | off · on · focus · disabled | Accent-filled track in the on state; neutral track otherwise. Track + thumb transitions share `semantic.motion.productive`. |
|
|
139
|
+
| **Segmented Control** (`SegmentedControl`) | Outer `12px` · inner segments `8px` | rest · hover · selected · disabled | Selected segment fills with the accent; unselected segments are transparent. Use for 2–5 mutually exclusive options; beyond that, prefer `Select`. |
|
|
140
|
+
|
|
141
|
+
See `public/hds-manifest.json` and `src/app/data/component-api.json` for the full inventory and prop tables.
|
|
142
|
+
<!-- auto:end:components -->
|
|
143
|
+
|
|
144
|
+
### Card Anatomy (mandatory — all properties non-negotiable)
|
|
145
|
+
|
|
146
|
+
Every HDS card surface must conform to this anatomy exactly. No creative interpretation is permitted on any of these properties.
|
|
147
|
+
|
|
148
|
+
| Property | Required value | Forbidden |
|
|
149
|
+
| ------------------------ | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
150
|
+
| Background | `var(--semantic-color-surface-raised)` | Any gradient, tinted fill, or custom color |
|
|
151
|
+
| Border | `1px solid var(--semantic-color-border-default)` | `box-shadow` as an elevation substitute |
|
|
152
|
+
| 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
|
+
| Padding | `var(--semantic-space-component-padding)` or `<HdsSurface padding="component">` | Raw pixel values or ad hoc insets |
|
|
154
|
+
| 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.heading3` / `<HdsText variant="heading3">` | Any other type style for the primary card heading |
|
|
156
|
+
| Subtitle / meta | `hds.typeStyles.caption` + `var(--semantic-color-content-secondary)` | Primary content color or body size for secondary text |
|
|
157
|
+
| Hover (interactive only) | `transform: scale(1.02)` | Background fill change, border color shift, or opacity fade on hover |
|
|
158
|
+
|
|
159
|
+
Never use on any card surface: gradient backgrounds, glow effects, frosted glass (`backdrop-filter: blur`), decorative overlays, gradient borders, colored or tinted backgrounds, inner shadows, patterned fills, shimmer or noise effects.
|
|
160
|
+
|
|
161
|
+
## Screen Patterns
|
|
162
|
+
|
|
163
|
+
Five patterns sit above the primitives so that every screen is composed the same way. Import them from `@hirobius/design-system/patterns`.
|
|
164
|
+
|
|
165
|
+
### Page title rule
|
|
166
|
+
|
|
167
|
+
Every screen has exactly one `PageHeader`. The page title is `heading2` (30px), a fixed size with no size prop; `level` changes only the DOM heading element. `display` and `h1` are reserved for marketing and landing surfaces and are never a screen title. Breadcrumb goes in the `breadcrumb` slot, one status `Badge` in `status`, screen-level actions in `actions`.
|
|
168
|
+
|
|
169
|
+
### Which one, when
|
|
170
|
+
|
|
171
|
+
Pick by what the thing is. The first four rows are for a headline number or a state; the rest are for containers and sections.
|
|
172
|
+
|
|
173
|
+
| Component | Use it for | Use instead |
|
|
174
|
+
| -------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
175
|
+
| `MetricTiles` | The default for any row of headline numbers on a screen. One fixed tile size. | Nothing: start here. |
|
|
176
|
+
| `Stat` | An inline number inside prose or a dense list. | `MetricTiles` when the number stands alone in a row of its own. |
|
|
177
|
+
| `Card.Metric` | A metric inside an existing `Card` only. | `MetricTiles` when there is no enclosing `Card`. |
|
|
178
|
+
| `StatusTile` | State with notes and a trailing badge, never a number. | `MetricTiles` when the tile is a number; `StatusTile` never carries a value. |
|
|
179
|
+
| `Card` | A titled or interactive content object (header, body, footer, or selectable). | `Surface` when there is no title and nothing to press. |
|
|
180
|
+
| `Surface` | A plain padded background around content that has no header of its own. | `Card` when the content is titled or interactive. |
|
|
181
|
+
| `DestructiveSection` | The one irreversible action on a screen, with an explanation and a confirm. | `FormActions` `destructive` slot for a delete beside a form's own buttons. |
|
|
182
|
+
| `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
|
+
|
|
184
|
+
`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
|
+
|
|
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 `heading2`, the label as an eyebrow and the sub line in `caption`. Its tone comes from the fixed feedback vocabulary: `neutral | success | warning | danger | info`.
|
|
187
|
+
|
|
188
|
+
### Destructive and table sections
|
|
189
|
+
|
|
190
|
+
`DestructiveSection` is a titled danger zone with one danger `Button` that opens an `AlertDialog`; it never runs the action from the button itself, only from the dialog's confirm. Put it last on the screen, below the form. `DataTableSection` puts a `Table` under a heading and a toolbar slot. Row actions are consumer nodes in a trailing column, an empty `rows` array shows an `EmptyState`, and the table scrolls horizontally inside the section on narrow widths, so a caller never sets `minWidth`.
|
|
191
|
+
|
|
192
|
+
### Form actions rule
|
|
193
|
+
|
|
194
|
+
`FormActions` puts the primary submit last in DOM order and right-most, with the secondary or cancel action immediately to its left. A destructive action goes on the far left, apart from the other two, and never to the right of the primary. The row is not sticky unless `sticky` is set.
|
|
195
|
+
|
|
196
|
+
## Do's and Don'ts
|
|
197
|
+
|
|
198
|
+
- Don't invent new corner behavior for interactive controls; buttons, inputs, disclosures, and similar action surfaces should follow the shared action radius (`semantic.radius.action`).
|
|
199
|
+
- Don't introduce additional accent hues; Hirobius uses exactly one accent, resolved from `semantic.accent.rest`, over a true monochromatic neutral system. The accent is a per-tenant knob (#208) and defaults to neutral — read the token, never assume a hue.
|
|
200
|
+
- Don't tint neutrals warm or cool; greys should remain genuinely neutral and high-contrast.
|
|
201
|
+
- Don't reach for shadow values directly — bind to a `semantic.elevation.*` role so surface + shadow + border stay paired. Cards default to `flat` (border, no shadow). Popovers/tooltips/dropdowns use `floating`. Dialogs/sheets use `overlay`.
|
|
202
|
+
- Don't hardcode colors, spacing, radius, or typography values when governed tokens already exist.
|
|
203
|
+
- Don't crowd the canvas; if a layout feels compressed, remove complexity before removing breathing room.
|
|
204
|
+
- Don't default repeated roadmap, status, process, or overview groups to outlined cards; prefer open bands, section dividers, accent rails, tabs, disclosures, and whitespace. Cards are for genuinely discrete repeated objects, not the primary layout language.
|
|
205
|
+
- Don't sticker badges onto the ends of prose lines. Status and progress should live in a predictable metadata slot, rail, header zone, table column, or progress surface; badges are reserved for compact state markers where their placement is intentional.
|
|
206
|
+
- Don't add decorative illustration, ornamental icons, or trend-driven filler that lacks functional or structural purpose.
|
|
207
|
+
- Don't make motion playful or bouncy by default; movement should feel controlled, precise, and intentional, with expressive motion used sparingly and only when it teaches or clarifies.
|
|
208
|
+
- Don't add `whileTap` scale transforms for press feedback; use color, border, and state-contrast changes instead — transforms are reserved for spatial motion, not interaction acknowledgment.
|
|
209
|
+
- Don't define a global disabled-state rule (opacity multiplier, saturation ramp, etc.); disabled presentation is governed per-component through its dedicated disabled tokens.
|
|
210
|
+
|
|
211
|
+
## Open Questions
|
|
212
|
+
|
|
213
|
+
Unresolved rules that the live repo cannot yet answer confidently are tracked in [`OPEN_DS_QUESTIONS.md`](OPEN_DS_QUESTIONS.md). Do not invent rules here to fill those gaps.
|
|
214
|
+
|
|
215
|
+
<!-- auto:start:build-meta -->
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
> Generated from `hirobius.tokens.json` (381 tokens) and `public/hds-manifest.json` by `scripts/build-design-md.mjs`.
|
|
219
|
+
> Hand-edit `DESIGN.source.md`; this file (`DESIGN.md`) is overwritten by `pnpm tokens`.
|
|
220
|
+
<!-- auto:end:build-meta -->
|
package/README.md
CHANGED
|
@@ -13,8 +13,8 @@ pnpm add @hirobius/design-system
|
|
|
13
13
|
<!-- auto:start:front-door-counts -->
|
|
14
14
|
|
|
15
15
|
- **109** public component modules, exported from `src/index.ts`
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
16
|
+
- **381** DTCG tokens in `hirobius.tokens.json`, compiled to CSS variables and TypeScript constants
|
|
17
|
+
- **484** Storybook stories in **121** story files
|
|
18
18
|
|
|
19
19
|
<!-- auto:end:front-door-counts -->
|
|
20
20
|
|
|
@@ -23,6 +23,24 @@ pnpm add @hirobius/design-system
|
|
|
23
23
|
|
|
24
24
|
The counts are generated from source by `pnpm readme:counts`, which `pnpm tokens` also runs. `scripts/__tests__/front-door.test.mjs` fails if this README claims more than the source has.
|
|
25
25
|
|
|
26
|
+
Agents: install the consumer skill with `npx skills add hirobius/hds --skill hds-consumer` (see `docs/CONSUMING.md`, "Agent skill").
|
|
27
|
+
|
|
28
|
+
## In use
|
|
29
|
+
|
|
30
|
+
<!-- auto:start:consumer-usage -->
|
|
31
|
+
|
|
32
|
+
The Ops dashboard (`hirobius/ops`) is the only verified component-level consumer: **35** of its source files import from `@hirobius/design-system`, using **25** distinct components. The two screenshots below are Ops pages rendered from its `main` (measured at commit `01fce2f`).
|
|
33
|
+
|
|
34
|
+
Other consumers: the split into product apps and token-level sites is not yet confirmed, so it is not stated here.
|
|
35
|
+
|
|
36
|
+
[](docs/images/ops-library.png)
|
|
37
|
+
|
|
38
|
+
[](docs/images/ops-fleet-audit.png)
|
|
39
|
+
|
|
40
|
+
<!-- auto:end:consumer-usage -->
|
|
41
|
+
|
|
42
|
+
The numbers come from `pnpm consumer:usage --root <ops checkout>`, which measures that checkout and records its commit in `docs/data/consumer-usage.json`; without a root it reuses the committed snapshot. `scripts/__tests__/consumer-usage.test.mjs` fails if this section drifts from that snapshot. The product-app and token-level-site split is a declared figure in that file and stays out of this section until `consumersConfirmed` there is set to true.
|
|
43
|
+
|
|
26
44
|
## Using the published package
|
|
27
45
|
|
|
28
46
|
Installing HDS in another project? It ships to the **public npm registry** as
|
|
@@ -178,6 +196,16 @@ The gates are deterministic and need no browser or live site:
|
|
|
178
196
|
- **CI** (`.github/workflows/ci.yml`): typecheck, zero-warning ESLint, token validity and contrast, Vitest, and the consumer smoke build, plus bundle budgets and a Storybook build.
|
|
179
197
|
- **Visual review:** Storybook is the visual verification surface, reviewed by hand. The earlier browser test suite drove a docs site that no longer exists and was removed (ADR-018).
|
|
180
198
|
|
|
199
|
+
### Agent consistency
|
|
200
|
+
|
|
201
|
+
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
|
+
|
|
203
|
+
```text
|
|
204
|
+
Agent consistency 2026-09-30 (harness, design-system 0.18.0): FAIL - builds 3/3, violations 0, axe 0, Jaccard min 0.7368 (limit >= 0.85), light diff max 1.0268% (limit <= 1.5%)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The baseline misses its thresholds on purpose: they are not softened to make it pass.
|
|
208
|
+
|
|
181
209
|
`CLAUDE.md` is the operating contract for agents working in this repo.
|
|
182
210
|
|
|
183
211
|
## Bundle and release hygiene
|