@nurix/etna 0.20.36 → 0.20.38
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/data/catalog/rules/frontend.md +5 -5
- package/data/catalog/rules/pnpm.md +2 -2
- package/data/catalog/skills/nustack/SKILL.md +2 -2
- package/data/catalog/skills/nustack/references/cli-design.md +35 -35
- package/data/catalog/skills/nustack/references/monorepo-master-list.md +6 -6
- package/data/catalog/skills/nustack/references/monorepo-setup.md +13 -13
- package/data/catalog/skills/nustack/references/publishing.md +40 -39
- package/data/harness-index.json +1 -1
- package/data/harnesses/code/harness.json +2 -2
- package/data/harnesses/code/hooks/approved-packages.md +1 -2
- package/data/harnesses/code/rules/frontend.md +5 -5
- package/data/harnesses/code/rules/pnpm.md +2 -2
- package/data/harnesses/code/skills/boundary/SKILL.md +33 -22
- package/data/harnesses/code/skills/boundary/references/index.md +2 -4
- package/data/harnesses/code/skills/boundary/references/mechanisms.md +3 -10
- package/data/harnesses/code/skills/boundary/references/packs/build-config-runtime.md +1 -1
- package/data/harnesses/code/skills/boundary/references/packs/frontend.md +17 -17
- package/data/harnesses/code/skills/boundary/references/packs/generic-tail.md +1 -1
- package/data/harnesses/code/skills/brainstorm/README.md +11 -13
- package/data/harnesses/code/skills/brainstorm/SKILL.md +150 -132
- package/data/harnesses/code/skills/brainstorm/references/doctrine.md +48 -51
- package/data/harnesses/code/skills/brainstorm/references/harvest.md +51 -56
- package/data/harnesses/code/skills/brainstorm/references/session-schema.md +48 -42
- package/data/harnesses/code/skills/design/SKILL.md +31 -35
- package/data/harnesses/code/skills/design/components/charts.md +121 -43
- package/data/harnesses/code/skills/design/components/collections.md +209 -41
- package/data/harnesses/code/skills/design/components/feedback.md +86 -25
- package/data/harnesses/code/skills/design/components/hooks.md +198 -12
- package/data/harnesses/code/skills/design/components/index.md +93 -71
- package/data/harnesses/code/skills/design/components/inputs.md +249 -157
- package/data/harnesses/code/skills/design/components/layout.md +98 -38
- package/data/harnesses/code/skills/design/components/media.md +46 -25
- package/data/harnesses/code/skills/design/components/motion.md +56 -46
- package/data/harnesses/code/skills/design/components/navigation.md +54 -15
- package/data/harnesses/code/skills/design/components/overlays.md +51 -41
- package/data/harnesses/code/skills/design/components/setup.md +84 -5
- package/data/harnesses/code/skills/design/components/typography.md +72 -22
- package/data/harnesses/code/skills/design/components/utils.md +47 -13
- package/data/harnesses/code/skills/design/data/chrome/board-column.md +12 -12
- package/data/harnesses/code/skills/design/data/chrome/data-table.md +6 -6
- package/data/harnesses/code/skills/design/data/chrome/side-panel.md +15 -15
- package/data/harnesses/code/skills/design/data/chrome/sidebar.md +25 -25
- package/data/harnesses/code/skills/design/data/chrome/stat-cards.md +22 -22
- package/data/harnesses/code/skills/design/data/chrome/topbar.md +12 -12
- package/data/harnesses/code/skills/design/data/iconography.md +2 -2
- package/data/harnesses/code/skills/design/references/compose.md +14 -16
- package/data/harnesses/code/skills/design/references/emission.md +6 -6
- package/data/harnesses/code/skills/design/references/foundations.md +5 -5
- package/data/harnesses/code/skills/design/references/intake.md +6 -6
- package/data/harnesses/code/skills/design/references/object-pass.md +1 -1
- package/data/harnesses/code/skills/design/references/ship-gate.md +5 -5
- package/data/harnesses/code/skills/design/references/ux-patterns/Readme.md +4 -4
- package/data/harnesses/code/skills/design/references/ux-patterns/ai-layer.md +1 -1
- package/data/harnesses/code/skills/design/references/ux-patterns/container-choice.md +1 -1
- package/data/harnesses/code/skills/design/references/ux-patterns/dashboards.md +1 -1
- package/data/harnesses/code/skills/design/references/ux-patterns/modes.md +1 -1
- package/data/harnesses/code/skills/design/references/ux-patterns/object-model.md +2 -2
- package/data/harnesses/code/skills/design/references/ux-patterns/promotion-demotion.md +1 -1
- package/data/harnesses/code/skills/design/references/ux-patterns/settings.md +1 -1
- package/data/harnesses/code/skills/design/references/ux-patterns/standard-pages.md +1 -1
- package/data/harnesses/code/skills/design/scripts/README-validator.md +20 -8
- package/data/harnesses/code/skills/design/scripts/validate-design-md.mjs +15 -10
- package/data/harnesses/code/skills/documentation/SKILL.md +5 -5
- package/data/harnesses/code/skills/documentation/references/examples.md +22 -22
- package/data/harnesses/code/skills/documentation/references/frontmatter.md +3 -3
- package/data/harnesses/code/skills/documentation/references/instruction-style.md +3 -3
- package/data/harnesses/code/skills/documentation/references/source-comments.md +3 -3
- package/data/harnesses/code/skills/documentation/references/style-guide.md +5 -5
- package/data/harnesses/code/skills/documentation/references/untemplated.md +8 -8
- package/data/harnesses/code/skills/documentation/scripts/doc-lint.mjs +3 -2
- package/data/harnesses/code/skills/documentation/templates/Readme.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/acceptance-criteria.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/api-reference.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/architecture-decision.md +3 -7
- package/data/harnesses/code/skills/documentation/templates/behavioral-checklist.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/changelog-entry.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/changelog-index.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/docs-readme.md +3 -3
- package/data/harnesses/code/skills/documentation/templates/doctrine.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/domain-model.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/environment-registry.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/feature.md +3 -3
- package/data/harnesses/code/skills/documentation/templates/handover.md +2 -2
- package/data/harnesses/code/skills/documentation/templates/implementation-map.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/migration-runbook.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/non-goals.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/operations-procedure.md +2 -2
- package/data/harnesses/code/skills/documentation/templates/package.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/protected-files.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/root-readme.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/security-overview.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/system-overview.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/system.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/testing-strategy.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/ux-behavior.md +3 -5
- package/data/harnesses/code/skills/issue-bridge/SKILL.md +51 -16
- package/data/harnesses/code/skills/issue-bridge/references/trackers/github.md +5 -5
- package/data/harnesses/code/skills/issue-bridge/references/trackers/jira.md +11 -8
- package/data/harnesses/code/skills/issue-bridge/references/trackers/linear.md +16 -11
- package/data/harnesses/code/skills/nustack-graph/SKILL.md +38 -29
- package/data/harnesses/code/skills/polygraph/references/dispatch.md +31 -18
- package/data/harnesses/code/skills/polygraph/references/rows.md +11 -6
- package/data/harnesses/code/skills/schema-design/SKILL.md +39 -41
- package/data/harnesses/code/skills/test/SKILL.md +38 -42
- package/data/harnesses/code/skills/testing/SKILL.md +83 -73
- package/data/harnesses/code/skills/workflow-mapper/SKILL.md +156 -193
- package/data/harnesses/code/skills/workflow-mapper/references/patterns.md +15 -15
- package/data/harnesses/delivery/harness.json +2 -2
- package/data/harnesses/delivery/skills/brainstorm/README.md +11 -13
- package/data/harnesses/delivery/skills/brainstorm/SKILL.md +150 -132
- package/data/harnesses/delivery/skills/brainstorm/references/doctrine.md +48 -51
- package/data/harnesses/delivery/skills/brainstorm/references/harvest.md +51 -56
- package/data/harnesses/delivery/skills/brainstorm/references/session-schema.md +48 -42
- package/data/harnesses/delivery/skills/cost-report/SKILL.md +10 -10
- package/data/harnesses/delivery/skills/documentation/SKILL.md +5 -5
- package/data/harnesses/delivery/skills/documentation/references/examples.md +22 -22
- package/data/harnesses/delivery/skills/documentation/references/frontmatter.md +3 -3
- package/data/harnesses/delivery/skills/documentation/references/instruction-style.md +3 -3
- package/data/harnesses/delivery/skills/documentation/references/source-comments.md +3 -3
- package/data/harnesses/delivery/skills/documentation/references/style-guide.md +5 -5
- package/data/harnesses/delivery/skills/documentation/references/untemplated.md +8 -8
- package/data/harnesses/delivery/skills/documentation/scripts/doc-lint.mjs +3 -2
- package/data/harnesses/delivery/skills/documentation/templates/Readme.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/acceptance-criteria.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/api-reference.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/architecture-decision.md +3 -7
- package/data/harnesses/delivery/skills/documentation/templates/behavioral-checklist.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/changelog-entry.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/changelog-index.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/docs-readme.md +3 -3
- package/data/harnesses/delivery/skills/documentation/templates/doctrine.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/domain-model.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/environment-registry.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/feature.md +3 -3
- package/data/harnesses/delivery/skills/documentation/templates/handover.md +2 -2
- package/data/harnesses/delivery/skills/documentation/templates/implementation-map.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/migration-runbook.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/non-goals.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/operations-procedure.md +2 -2
- package/data/harnesses/delivery/skills/documentation/templates/package.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/protected-files.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/root-readme.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/security-overview.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/system-overview.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/system.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/testing-strategy.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/ux-behavior.md +3 -5
- package/data/harnesses/delivery/skills/escalate/SKILL.md +11 -11
- package/data/harnesses/delivery/skills/prompt-eval/SKILL.md +8 -8
- package/data/harnesses/delivery/skills/prompt-library/SKILL.md +30 -9
- package/data/harnesses/delivery/skills/route/SKILL.md +8 -8
- package/data/harnesses/etna/harness.json +2 -2
- package/data/harnesses/etna/skills/boundary/SKILL.md +33 -22
- package/data/harnesses/etna/skills/boundary/references/index.md +2 -4
- package/data/harnesses/etna/skills/boundary/references/mechanisms.md +3 -10
- package/data/harnesses/etna/skills/boundary/references/packs/build-config-runtime.md +1 -1
- package/data/harnesses/etna/skills/boundary/references/packs/frontend.md +17 -17
- package/data/harnesses/etna/skills/boundary/references/packs/generic-tail.md +1 -1
- package/data/harnesses/etna/skills/brainstorm/README.md +11 -13
- package/data/harnesses/etna/skills/brainstorm/SKILL.md +150 -132
- package/data/harnesses/etna/skills/brainstorm/references/doctrine.md +48 -51
- package/data/harnesses/etna/skills/brainstorm/references/harvest.md +51 -56
- package/data/harnesses/etna/skills/brainstorm/references/session-schema.md +48 -42
- package/data/harnesses/etna/skills/documentation/SKILL.md +5 -5
- package/data/harnesses/etna/skills/documentation/references/examples.md +22 -22
- package/data/harnesses/etna/skills/documentation/references/frontmatter.md +3 -3
- package/data/harnesses/etna/skills/documentation/references/instruction-style.md +3 -3
- package/data/harnesses/etna/skills/documentation/references/source-comments.md +3 -3
- package/data/harnesses/etna/skills/documentation/references/style-guide.md +5 -5
- package/data/harnesses/etna/skills/documentation/references/untemplated.md +8 -8
- package/data/harnesses/etna/skills/documentation/scripts/doc-lint.mjs +3 -2
- package/data/harnesses/etna/skills/documentation/templates/Readme.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/acceptance-criteria.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/api-reference.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/architecture-decision.md +3 -7
- package/data/harnesses/etna/skills/documentation/templates/behavioral-checklist.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/changelog-entry.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/changelog-index.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/docs-readme.md +3 -3
- package/data/harnesses/etna/skills/documentation/templates/doctrine.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/domain-model.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/environment-registry.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/feature.md +3 -3
- package/data/harnesses/etna/skills/documentation/templates/handover.md +2 -2
- package/data/harnesses/etna/skills/documentation/templates/implementation-map.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/migration-runbook.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/non-goals.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/operations-procedure.md +2 -2
- package/data/harnesses/etna/skills/documentation/templates/package.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/protected-files.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/root-readme.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/security-overview.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/system-overview.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/system.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/testing-strategy.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/ux-behavior.md +3 -5
- package/data/harnesses/etna/skills/workflow-mapper/SKILL.md +156 -193
- package/data/harnesses/etna/skills/workflow-mapper/references/patterns.md +15 -15
- package/data/harnesses/hr/harness.json +2 -2
- package/data/harnesses/hr/skills/evaluator/SKILL.md +71 -73
- package/data/harnesses/hr/skills/evaluator/criteria/code-quality.md +28 -47
- package/data/harnesses/hr/skills/evaluator/criteria/deployment-readiness.md +33 -43
- package/data/harnesses/hr/skills/evaluator/criteria/maintainability.md +34 -46
- package/data/harnesses/hr/skills/evaluator/criteria/mechanical-ambition.md +13 -13
- package/data/harnesses/hr/skills/evaluator/criteria/problem-substantiveness.md +7 -7
- package/data/harnesses/hr/skills/evaluator/criteria/testing.md +36 -41
- package/data/harnesses/hr/skills/evaluator/criteria/ux-mechanics.md +25 -11
- package/data/harnesses/hr/skills/evaluator/criteria/value-articulation.md +7 -7
- package/data/harnesses/hr/skills/evaluator/preflight.md +27 -46
- package/data/harnesses/hr/skills/interview-eval/SKILL.md +37 -42
- package/data/harnesses/hr/skills/interview-eval/rubric.md +21 -23
- package/package.json +2 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: side-panel
|
|
3
3
|
class: d2
|
|
4
|
-
sourced: 2026-07
|
|
5
|
-
source: "@nurix/components Sheet (
|
|
4
|
+
sourced: 2026-10-07
|
|
5
|
+
source: "@nurix/components Sheet (Base UI Dialog with a logical side, a size ladder and a header/body/footer skeleton) — ~/dev/components"
|
|
6
6
|
license: open
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -14,23 +14,23 @@ The slide-in detail/edit panel: a modal surface anchored to a screen edge with a
|
|
|
14
14
|
|
|
15
15
|
| Slot | Library part | Spec |
|
|
16
16
|
| --- | --- | --- |
|
|
17
|
-
| Root | `Sheet` | Open-state owner; controlled (`open`/`onOpenChange`) or uncontrolled via trigger. |
|
|
18
|
-
| Trigger | `
|
|
19
|
-
| Overlay | `
|
|
20
|
-
| Panel | `
|
|
21
|
-
| Header | `
|
|
22
|
-
| Body | `
|
|
23
|
-
| Footer | `
|
|
24
|
-
| Close | `
|
|
17
|
+
| Root | `Sheet { open?, defaultOpen?, onOpenChange?, modal?, dismissible?, intent?, labels? }` | Open-state owner; controlled (`open`/`onOpenChange`) or uncontrolled via trigger. `dismissible={false}` refuses Escape and the scrim; `intent="danger"` makes it an alert dialog. |
|
|
18
|
+
| Trigger | `Sheet.Trigger` | Any element (`render`) that opens the panel — a table row's action, a card, a button. |
|
|
19
|
+
| Overlay | drawn by `Sheet.Popup` (styled through `overlayClassName`) | Full-viewport scrim behind the panel; fades with open state; click closes. `<Sheet modal={false}>` draws none. |
|
|
20
|
+
| Panel | `Sheet.Popup { side: "end" (default) \| "start" \| "top" \| "bottom", size: "xs" 288px \| "sm" 320px \| "default" 480px \| "lg" 640px \| "full", hasCloseButton? }` | Fixed to the chosen edge — `start`/`end` follow the writing direction — full-height (start/end), hairline border on the attached edge, elevated shadow, vertical flex column. `size` is the width of a start/end panel and the height cap of a top/bottom one. Below the small breakpoint a start/end panel takes three quarters of the width regardless of size. |
|
|
21
|
+
| Header | `Sheet.Header { hasDivider? }` | Shrink-to-content top region: `Sheet.Title` (the accessible name — an untitled panel is named from `labels.title`, "Sheet" by default, so a real title is the spec) + `Sheet.Description` in muted text. |
|
|
22
|
+
| Body | `Sheet.Body` | `flex-1` scroll region — the only part that scrolls; long content never pushes the footer off-screen. Its `className` styles the padded content box; `className="p-0"` drops the inset. |
|
|
23
|
+
| Footer | `Sheet.Footer { hasDivider? }` | Bottom-anchored action row (`mt-auto`): primary commit right, secondary/cancel beside it. |
|
|
24
|
+
| Close | `Sheet.Close` + built-in ✕ | Corner close button (ghost, icon-only, `xs`, top end corner, muted at rest), drawn while the panel is dismissible, plus any element rendered as `Sheet.Close`. |
|
|
25
25
|
|
|
26
26
|
## The eight states
|
|
27
27
|
|
|
28
28
|
| State | Spec | Token roles |
|
|
29
29
|
| --- | --- | --- |
|
|
30
30
|
| default | Open at rest: panel on the base surface over the scrim; header/body/footer stacked; hairline dividers where opted in. | `background`, `border`, `foreground`, `muted-foreground` (description) |
|
|
31
|
-
| hover | Corner close
|
|
31
|
+
| hover | Corner close takes the ghost button's hover wash; interior controls follow their own recipes. | control-level roles |
|
|
32
32
|
| focus | Focus is trapped inside the open panel; the close button and every control show a focus-visible ring. | `ring` |
|
|
33
|
-
| active | Open/closed is expressed as `data-
|
|
33
|
+
| active | Open/closed is expressed as `data-open` / `data-closed`; the slide + fade is a tween on the slow motion step (300ms at the neutral pace) with the travel ease, and reduced motion drops the travel. The A6 panel budget (200–280ms, interruptible) governs — the package's `pace` brings the slow step inside it, and a non-interruptible slide is a bug, not a style. | — |
|
|
34
34
|
| disabled | The commit action disables (~50% opacity, no pointer events) while the form is invalid or a save is in flight; the panel itself never disables — Escape and close always work. | reduced-alpha on controls |
|
|
35
35
|
| loading | Record still resolving: header renders immediately with the known title, body shows skeleton blocks, footer actions disabled. The panel never opens empty-then-jumps. | `muted` (skeleton) |
|
|
36
36
|
| error / empty | Save failure: inline error summary at the top of the body plus per-field errors — the panel stays open, input preserved. A missing record renders an empty state in the body with a close affordance. | `destructive`, `muted-foreground` |
|
|
@@ -49,12 +49,12 @@ The slide-in detail/edit panel: a modal surface anchored to a screen edge with a
|
|
|
49
49
|
- `background` — the panel surface; `border` — the attached-edge hairline and the header/footer dividers.
|
|
50
50
|
- `foreground` / `muted-foreground` — title / description and secondary text.
|
|
51
51
|
- `ring` — the focus trap's visible rings, including the corner close.
|
|
52
|
-
- Scrim: the library
|
|
52
|
+
- Scrim: the library paints `--scrim` (black at 50% in both modes) — as a specification the scrim derives from that token, restated per mode by the package so dark themes don't double-darken; the token-reach report flags a literal scrim.
|
|
53
53
|
- Footer actions consume the button recipe's roles (`primary`/`primary-foreground`, `secondary`, `destructive`); inline errors consume `destructive`; skeletons consume `muted`.
|
|
54
54
|
|
|
55
55
|
## When to use / composition notes
|
|
56
56
|
|
|
57
|
-
The side panel is edit-in-context: opening a record's detail or a scoped form beside a table, board, or list without losing the operator's position — the list stays visible under the scrim and scroll position survives the round trip. It is the wrong container for destructive confirmation (centered dialog), for navigation (sidebar), and for content that deserves its own URL (a full page — if operators share links to it, it's a page). Pick `size` by content: `sm
|
|
57
|
+
The side panel is edit-in-context: opening a record's detail or a scoped form beside a table, board, or list without losing the operator's position — the list stays visible under the scrim and scroll position survives the round trip. It is the wrong container for destructive confirmation (centered dialog), for navigation (sidebar), and for content that deserves its own URL (a full page — if operators share links to it, it's a page). Pick `size` by content: `xs`/`sm` for quick property edits, `default`/`lg` for record detail, `lg` or a width class for side-by-side comparison; nested panels are a smell — a second level of depth means the flow wants a page. Footer actions: one primary commit, right-aligned; cancel is quiet, never a second primary.
|
|
58
58
|
|
|
59
|
-
- **`primitives: nurix`** — `import { Sheet
|
|
59
|
+
- **`primitives: nurix`** — `import { Sheet } from "@nurix/components/block/sheet";` — parts hang off the namespace: `Sheet.Trigger`, `Sheet.Popup`, `Sheet.Header`, `Sheet.Title`, `Sheet.Description`, `Sheet.Body`, `Sheet.Footer`, `Sheet.Close`.
|
|
60
60
|
- **`primitives: native`** — this recipe is the build spec: a portal-rendered fixed aside on the chosen edge with the size ramp above, dialog semantics (`role="dialog"`, `aria-modal`, labeled by the title), a real focus trap with focus restore, Escape + overlay close, a fixed-header / scrollable-body / pinned-footer column, and an interruptible slide within the panel motion budget — implemented with the incumbent stack's own dialog/drawer primitive.
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: sidebar
|
|
3
3
|
class: d2
|
|
4
|
-
sourced: 2026-07
|
|
5
|
-
source: "@nurix/components Sidebar + opinionated layer (Brand, Switcher, ItemGroup, UserMenu,
|
|
4
|
+
sourced: 2026-10-07
|
|
5
|
+
source: "@nurix/components Sidebar + opinionated layer (Brand, Switcher, ItemGroup, UserMenu, CommandTrigger) and the sidebar-callout-card composition — ~/dev/components"
|
|
6
6
|
license: open
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Chrome recipe — sidebar
|
|
10
10
|
|
|
11
|
-
The complete app-nav sidebar: brand block, searchable sections, badged items, promo slot, user footer, icon-collapse rail. One import path (`@nurix/components/sidebar`) carries both the unstyled shell primitives and the opinionated content layer.
|
|
11
|
+
The complete app-nav sidebar: brand block, searchable sections, badged items, promo slot, user footer, icon-collapse rail. One import path (`@nurix/components/block/sidebar`) carries both the unstyled shell primitives and the opinionated content layer, every part a member of the `Sidebar` namespace.
|
|
12
12
|
|
|
13
13
|
## Anatomy
|
|
14
14
|
|
|
@@ -16,24 +16,24 @@ The complete app-nav sidebar: brand block, searchable sections, badged items, pr
|
|
|
16
16
|
|
|
17
17
|
| Slot | Library part | Spec |
|
|
18
18
|
| --- | --- | --- |
|
|
19
|
-
| Provider | `
|
|
20
|
-
| Frame | `Sidebar` | `collapsible: "
|
|
21
|
-
| Header | `
|
|
22
|
-
| Content | `
|
|
23
|
-
| Footer | `
|
|
24
|
-
| Rail | `
|
|
25
|
-
| Inset | `
|
|
26
|
-
| Trigger | `
|
|
19
|
+
| Provider | `Sidebar.Provider` | Owns open/collapsed state (`open` / `defaultOpen` / `onOpenChange`, plus the mobile drawer's `openMobile` trio) and registers the global toggle shortcut (`keyboardShortcut`, default `b`). It stores nothing: persistence is the caller's — read the store into `defaultOpen`, write it from `onOpenChange`. |
|
|
20
|
+
| Frame | `Sidebar` | `collapsible: "offcanvas" (default) \| "icon" \| "none"`, `side: "start" \| "end"`. Widths: 16rem expanded, a 3rem icon rail at the regular density (the item height plus its inset, so it follows `density`), 18rem as a mobile drawer below the `md` breakpoint (mobile renders as an overlay sheet, not an inline column). |
|
|
21
|
+
| Header | `Sidebar.Header` | Pinned top region — hosts the brand block or switcher, and the search trigger. |
|
|
22
|
+
| Content | `Sidebar.Content` | The scrollable middle — one or more item groups. |
|
|
23
|
+
| Footer | `Sidebar.Footer` | Pinned bottom region — promo slot and/or user footer. |
|
|
24
|
+
| Rail | `Sidebar.Rail` | Thin edge hit-area that toggles collapse on click. |
|
|
25
|
+
| Inset | `Sidebar.Inset` | The main-content wrapper beside the sidebar. |
|
|
26
|
+
| Trigger | `Sidebar.Trigger` | Icon button (lives in the topbar) toggling the same state. |
|
|
27
27
|
|
|
28
28
|
**Content layer** (each grounded in the shipped props):
|
|
29
29
|
|
|
30
|
-
- **Brand block** — `Brand { logo, name, caption?, href? }`: a 32px logo tile filled with `sidebar-primary` / `sidebar-primary-foreground`, name (medium weight) over an optional caption. Collapses to logo-only in icon mode.
|
|
31
|
-
- **Switcher** — `Switcher {
|
|
32
|
-
- **Search trigger** — `
|
|
33
|
-
- **Sections** — `ItemGroup { label?,
|
|
34
|
-
- **Item** — `{ id, label, icon?, badge?, state?, emoji?, avatar?, sublabel?, trailing?, isActive?, href?, onClick?, children? }`: one leading decoration (icon, emoji, or avatar), the label, and a trailing badge/state pill rendered outside the button (`
|
|
35
|
-
- **Promo slot** — `
|
|
36
|
-
- **User footer** — `UserMenu { user: { name, email, avatar? }, menuItems
|
|
30
|
+
- **Brand block** — `Sidebar.Brand { logo, name, caption?, href? }`: a 32px logo tile filled with `sidebar-primary` / `sidebar-primary-foreground`, name (medium weight) over an optional caption. Collapses to logo-only in icon mode.
|
|
31
|
+
- **Switcher** — `Sidebar.Switcher { variant: "simple" | "rich", side: "bottom" | "inline-end", options, value / defaultValue, onValueChange(id, details), addAction?, menuLabel? }`: header dropdown for team/workspace/version. Options carry `{ id, label, sublabel?, icon?, shortcut? }`; an optional add-action row sits under a separator.
|
|
32
|
+
- **Search trigger** — `Sidebar.CommandTrigger { children, shortcut?, collapsedTooltip? }`: a ⌘K search chip, its text as children, pinned to the sidebar item height (32px at the regular density); in icon mode it swaps to a matched-height icon button with a tooltip, so the rail never shifts on collapse.
|
|
33
|
+
- **Sections** — `Sidebar.ItemGroup { label?, variant: "list" | "tree" | "rich", isCollapsible?, open / defaultOpen / onOpenChange, onShowMore?, items, hasCollapsibleItems?, defaultExpandedIds?, collapsedLabel: "hide" | "divider", labels?, onItemClick? }`. `list` = flat rows with optional inline collapsible submenu; `tree` = recursive chevron tree (a file tree is this variant with folder and file icons); `rich` = two-line rows (label + sublabel + trailing hint). `collapsedLabel: "divider"` keeps a hairline footprint in icon mode so groups stay separated without rail shift. Group spacing follows the `density` trait; there is no per-group compact switch.
|
|
34
|
+
- **Item** — `{ id, label, icon?, badge?, state?, emoji?, avatar?, sublabel?, trailing?, isActive?, disabled?, href?, onClick?, children? }`: one leading decoration (icon, emoji, or avatar), the label, and a trailing badge/state pill rendered outside the button (`Sidebar.MenuBadge`). `children` makes it a branch.
|
|
35
|
+
- **Promo slot** — the `sidebar-callout-card` composition (no library member): a `Sidebar.Group` in the footer holding a bordered card with title, description, optional single `Input` + full-width `Button`. Hidden entirely in icon mode (`group-data-[collapsible=icon]:hidden`).
|
|
36
|
+
- **User footer** — `Sidebar.UserMenu { user: { name, email, avatar? }, menuItems }`: avatar (initials fallback) + name/email + up-down chevron; opens a dropdown (beside the rail on desktop, below on mobile) with an identity header and the caller's action items (`{ id, label, icon?, shortcut?, onSelect?, intent?: "neutral" | "danger" }`) — `menuItems` is required; nothing is defaulted.
|
|
37
37
|
|
|
38
38
|
## The eight states
|
|
39
39
|
|
|
@@ -42,17 +42,17 @@ The complete app-nav sidebar: brand block, searchable sections, badged items, pr
|
|
|
42
42
|
| default | Rows at rest: transparent fill, quiet text, 16px icons. | `sidebar` (surface), `sidebar-foreground` |
|
|
43
43
|
| hover | Row fills; text sharpens. Group labels with collapse affordance show a pointer + chevron. | `sidebar-accent`, `sidebar-accent-foreground` |
|
|
44
44
|
| focus | Focus-visible ring on the focused row/control; same fill as hover. Never suppressed. | `sidebar-ring` |
|
|
45
|
-
| active | The current destination: persistent fill + medium weight, exposed via `data-active` / `aria-current`. Open branch parents show a rotated chevron; an open dropdown trigger holds the accent fill (`data-
|
|
45
|
+
| active | The current destination: persistent fill + medium weight, exposed via `data-active` / `aria-current`. Open branch parents show a rotated chevron; an open dropdown trigger holds the accent fill (`data-popup-open`). | `sidebar-accent`, `sidebar-accent-foreground` |
|
|
46
46
|
| disabled | ~50% opacity, no pointer events, removed from tab order; label still legible. | `sidebar-foreground` at reduced alpha |
|
|
47
|
-
| loading | `
|
|
47
|
+
| loading | `Sidebar.MenuSkeleton` rows — icon square + text bar shimmer — while nav data resolves; header and footer regions render immediately so the frame never jumps. | `muted` (skeleton), `sidebar-border` |
|
|
48
48
|
| error / empty | A group with nothing to show renders a one-line hint in muted text or is omitted; the sidebar itself carries no error surface — failures belong to content. Attention counts ride item badges. | `muted-foreground`; `destructive` on alert badges |
|
|
49
49
|
| success | No persistent success state; confirmation is expressed as badge-count change or a status-role pill on the relevant item. | status roles via badge |
|
|
50
50
|
|
|
51
51
|
## Keyboard behavior
|
|
52
52
|
|
|
53
|
-
- **Cmd/Ctrl+B** toggles collapse globally (registered by `
|
|
53
|
+
- **Cmd/Ctrl+B** toggles collapse globally (registered by `Sidebar.Provider`; `keyboardShortcut` rebinds it, `null` unbinds); `Sidebar.Trigger` and `Sidebar.Rail` are the pointer equivalents.
|
|
54
54
|
- **Tab order**: header (brand/switcher → search trigger) → groups top-to-bottom, items in visual order → footer user menu. Items are real links or buttons — **Enter** activates, **Space** activates buttons.
|
|
55
|
-
- **Branches**: Enter/Space on a collapsible parent toggles it; the chevron rotation mirrors
|
|
55
|
+
- **Branches**: Enter/Space on a collapsible parent toggles it; the chevron rotation mirrors its open state.
|
|
56
56
|
- **Dropdowns** (Switcher, UserMenu): Enter/Space opens; arrow keys move through options; Enter selects; **Escape** closes and returns focus to the trigger.
|
|
57
57
|
- **cmdk path**: the search chip opens the command palette (⌘K); deep or infrequent destinations live in the palette — the sidebar carries the trigger, not a second search UI.
|
|
58
58
|
- **Icon mode**: focus order is unchanged; each item surfaces its label as a tooltip on focus/hover so keyboard users are never navigating unlabeled glyphs.
|
|
@@ -68,11 +68,11 @@ The sidebar consumes the eight dedicated sidebar roles — the active package's
|
|
|
68
68
|
- `sidebar-border` — hairlines: the panel edge, icon-mode group dividers.
|
|
69
69
|
- `sidebar-ring` — focus rings.
|
|
70
70
|
|
|
71
|
-
Secondary reach: the
|
|
71
|
+
Secondary reach: the footer callout card draws a `sidebar-border` frame with `muted-foreground` copy; dropdown menus render on `popover` / `popover-foreground`; danger menu items (`intent: "danger"`) use `destructive`; skeletons use `muted`. No color literal appears anywhere in the recipe — badge and state-pill colors resolve to status roles.
|
|
72
72
|
|
|
73
73
|
## When to use / composition notes
|
|
74
74
|
|
|
75
|
-
The sidebar is the primary navigation for any operator shell with more than a handful of destinations; icon-collapse earns its place on dense screens where horizontal space is the scarce resource, and the
|
|
75
|
+
The sidebar is the primary navigation for any operator shell with more than a handful of destinations; icon-collapse earns its place on dense screens where horizontal space is the scarce resource, and persisting the open state (read into `defaultOpen`, written from `onOpenChange`) lets the user's choice survive reloads. Canonical composition: `Sidebar.Brand` **or** `Sidebar.Switcher` in the header (both only when the product truly has workspace switching), the search trigger under it, one to three `Sidebar.ItemGroup`s in the content (label them once there are two), and the footer stacking the callout card (optional, promotional, dismissible by omission) above `Sidebar.UserMenu`. Icons on every item are a positive obligation — a bare, icon-less nav is a known judge complaint. Density: the compact `density` trait on data-heavy operator tools, regular elsewhere.
|
|
76
76
|
|
|
77
|
-
- **`primitives: nurix`** — `import {
|
|
77
|
+
- **`primitives: nurix`** — `import { Sidebar } from "@nurix/components/block/sidebar";` — every part hangs off the namespace: `Sidebar.Provider`, `Sidebar.Header`, `Sidebar.Content`, `Sidebar.Footer`, `Sidebar.Rail`, `Sidebar.Inset`, `Sidebar.Trigger`, `Sidebar.Brand`, `Sidebar.Switcher`, `Sidebar.ItemGroup`, `Sidebar.UserMenu`, `Sidebar.CommandTrigger`; the callout card composes `Sidebar.Group` with `Input` (`@nurix/components/element/input`) and `Button` (`@nurix/components/element/button`).
|
|
78
78
|
- **`primitives: native`** — this recipe is the build spec: a fixed aside (256px expanded / 48px icon rail) with pinned header/footer and an independently scrolling middle, collapse state persisted (cookie or localStorage), a global Cmd/Ctrl+B toggle, an overlay drawer below 768px, tooltips on rail items, and the state/keyboard/token contracts above implemented with the incumbent stack's own nav primitives.
|
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: stat-cards
|
|
3
3
|
class: d2
|
|
4
|
-
sourced: 2026-07
|
|
5
|
-
source: "@nurix/components
|
|
4
|
+
sourced: 2026-10-07
|
|
5
|
+
source: "@nurix/components — composed pattern: Card + Stat (+ AnimatedNumber, Chip trend; visuals Spark / Progress / Chart) — ~/dev/components"
|
|
6
6
|
license: open
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Chrome recipe — stat cards
|
|
10
10
|
|
|
11
|
-
The stat-card family: one
|
|
11
|
+
The stat-card family: one composition grammar for every KPI tile, gauge card, sparkline card, legend card, and list card. In `@nurix/components` this is a **documented composition, not a single export** — a `Card` frame whose regions hold a `Stat` (or an `AnimatedNumber`), a trend `Chip`, a visual, and a footer. The card draws no chart geometry itself — the visual slot hosts a chart element. The dashboard-tile obligation applies to every instance: **number + comparison + trend + state + door**.
|
|
12
12
|
|
|
13
13
|
## Anatomy
|
|
14
14
|
|
|
15
15
|
| Slot | Library part | Spec |
|
|
16
16
|
| --- | --- | --- |
|
|
17
|
-
| Root | `
|
|
18
|
-
| Section |
|
|
19
|
-
| Header | `
|
|
20
|
-
| Metric row | `
|
|
21
|
-
| Visual | `
|
|
22
|
-
| Content | `
|
|
23
|
-
| Footer | `
|
|
24
|
-
| Hover bridge |
|
|
17
|
+
| Root | `Card` | The surface: ring edge, scaled radius, insets from the `--density-pad-*` tokens. No variant, size or accent props — depth rides `className` as `shadow-(--elevation-N)` (or `shadow-none`), and an accent stripe is a 4px inline-start border on a `--intent-*` or `primary` role, set through `className`. |
|
|
18
|
+
| Section | flex column | Vertical region group for top/bottom cascades (`justify-between` pins one to each end). |
|
|
19
|
+
| Header | `Card.Header` → `Card.Title` (`render={<h3 />}` picks the level) + `Card.Description` + `Card.Header.Action` | Icon/title block at the start, trailing actions (menu, info, tabs, date range) pinned to the header's end column. The icon is a child glyph tinted from a role. |
|
|
20
|
+
| Metric row | `Stat { label, value, sub?, intent? }` or `AnimatedNumber { value, format?, trend?, fallback? }` + trend `Chip { intent }` | Baseline-aligned: headline value (a pre-formatted `Stat` string in the display face with tabular numerals, or a digit-rolling `AnimatedNumber` that paints formatted static text first — SSR-safe), unit ("/ 5,000") formatted into the string, signed trend chip, comparison line as `Stat`'s `sub`. |
|
|
21
|
+
| Visual | `Card.Content` slot | Generic centered slot hosting `<Spark>` (an axis-less trend: `family` line, area, bar or pie), `<Progress>` (one current value: bar or `variant="circular"` ring), or `<Chart>` once a gauge, legend, axes or a tooltip enter (`family: "gauge"`, `"ring"`, `"progress"` and the series families). |
|
|
22
|
+
| Content | `Card.Content` | Escape hatch for list / table / paragraph cards. |
|
|
23
|
+
| Footer | `Card.Footer` → `Timestamp` caption + `Link` | Freshness caption at the start ("Updated 2m ago" — the tile's *state*, staleness from `useFreshness`), link at the end (the tile's *door*). A whole-tile door is `Stat`'s `href` / `onClick`. |
|
|
24
|
+
| Hover bridge | caller state | No library hook: where the embedded `Chart` reports hover (`onHoverChange`, emitted by some renderers only), hold the hovered point in state and show it in the value/description with a resting fallback. `Spark` is decorative and reports nothing. |
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
The parts emit `data-slot="card-…"` and `data-slot="stat-…"` for styling and testing hooks; `Stat` and `Chip` add `data-intent`.
|
|
27
27
|
|
|
28
28
|
## The eight states
|
|
29
29
|
|
|
30
30
|
| State | Spec | Token roles |
|
|
31
31
|
| --- | --- | --- |
|
|
32
|
-
| default |
|
|
33
|
-
| hover |
|
|
32
|
+
| default | The card on the card surface with its hairline ring edge (`--edge-hairline`); value at display scale with tabular numerals; title strong, description muted. | `card` + `card-foreground`, `border`, `foreground`, `muted-foreground` |
|
|
33
|
+
| hover | A clickable card raises one rung on the package's elevation ramp (`hover:shadow-(--elevation-3)`); a clickable `Stat` takes its hover wash; with the hover bridge wired, hovering the embedded chart live-rewrites the value and description to the hovered point, reverting on leave. | elevation system; value stays `foreground` |
|
|
34
34
|
| focus | The door (footer link or whole-card link) and any header action are focus stops with visible rings. | `ring` |
|
|
35
35
|
| active | A clickable card shows a pressed treatment; the accent stripe marks category or alert affiliation. | `accent` stripe via `primary`/status roles |
|
|
36
36
|
| disabled | No permission for the metric: value masked (—), lock caption, non-interactive, ~50% presence — the tile keeps its footprint. | reduced-alpha, `muted-foreground` |
|
|
37
37
|
| loading | Skeleton title bar + value block + visual area at the card's fixed height — the grid never reflows when data lands. | `muted` |
|
|
38
38
|
| error / empty | Degraded: em-dash value + status-role caption ("stale as of 09:12") — never a fabricated number. Empty series: the visual slot shows a quiet hint, never a zeroed fake chart. | `warning` / `destructive` caption, `muted-foreground` |
|
|
39
|
-
| success | Trend intent is the success surface: up = good on the success role, down = bad on `destructive`;
|
|
39
|
+
| success | Trend intent is the success surface: up = good on the success role, down = bad on `destructive`; the mapping inverts for lower-is-better metrics (error rate, latency, churn); zero/no-direction = neutral muted. The caller maps the sign to the trend `Chip`'s `intent` (`success`, `danger`, `neutral`), exposed as `data-intent`. | `success` / `destructive` status roles |
|
|
40
40
|
|
|
41
41
|
## Keyboard behavior
|
|
42
42
|
|
|
@@ -48,15 +48,15 @@ Every part emits `data-slot="statistics-…"` for styling and testing hooks.
|
|
|
48
48
|
|
|
49
49
|
## Token reach
|
|
50
50
|
|
|
51
|
-
- `card` / `card-foreground` — surface and text; `border` —
|
|
52
|
-
- Accent stripe and
|
|
53
|
-
- `
|
|
54
|
-
- Embedded visuals — `chart-1..5` for series color, chart chrome roles (`chart-grid`, `chart-tooltip-background/foreground`) for scaffolding; a spark/gauge/progress fill is never a hex literal.
|
|
55
|
-
- The headline value
|
|
51
|
+
- `card` / `card-foreground` — surface and text; `border` — the card's hairline ring edge; `muted` / `muted-foreground` — description, captions, skeletons.
|
|
52
|
+
- Accent stripe and the header icon tint — `primary` plus the status roles (`success`, `warning`, `destructive`/danger, `info`) at soft/solid/outline strengths.
|
|
53
|
+
- Trend chip and `Stat` value — **status roles only**: both paint from the `--intent-*` tokens through `intent`, which a package's `success` / `warning` / `info` roles feed; the token-reach report flags a literal trend colour.
|
|
54
|
+
- Embedded visuals — `chart-1..5` for series color, chart chrome roles (`chart-grid`, `chart-tooltip-background/foreground`) for scaffolding; a spark/gauge/progress fill is never a hex literal. `Spark` paints in `currentColor` by default and takes `color="var(--chart-1)"` for a series role.
|
|
55
|
+
- The headline value sits in the display face (`--font-display`) with tabular numerals; a hero size comes from the type ramp through `className`, and mono/tabular figures come from the package's type roles, never a hardcoded family.
|
|
56
56
|
|
|
57
57
|
## When to use / composition notes
|
|
58
58
|
|
|
59
|
-
Stat cards are the dashboard's KPI strip (4–6 tiles full-width per the blueprint shapes) and the record header's vital signs; the same grammar scales from a bare number-plus-trend to a gauge card with a legend, so one card system covers the whole surface. Every tile carries all five obligations — a number without a comparison is decoration, a tile without a door is a dead end.
|
|
59
|
+
Stat cards are the dashboard's KPI strip (4–6 tiles full-width per the blueprint shapes) and the record header's vital signs; the same grammar scales from a bare number-plus-trend to a gauge card with a legend, so one card system covers the whole surface. Every tile carries all five obligations — a number without a comparison is decoration, a tile without a door is a dead end. Build hierarchy from the type ramp (one hero metric a ramp step up, support tiles at the base) instead of ad-hoc font sizes; use `justify-between` with two sections for cards whose visual anchors the bottom edge. Wire the hover bridge whenever a hover-reporting chart lives in the card — it is the cheapest "designed, not templated" signal in the set. A bare row of KPIs with no card frame is `Stat`s on a `Row`.
|
|
60
60
|
|
|
61
|
-
- **`primitives: nurix`** — `import {
|
|
61
|
+
- **`primitives: nurix`** — composed, e.g.: `import { Card } from "@nurix/components/block/card"; import { Stat } from "@nurix/components/element/stat"; import { Chip } from "@nurix/components/element/chip"; import { Timestamp } from "@nurix/components/element/timestamp"; import { Link } from "@nurix/components/element/link";` (visuals: `Spark` from `@nurix/components/element/spark`, `Progress` from `@nurix/components/element/progress`, `Chart` from `@nurix/components/block/chart`; the animated number: `AnimatedNumber` from `@nurix/components/element/animated-number`; staleness: `useFreshness` from `@nurix/components/hooks/use-freshness`). `element/spark`, `block/chart` and `element/animated-number` reach optional peer families (`spark`, `charts`, `animated-number`) the consumer installs.
|
|
62
62
|
- **`primitives: native`** — this recipe is the build spec: a fixed-height card with header / baseline-aligned metric row / visual slot / footer regions, a display-scale tabular headline that is correct on first paint (animation optional and additive), a sign-driven trend badge with an invert switch on status roles, skeletons that hold the footprint, degraded states that never fabricate numbers, and all series color drawn from the token artifact's `chart-1..5`.
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: topbar
|
|
3
3
|
class: d2
|
|
4
|
-
sourced: 2026-07
|
|
5
|
-
source: "@nurix/components — composed pattern:
|
|
4
|
+
sourced: 2026-10-07
|
|
5
|
+
source: "@nurix/components — composed pattern: Sidebar.Trigger + Separator + Breadcrumb + Command.Trigger + Button (the library's site-header pattern; no single Topbar export) — ~/dev/components"
|
|
6
6
|
license: open
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Chrome recipe — topbar
|
|
10
10
|
|
|
11
|
-
The app-shell top bar: the sticky row above the content pane carrying the sidebar toggle, the breadcrumb trail, global search, and screen-level actions. In `@nurix/components` this is a **documented composition, not a single export** — every shipped app-shell demo assembles it from `
|
|
11
|
+
The app-shell top bar: the sticky row above the content pane carrying the sidebar toggle, the breadcrumb trail, global search, and screen-level actions. In `@nurix/components` this is a **documented composition, not a single export** — every shipped app-shell demo assembles it from `Sidebar.Trigger`, `Separator`, `Breadcrumb`, `Command.Trigger`, and `Button`. Page-level headers below it are separate compositions (the library's masthead and record-card header recipes over `Block`, `Row`, `Breadcrumb`, `Stat` and `FieldGrid`) and are not part of this recipe.
|
|
12
12
|
|
|
13
13
|
## Anatomy
|
|
14
14
|
|
|
15
15
|
| Slot | Library part | Spec |
|
|
16
16
|
| --- | --- | --- |
|
|
17
17
|
| Bar | flex row | Sticky (`top-0`), 48–56px tall, full width of the content pane, hairline bottom border. Optional glass treatment: `background` at ~95% alpha + backdrop blur. |
|
|
18
|
-
| Sidebar toggle | `
|
|
18
|
+
| Sidebar toggle | `Sidebar.Trigger` | Leading ghost icon-only button toggling the sidebar; reflects open state via `aria-expanded` (the sidebar frame carries `data-state="expanded" \| "collapsed"`). |
|
|
19
19
|
| Divider | `Separator orientation="vertical"` | 16px vertical hairline between toggle and breadcrumbs. |
|
|
20
|
-
| Breadcrumbs | `Breadcrumb` → `
|
|
20
|
+
| Breadcrumbs | `Breadcrumb` → `Breadcrumb.Item` | The location trail. Ancestors are links (`href`, or a router link through `render`); the last `Breadcrumb.Item` is the current page — plain text, `aria-current="page"` and `data-active`, never a link. Deep trails fold the middle segments behind an ellipsis menu (`maxItems`). |
|
|
21
21
|
| Spring | flex spacer | Pushes the trailing cluster right. |
|
|
22
|
-
| Search slot | `
|
|
23
|
-
| Actions | `Button
|
|
22
|
+
| Search slot | `Command.Trigger { children, shortcut? }` inside `Command.Dialog` | A bordered search chip with its text as children (default "Search") and a right-aligned `⌘K` / `Ctrl+K` kbd hint; opens the palette's `Command.Popup`. |
|
|
23
|
+
| Actions | `Button variant="ghost" isIconOnly`, overflow `DropdownMenu` | At most 3 visible screen-level actions; further actions collapse into an overflow menu. Theme toggle sits here. |
|
|
24
24
|
| Identity | `Avatar` (optional) | Only when the shell has no sidebar user footer — one identity home per shell, never two. |
|
|
25
25
|
|
|
26
26
|
## The eight states
|
|
@@ -32,9 +32,9 @@ State is mostly per-control; the bar itself is stable chrome.
|
|
|
32
32
|
| default | Bar on the content canvas with a hairline base; breadcrumb ancestors quiet, current page at full strength. | `background`, `border`, `muted-foreground` (ancestors), `foreground` (current) |
|
|
33
33
|
| hover | Breadcrumb links sharpen to full foreground; icon buttons gain an accent fill. | `foreground`, `accent` + `accent-foreground` |
|
|
34
34
|
| focus | Focus-visible ring on every control — toggle, links, chip, buttons. | `ring` |
|
|
35
|
-
| active | Sidebar toggle mirrors the open sidebar (`
|
|
35
|
+
| active | Sidebar toggle mirrors the open sidebar (`aria-expanded`); the current crumb is the persistent active marker; a pressed action holds its pressed fill. | `foreground`, `accent` |
|
|
36
36
|
| disabled | Actions at ~50% opacity, no pointer events; breadcrumbs are never disabled. | reduced-alpha foreground |
|
|
37
|
-
| loading | An in-flight screen action shows
|
|
37
|
+
| loading | An in-flight screen action shows its spinner inside its own button (`Button loading`). The bar never skeletons: breadcrumbs derive from the route and render immediately. | `muted-foreground` (spinner) |
|
|
38
38
|
| error / empty | An empty trail renders the page title as the single `BreadcrumbPage`. Errors surface as toasts or content-level states — the bar itself hosts none; a degraded-environment banner may attach *beneath* the bar on a status-role tint. | `warning` / `destructive` (banner only) |
|
|
39
39
|
| success | Transient: an action button briefly swaps to a check, or a toast confirms; nothing persistent lives in the bar. | `success` status role (transient) |
|
|
40
40
|
|
|
@@ -42,7 +42,7 @@ State is mostly per-control; the bar itself is stable chrome.
|
|
|
42
42
|
|
|
43
43
|
- **Tab order**: the bar follows the skip link and precedes the content pane — toggle → breadcrumb links → search chip → actions → identity. Plain sequential tabbing; no roving focus is needed at this control count.
|
|
44
44
|
- **Enter** activates crumbs and buttons; **Space** activates buttons.
|
|
45
|
-
- **Cmd/Ctrl+B** toggles the sidebar from anywhere (
|
|
45
|
+
- **Cmd/Ctrl+B** toggles the sidebar from anywhere (owned by `Sidebar.Provider`, rebound through `keyboardShortcut`); the `Sidebar.Trigger` is its visible affordance.
|
|
46
46
|
- **cmdk path**: **⌘K / Ctrl+K** opens the command palette from anywhere; the search chip is the visible, clickable affordance and displays the shortcut as a kbd hint. Inside the palette: type to filter, arrows to move, Enter to run, Escape to close. Global navigation and infrequent actions belong in the palette, not as more topbar buttons.
|
|
47
47
|
- Overflow menu: Enter/Space opens, arrows move, Escape closes and returns focus to the trigger.
|
|
48
48
|
|
|
@@ -59,7 +59,7 @@ State is mostly per-control; the bar itself is stable chrome.
|
|
|
59
59
|
|
|
60
60
|
## When to use / composition notes
|
|
61
61
|
|
|
62
|
-
Every screen inside the shell gets the topbar; it is the constant that makes the sidebar collapsible (the toggle must survive collapse) and the command palette discoverable. Keep it thin — the topbar locates and searches; identity-weight content (record title, status, metadata, stats) belongs to the page
|
|
62
|
+
Every screen inside the shell gets the topbar; it is the constant that makes the sidebar collapsible (the toggle must survive collapse) and the command palette discoverable. Keep it thin — the topbar locates and searches; identity-weight content (record title, status, metadata, stats) belongs to the page header below it (the masthead or record-card recipe under `nurix`), and datasets' own toolbars belong to their tables. The three-zone rhythm — locate left, search center-right, act right — is the invariant; what varies per product is only which trailing actions earn a permanent slot.
|
|
63
63
|
|
|
64
|
-
- **`primitives: nurix`** — composed, e.g.: `import {
|
|
64
|
+
- **`primitives: nurix`** — composed, e.g.: `import { Sidebar } from "@nurix/components/block/sidebar"; import { Breadcrumb } from "@nurix/components/compound/breadcrumb"; import { Command } from "@nurix/components/compound/command"; import { Separator } from "@nurix/components/element/separator"; import { Button } from "@nurix/components/element/button"; import { DropdownMenu } from "@nurix/components/compound/dropdown-menu";` — parts hang off each namespace (`Sidebar.Trigger`, `Breadcrumb.Item`, `Command.Dialog` / `Command.Trigger` / `Command.Popup`, `DropdownMenu.Trigger` / `DropdownMenu.Popup`).
|
|
65
65
|
- **`primitives: native`** — this recipe is the build spec: a sticky 48–56px flex row with the slot order above, a real `<nav aria-label="Breadcrumb">` trail with `aria-current="page"` on the last segment, a search affordance wired to the stack's palette (or a plain search route) showing its shortcut, and the state/keyboard/token contracts implemented with the incumbent stack's own primitives.
|
|
@@ -6,13 +6,13 @@ source: "distilled from the A5 iconography phase-1 research sweep — Lucide, Ta
|
|
|
6
6
|
license: mixed — nine of the eleven conventions below are open (OSS icon sets under ISC/MIT/Apache-2.0); two are licensed (Linear, Stripe) because they describe a proprietary product's house convention rather than a redistributable asset — see the `license` line inside each convention's own values block
|
|
7
7
|
feel: "the register this file spans: quiet neutral utility (Lucide, Tabler) through dense-precision (Radix), paired-weight (Heroicons), rounded-friendly and illustrative (Phosphor), mechanically variable (Material Symbols), rectilinear enterprise (Carbon), hand-tuned developer precision (Octicons), to near-monochrome restraint (Linear, Stripe) — pick the register the product needs, not just a glyph shape"
|
|
8
8
|
---
|
|
9
|
-
|
|
9
|
+
Eleven curated iconography conventions, each a coherent bundle of icon set, stroke/fill treatment, size grid, default color role, and type-pairing rule — real products never choose these independently. A bare, icon-less nav reads as unfinished, so each convention states where icons are obligatory (nav, status glyphs, empty states) versus optional (body copy, pure decoration).
|
|
10
10
|
|
|
11
11
|
## Convention: lucide-quiet-utility
|
|
12
12
|
|
|
13
13
|
**Feel:** "quiet, neutral utility line-work — reads as structural chrome, not decoration"
|
|
14
14
|
|
|
15
|
-
Lucide (ISC-licensed fork of Feather) is the default pick for stroke-based quiet utility icons in SaaS dashboards. Obligatory in nav rails, sidebar items, and empty-state graphics
|
|
15
|
+
Lucide (ISC-licensed fork of Feather) is the default pick for stroke-based quiet utility icons in SaaS dashboards. Obligatory in nav rails, sidebar items, and empty-state graphics; optional inline in body copy or button labels where the text alone already carries the meaning.
|
|
16
16
|
|
|
17
17
|
```yaml values
|
|
18
18
|
set: Lucide (ISC license)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Step 4 — Compose: one mind, whole canvas
|
|
2
2
|
|
|
3
|
-
Build the app — all surfaces in one continuous context, the chosen package's design.md in view throughout.
|
|
3
|
+
Build the app — all surfaces in one continuous context, the chosen package's design.md in view throughout. **Never** decompose this step into per-surface handoffs with frozen intermediate artifacts; the composing mind holds the whole canvas.
|
|
4
4
|
|
|
5
5
|
## Chrome
|
|
6
6
|
|
|
@@ -9,7 +9,7 @@ Per the primitives switch recorded in the project's `design.md`:
|
|
|
9
9
|
- **`nurix`** — compose chrome from `@nurix/components`. The chrome recipes (`data/chrome/` — read only the recipes for surfaces in the structure doc) carry composition, states, and when-to-use; the component dictionary (`components/`) carries props, imports, and usage across both provenances — consult the index for what exists, and open the touched category's API reference on need.
|
|
10
10
|
- **`native`** — the recipes are *specifications*: anatomy, the eight states, keyboard behavior, token reach — implement them with the incumbent stack's own components. The index's shadcn/fallback columns carry the composition; category API references never load.
|
|
11
11
|
|
|
12
|
-
Recipes are starting points, not jails: restyle or extend them in place when the package demands it.
|
|
12
|
+
Recipes, generated files, and library compositions are starting points, not jails: restyle or extend them in place when the package demands it.
|
|
13
13
|
|
|
14
14
|
## Composition rules
|
|
15
15
|
|
|
@@ -18,24 +18,22 @@ Recipes are starting points, not jails: restyle or extend them in place when the
|
|
|
18
18
|
- **Positive obligations apply** — load the surface's archetype checklist (`data/checklists/<archetype>.md`); the checklists are ceilings: a sidebar ships with brand block, icons, and user context; a dashboard tile ships with number + comparison + trend + state + door; every surface ships its state matrix.
|
|
19
19
|
- **The register scope holds**: the display face never touches cells, buttons, labels, or form chrome; data numerals run the mono face with `tabular-nums`.
|
|
20
20
|
- **Motion follows the package's §7** and the frequency law: actions performed 100+ times a day, and any keyboard-initiated action, get no animation, ever; panel/drawer motion is interruptible; entrances never `ease-in`.
|
|
21
|
-
- **No ownership jails.** Any file may be edited at any time, including generated ones and library compositions.
|
|
22
|
-
- **No paper-trail bureaucracy.** Decision-log lines only, through `record`; attention goes to pixels.
|
|
23
21
|
- **A finishing pass with authority over everything is part of this step** — walk the whole canvas once at the end: rhythm, alignment, density, identity-consistency across surfaces, the one-accent discipline.
|
|
24
22
|
|
|
25
23
|
## Library reach — under `primitives: nurix`
|
|
26
24
|
|
|
27
25
|
The library carries behavior stock shadcn does not, and each item below is reachable only when the composition routes to it; a hand-rolled equivalent is a defect, not a style choice.
|
|
28
26
|
|
|
29
|
-
- **Density is the trait.** Control height and padding come from `personality.density` through `--density-control-*`; container padding and gaps on scanned surfaces (cards in a grid, list rows, panel bodies) read `--density-pad-xs|md|lg` and `--density-gap-md` — `p-(--density-pad-md)`, `gap-(--density-gap-md)` — so one trait re-spaces the product. A `p-3` on a data surface is the per-surface mood foundations forbid; structural spacing (a divider, an icon's optical nudge) stays literal.
|
|
27
|
+
- **Density is the trait.** Control height and padding come from `personality.density` through `--density-control-*`; container padding and gaps on scanned surfaces (cards in a grid, list rows, panel bodies) read `--density-pad-xs|sm|md|lg` and `--density-gap-sm|md|lg` — `p-(--density-pad-md)`, `gap-(--density-gap-md)` — so one trait re-spaces the product. A `p-3` on a data surface is the per-surface mood foundations forbid; structural spacing (a divider, an icon's optical nudge) stays literal.
|
|
30
28
|
- **Motion rides the seam.** A `transition-*` utility with no `duration-*` class inherits `--motion-duration-fast`, which `pace`, the reduced-motion block, and the inline clamp all reach; a `duration-200` opts that element out of all three. App-owned animation reads `var(--motion-duration-base)` and `var(--motion-ease-standard)`, never a literal; the library's `effect/*` primitives already read them, so an identity moment composes from those before a bespoke variant.
|
|
31
|
-
- **Collections are inline.** Any container of repeated rows the operator scans — a table body, a list, a feed, a board column — carries `data-mode="inline"`: every DOM-descendant transition collapses to instant (portaled tooltips escape) while loaders keep breathing on the ambient duration.
|
|
32
|
-
- **Every time on screen is `<Timestamp>`** (`element/timestamp`):
|
|
33
|
-
- **Typing is not saving.** Input (
|
|
34
|
-
- **Remote and long option lists are the Combobox's** (`compound/combobox`): `
|
|
35
|
-
- **Time-range filters are `DatePicker.Range` with `presets`** (
|
|
36
|
-
- **Uploads reject before they upload.** `
|
|
37
|
-
- **Shell affordances
|
|
38
|
-
- **Status colour is the role.** The package's `success` / `warning` / `info` reach the library as `--
|
|
39
|
-
- **
|
|
40
|
-
- **Clipping is `clip`.** `clip` for one line, `clip-<n>` for n lines, `clip-fade` where prose is expected to overflow —
|
|
41
|
-
- **Type roles are utilities** — `text-display`, `text-title`, `text-h1`, `text-body`, `text-dense`, `text-meta`, … from the library's `@theme` — and `cn()` (tailwind-merge tables) classifies `text-<role>` as a colour: a role and a colour passed through `cn`, together or apart, keep only the later one. Keep both in a `className` string that never goes through `cn`, or put the colour on the parent.
|
|
29
|
+
- **Collections are inline.** Any container of repeated rows the operator scans — a table body, a list, a feed, a board column — carries `data-mode="inline"`: every DOM-descendant transition collapses to instant (portaled tooltips escape) while loaders keep breathing on the ambient duration. `<Cell mode="inline">` stamps it on the cell and `block/list` on each of its rows; `block/table` stamps nothing on its container, so the app stamps it on the collection — a table, and every collection it builds itself.
|
|
30
|
+
- **Every time on screen is `<Timestamp>`** (`element/timestamp`): it reads relative inside `relativeThreshold` and absolute beyond it (`format` takes the absolute label's Intl options), the full absolute stamp sits on the native `title` by default, and `isLive` (on by default) ticks on one shared interval. A custom live label subscribes through `useNow` (`lib/time`) — one ticker per interval, never a timer per row.
|
|
31
|
+
- **Typing is not saving.** Input (Enter, blur), Textarea (blur, Cmd/Ctrl+Enter), NumberInput (blur, Enter, the steppers), Slider (release), Combobox (the pick; close for a multi-select), `DatePicker` and `DatePicker.Range` (the pick) carry `onValueChange(value, details)` for the draft and `onValueCommitted` for the commit, once per settled change; `InlineEdit` carries `onSave` alone. Mutations wire to `onValueCommitted`; `onValueChange` never reaches the server. A form with a Save button is the one exception — a transaction the button commits whole.
|
|
32
|
+
- **Remote and long option lists are the Combobox's** (`compound/combobox`): hold the query with `searchValue` / `onSearchValueChange` (holding it turns the built-in filter off), pass the fetched rows as `options` with `loading`, page with `hasMore`, `loadingMore` and `onLoadMore()`, and cap a `multiple` select with `max`. Fetching and debouncing are the app's — `useSearchSource` (`hooks/use-search-source`) drops stale responses and debounces. A fetch-in-effect feeding a filtered `<Select>` is a defect.
|
|
33
|
+
- **Time-range filters are `DatePicker.Range` with `presets`** (its preset column shows by default, `DatePicker`'s needs `hasPresets`), `weekStartsOn` from the locale decision, `min` / `max` where the domain bounds them; a single date in a table cell is a `DatePicker` with `variant="bare"` (a host such as `Cell` passes it). A non-required picker carries its own Clear; `required` removes it. A bespoke window picker is a defect.
|
|
34
|
+
- **Uploads reject before they upload.** `FilePicker` (`compound/file-picker`) takes `accept`, `maxSize`, `maxFiles`, and `validate` (return the rejection message, or nothing to accept); a rejected file never enters the list and `onFileReject(file, message)` receives it. The dropzone only reads invalid for the dwell hold — the composition surfaces the message (a field error or a toast from `compound/toast`).
|
|
35
|
+
- **Shell affordances are library compositions**: "as of" is `useFreshness({ fetchedAt, onRefresh, staleAfterMs })` (`hooks/use-freshness`) on the app's own `Button` beside a `Timestamp`; an action a gate blocks is an `Empty` (`compound/empty-state`) carrying the icon, the reason and the way out; a system-wide notice is `SystemBanner` (`element/system-banner`). A bespoke freshness timer or banner is a defect.
|
|
36
|
+
- **Status colour is the role.** The package's `success` / `warning` / `info` reach the library as `--intent-success|warning|info`, and `destructive` as `--intent-danger` (library fallbacks when undeclared): status meaning is the `intent` prop (`neutral`, `info`, `success`, `warning`, `danger` — a status dot is `<Chip prefix="dot" intent="success">`, a destructive button `<Button intent="danger">`), never a raw hue and never `color`, which carries a stored data colour, and app utilities map onto the same roles (`--color-success: var(--success)` in the app's `@theme inline`), so a chart legend and a chip dot never disagree.
|
|
37
|
+
- **Library controls carry the 24px pointer floor built in; an app-drawn target under 24px grows through `hit-area-*`** (`hit-area-2` adds 0.5rem of pointer box on every side; `hit-area-x-*` / `-y-*` / `-t-*` / `-b-*` / `-l-*` / `-r-*` where a neighbour would overlap) — the pointer box grows, the paint box does not. `hit-area-debug` shows the box while composing.
|
|
38
|
+
- **Clipping is `clip`.** `clip` for one line, `clip-<n>` for n lines, `clip-fade` where prose is expected to overflow — `clip` and `clip-<n>` carry `min-width: 0`, and `clip-<n>` `overflow-wrap: anywhere`, which Tailwind's `truncate` and `line-clamp-*` lack (the flex-child overflow bug those leave open).
|
|
39
|
+
- **Type roles are utilities** — `text-display`, `text-title`, `text-h1`, `text-body`, `text-dense`, `text-meta`, … from the library's `@theme` — and `cn()` (the `cn` package, on tailwind-merge's tables) classifies `text-<role>` as a colour: a role and a colour passed through `cn`, together or apart, keep only the later one. Keep both in a `className` string that never goes through `cn`, or put the colour on the parent.
|
|
@@ -5,7 +5,7 @@ One source, two derivations: the project's `design.md` is canonical; the artifac
|
|
|
5
5
|
| Artifact | Emit with | Carries | Compiled by |
|
|
6
6
|
| --- | --- | --- | --- |
|
|
7
7
|
| `design.tokens.css` | `node scripts/validate-design-md.mjs --project . --emit --write` | the color roles in both themes, the type ramp, elevation, the motion base, layout | the validator |
|
|
8
|
-
| `personality.css` — `primitives: nurix` only | `node scripts/validate-design-md.mjs --project . --emit-personality --write` | the six traits compiled into the token families every `@nurix/components` component reads: `--density-*`, `--motion-duration
|
|
8
|
+
| `personality.css` — `primitives: nurix` only | `node scripts/validate-design-md.mjs --project . --emit-personality --write` | the six traits compiled into the token families every `@nurix/components` component reads: `--density-*`, `--motion-duration-*` / `--motion-ease-*` / `--hold-dwell`, `--radius-scale` / `--radius-pill` / `--ring-width` / `--ring-offset` / `--edge-hairline` / `--edge-stroke`, `--elevation-*`, `--texture-*`, `--accent` / `--ring` | `compileToCss` from `@nurix/components/lib/personality`, reached through the validator from the project's own install |
|
|
9
9
|
|
|
10
10
|
## Coverage
|
|
11
11
|
|
|
@@ -19,15 +19,15 @@ One source, two derivations: the project's `design.md` is canonical; the artifac
|
|
|
19
19
|
personality:
|
|
20
20
|
density: compact # compact | regular | airy — control height and padding, container padding, gaps
|
|
21
21
|
pace: 0.3 # 0 still · 0.5 the library's neutral timing · 1 lively
|
|
22
|
-
edge: sharp # sharp | regular | soft — the radius scale
|
|
22
|
+
edge: sharp # sharp | regular | soft — the radius scale; sharp also thins ring, hairline and stroke, and squares pills
|
|
23
23
|
depth: flat # flat | layered | elevated — the elevation ladder
|
|
24
24
|
texture: clean # clean | grainy — grain opacity on effect surfaces
|
|
25
|
-
|
|
25
|
+
accent: neutral # neutral | expressive — whether the accent re-anchors on the primary
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
- **§4–§7 are read into the traits, never into pixels.** A §6 of "flat, hairline-separated" is `depth: flat`; a §7 of "utilitarian, nothing animates twice a day" is `pace` at 0.2–0.35; an operator §4 is `density: compact`. Declare only what differs from neutral (regular · 0.5 · regular · layered · grainy · neutral). An absent block ships the library's defaults and the validator warns (`dmw-
|
|
28
|
+
- **§4–§7 are read into the traits, never into pixels.** A §6 of "flat, hairline-separated" is `depth: flat`; a §7 of "utilitarian, nothing animates twice a day" is `pace` at 0.2–0.35; an operator §4 is `density: compact`. Declare only what differs from neutral (regular · 0.5 · regular · layered · grainy · neutral). An absent block ships the library's defaults and the validator warns (`dmw-09`) — a project that never declared its feel is how these families go unused.
|
|
29
29
|
- **`motion.duration` and `motion.easing` are the app's own vocabulary** (`--duration-panel`, `--ease-standard`, read by app-owned animation); the library's components never read them. `pace` is what retimes the library — and, through the library's default transition duration, every plain Tailwind `transition-*` utility in the app. State both from the same §7 reading.
|
|
30
|
-
- **The block is closed-vocabulary and fails closed** (`dmv-12`): an unknown trait, an out-of-range pace, or a block under `primitives: native` (nothing there reads it) fails validation.
|
|
30
|
+
- **The block is closed-vocabulary and fails closed** (`dmv-12`): an unknown trait (a `tone` key among them — the accent axis is `accent`, and the library's compiler rejects `tone`), an out-of-range pace, or a block under `primitives: native` (nothing there reads it) fails validation.
|
|
31
31
|
- **The compiler is the project's own install.** `--emit-personality` resolves `@nurix/components` by walking up from the design.md, then one and two levels into the workspace (`packages/<app>`), because a pnpm root hoists nothing. Several installed versions is an ambiguity the app package resolves: `--project . --emit-personality --write --lib packages/<app>`. The artifact's header names the library version that compiled it, so a library upgrade re-emits by drift, never by hand.
|
|
32
32
|
- **The artifact is committed, never injected.** `personality.css` is the product's artifact — inspectable, diffable, drift-checked at the ship gate — imported after the library's `styles.css` so its `:root` wins on order. `<PersonalityStyle>` from the same subpath serves a runtime switch (a density toggle, a preview) and never replaces the artifact.
|
|
33
33
|
|
|
@@ -38,7 +38,7 @@ Each prevents a known silent-failure class:
|
|
|
38
38
|
- **Specificity armor.** Theme blocks are `:root:not(.dark)` and `:root.dark` — both specificity (0,2,0) — keyed on the `.dark` class the library compiles into its own stylesheet. A `data-theme` attribute keys none of the library's `dark:` variants, its theme toggle, or its toaster: an app on the attribute inverts its tokens and renders mixed library chrome.
|
|
39
39
|
- **Theme-invariant tokens sit in a bare `:root`.** Only the colour roles are themed. Font stacks, the type ramp, radii, shadows, motion and layout emit into a bare `:root` block ahead of the two theme blocks. Behind `:root:not(.dark)` they match nothing once `.dark` is on the root, and every `font-size: var(--text-…)` collapses to an inherited 16px in dark mode while the colours still look right.
|
|
40
40
|
- **The ramp emits Tailwind's composite sub-keys.** `--text-<step>` with `--line-height`, `--font-weight` and `--letter-spacing` sub-keys are exactly what the `text-<step>` utility applies. A `-size`/`-line`/`-weight` suffix form generates junk utilities and leaves `text-<step>` undefined. The `--font-feature-settings` and `--font-family` sub-keys are inert for the utility and are read through `var()`. The dense operator tier's steps are optional: absent steps emit nothing and warn once per package (`dmw-08`).
|
|
41
|
-
- **The
|
|
41
|
+
- **The intent trio.** `success`, `warning`, and `info` reach the library as `--intent-success|warning|info` (and `destructive` as `--intent-danger`) — what a status dot (`<Chip prefix="dot" intent="success">`) and the copy button's check read, falling back to the library's own hue when a role is absent. Declare all three, in both themes — a missing `info` (`dmw-10`) puts the library's blue beside the package's green and amber.
|
|
42
42
|
- **Font binding by derivation.** Font stacks bind to installed packages under **registered family names** (`"Inter Variable"`, not `"Inter"`), each with its declared fallback stack — otherwise the flagship font silently never renders. Install the font packages the design.md declares (e.g. Fontsource) and verify the name matches what the package registers. The library's `font-sans` / `font-mono` / `font-display` utilities compile to `inherit` on purpose (a ramp, never a face): the app declares its own theme keys in a plain `@theme` pointing at the emitted slots — `--font-body: var(--font-sans)`, `--font-heading: var(--font-display)`, `--font-code: var(--font-mono)` — and sets the ambient face on `body` in `@layer base`.
|
|
43
43
|
- **Every authored token group reaches a named consumer.** An authored ramp with no emitter vanishes without a trace — wire each group (colors → theme classes/utilities, ramp → text styles, elevation → radius/shadow utilities, motion → duration/easing vars) and let the ship gate's reach check prove it. The personality families reach the library by construction; the app's `--duration-*` / `--ease-*` reach app-owned animation code, or they are unused.
|
|
44
44
|
- **Compile the emitted CSS once per change** — a stray utility string inside a CSS comment can defeat a fully green byte-level check.
|
|
@@ -22,7 +22,7 @@ No database — three constraints, values yours: **categorical** series need dis
|
|
|
22
22
|
|
|
23
23
|
## Number, date, unit formatting
|
|
24
24
|
|
|
25
|
-
Tabular figures in tables and KPIs; thousands separators; unit or currency attached to the number; consistent decimals per column; large values truncate meaningfully (`1.2M`, never `1,200,000` in a tile); timestamps show relative when recent and absolute on hover (under `nurix`, that is `<Timestamp
|
|
25
|
+
Tabular figures in tables and KPIs; thousands separators; unit or currency attached to the number; consistent decimals per column; large values truncate meaningfully (`1.2M`, never `1,200,000` in a tile); timestamps show relative when recent and absolute on hover (under `nurix`, that is `<Timestamp>`, never a hand-rolled formatter); timezones explicit anywhere ambiguity costs money.
|
|
26
26
|
|
|
27
27
|
## Spacing & density
|
|
28
28
|
|
|
@@ -30,15 +30,15 @@ The 2/4/6/8 scale, period. Density is a per-project decision made once, not a pe
|
|
|
30
30
|
|
|
31
31
|
## Radius & stroke
|
|
32
32
|
|
|
33
|
-
Radius is identity — sharp (0–2px), soft (4–6px), or round (10px+) — chosen by the package and applied consistently; mixing radii on one surface reads as templated. One border weight per surface; elevation *or* borders separate layers, never both at full strength.
|
|
33
|
+
Radius is identity — sharp (0–2px), soft (4–6px), or round (10px+) — chosen by the package and applied consistently; mixing radii on one surface reads as templated. One border weight per surface; elevation *or* borders separate layers, never both at full strength. Under `nurix` both are the `edge` trait — the radius scale, `--radius-pill`, and the `--edge-hairline` / `--edge-stroke` weights — never per-surface literals.
|
|
34
34
|
|
|
35
35
|
## Loading, skeleton, progress
|
|
36
36
|
|
|
37
|
-
Skeletons for layout-known loads, spinners only for unknowable ones, optimistic updates for high-frequency mutations with undo. Skeletons mirror the real layout's bones (no generic gray blocks). Nothing pulses faster than 1.2s; under `nurix` a status pulse is `<Chip prefix="dot"
|
|
37
|
+
Skeletons for layout-known loads, spinners only for unknowable ones, optimistic updates for high-frequency mutations with undo. Skeletons mirror the real layout's bones (no generic gray blocks). Nothing pulses faster than 1.2s; under `nurix` a status pulse is `<Chip prefix="dot" isPulsing>`, never `animate-pulse` (a hardcoded keyframe neither `pace` nor the inline clamp can reach), and loaders keep breathing inside inline collections by design.
|
|
38
38
|
|
|
39
39
|
## Empty states
|
|
40
40
|
|
|
41
|
-
The three empties (first-
|
|
41
|
+
The three empties (first-run, filtered-to-zero, failed-to-load) and a queue's cleared-empty each say what happened and what to do next, in the project voice. Art direction follows the package's restraint: an icon or nothing for dense tools; illustration only where the package's feel says warm. Never a bare "No data".
|
|
42
42
|
|
|
43
43
|
## Keyboard
|
|
44
44
|
|
|
@@ -54,4 +54,4 @@ Mono face with `tabular-nums`; a shiki theme paired to the package (dark identit
|
|
|
54
54
|
|
|
55
55
|
## Polish
|
|
56
56
|
|
|
57
|
-
Focus rings on-brand (
|
|
57
|
+
Focus rings on-brand (a visible width, the accent — never browser default blue on a styled app; under `nurix` the library's `focus-ring`, sized by `--ring-width` / `--ring-offset` and coloured by `--ring`); selection color derived from the accent at low alpha; scrollbars styled thin on dense panes; cursor changes only where affordance is real.
|