@nurix/etna 0.19.2 → 0.19.4
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/harness-index.json +1 -1
- package/data/harnesses/code/CLAUDE.md +2 -2
- package/data/harnesses/code/harness.json +2 -2
- package/data/harnesses/code/rules/frontend.md +3 -3
- package/data/harnesses/code/skills/design/SKILL.md +20 -8
- package/data/harnesses/code/skills/design/data/catalog/rules.csv +10 -5
- package/data/harnesses/code/skills/design/data/combinations/Readme.md +8 -4
- package/data/harnesses/code/skills/design/data/combinations/accent-presets.md +113 -0
- package/data/harnesses/code/skills/design/data/combinations/fonts-known.json +11 -0
- package/data/harnesses/code/skills/design/data/combinations/manifest.json +57 -10
- package/data/harnesses/code/skills/design/data/combinations/motion.csv +10 -0
- package/data/harnesses/code/skills/design/data/combinations/palettes.csv +92 -49
- package/data/harnesses/code/skills/design/data/combinations/styles.csv +67 -54
- package/data/harnesses/code/skills/design/data/combinations/typography.csv +64 -61
- package/data/harnesses/code/skills/design/data/design-md/library/expo.md +4 -4
- package/data/harnesses/code/skills/design/data/design-md/library/lovable.md +5 -5
- package/data/harnesses/code/skills/design/data/design-md/library/minimax.md +6 -6
- package/data/harnesses/code/skills/design/data/design-md/library/notion.md +4 -4
- package/data/harnesses/code/skills/design/data/design-md/library/runwayml.md +4 -4
- package/data/harnesses/code/skills/design/data/design-md/library/sentry.md +2 -2
- package/data/harnesses/code/skills/design/data/design-md/library/slack.md +6 -6
- package/data/harnesses/code/skills/design/data/design-md/library/webflow.md +5 -5
- package/data/harnesses/code/skills/design/data/design-md/nurix.md +11 -7
- package/data/harnesses/code/skills/design/data/design-tokens/SCHEMA.md +256 -141
- package/data/harnesses/code/skills/design/data/design-tokens/airbnb/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/airtable/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/anthropic-claude/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/apple/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/binance/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/bmw/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/cal/design.tokens.json +313 -1
- package/data/harnesses/code/skills/design/data/design-tokens/clay/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/clickhouse/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/cohere/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/coinbase/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/composio/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/cursor/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/elevenlabs/design.tokens.json +313 -1
- package/data/harnesses/code/skills/design/data/design-tokens/expo/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/figma/design.tokens.json +313 -1
- package/data/harnesses/code/skills/design/data/design-tokens/framer/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/goldens.json +123 -123
- package/data/harnesses/code/skills/design/data/design-tokens/hashicorp/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/ibm/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/intercom/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/kraken/design.tokens.json +313 -1
- package/data/harnesses/code/skills/design/data/design-tokens/linear-app/design.resolver.json +1 -1
- package/data/harnesses/code/skills/design/data/design-tokens/linear-app/design.tokens.dark.json +10 -0
- package/data/harnesses/code/skills/design/data/design-tokens/linear-app/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/lovable/design.tokens.json +313 -1
- package/data/harnesses/code/skills/design/data/design-tokens/meta/design.tokens.json +313 -1
- package/data/harnesses/code/skills/design/data/design-tokens/minimax/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/mintlify/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/miro/design.tokens.json +313 -1
- package/data/harnesses/code/skills/design/data/design-tokens/mistral-ai/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/mongodb/design.tokens.json +313 -1
- package/data/harnesses/code/skills/design/data/design-tokens/notion/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/nurix/design.tokens.json +307 -2
- package/data/harnesses/code/skills/design/data/design-tokens/nvidia/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/ollama/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/opencode-ai/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/posthog/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/raycast/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/replicate/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/resend/design.resolver.json +1 -1
- package/data/harnesses/code/skills/design/data/design-tokens/resend/design.tokens.dark.json +10 -0
- package/data/harnesses/code/skills/design/data/design-tokens/resend/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/revolut/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/runwayml/design.tokens.json +310 -2
- package/data/harnesses/code/skills/design/data/design-tokens/sanity/design.tokens.json +313 -1
- package/data/harnesses/code/skills/design/data/design-tokens/sentry/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/shopify/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/slack/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/spacex/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/spotify/design.tokens.json +313 -1
- package/data/harnesses/code/skills/design/data/design-tokens/stripe/design.tokens.json +313 -1
- package/data/harnesses/code/skills/design/data/design-tokens/supabase/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/superhuman/design.tokens.json +313 -1
- package/data/harnesses/code/skills/design/data/design-tokens/tesla/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/theverge/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/together-ai/design.tokens.json +313 -1
- package/data/harnesses/code/skills/design/data/design-tokens/uber/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/vercel/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/voltagent/design.resolver.json +1 -1
- package/data/harnesses/code/skills/design/data/design-tokens/voltagent/design.tokens.dark.json +10 -0
- package/data/harnesses/code/skills/design/data/design-tokens/voltagent/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/warp/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/webflow/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/wired/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/wise/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/x-ai/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/design-tokens/zapier/design.tokens.json +309 -1
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/command-center.tsx +25 -25
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/create-edit.tsx +18 -18
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/dashboard.tsx +82 -105
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/entity-detail.tsx +97 -93
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/list-detail.tsx +16 -15
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/log-stream.tsx +34 -34
- package/data/harnesses/code/skills/design/data/operator-pack/surface-catalog.md +5 -5
- package/data/harnesses/code/skills/design/references/create.md +49 -7
- package/data/harnesses/code/skills/design/references/diagnosis.md +3 -3
- package/data/harnesses/code/skills/design/references/hard-rules.md +25 -3
- package/data/harnesses/code/skills/design/references/improve.md +32 -3
- package/data/harnesses/code/skills/design/references/interaction.md +3 -3
- package/data/harnesses/code/skills/design/references/introspect.md +1 -1
- package/data/harnesses/code/skills/design/references/lifecycle.md +2 -2
- package/data/harnesses/code/skills/design/references/personality.md +280 -133
- package/data/harnesses/code/skills/design/references/structure.md +22 -1
- package/data/harnesses/code/skills/design/scripts/convert-design-md.mjs +306 -26
- package/data/harnesses/code/skills/design/scripts/derive-personality.mjs +596 -104
- package/data/harnesses/code/skills/design/scripts/lib/checks.mjs +200 -11
- package/data/harnesses/code/skills/design/scripts/lib/vocabulary.mjs +65 -0
- package/data/harnesses/code/skills/design/scripts/phase-gate.mjs +95 -11
- package/data/harnesses/code/skills/design/scripts/preship-sweep.mjs +50 -5
- package/data/harnesses/code/skills/design/scripts/render-rung.mjs +214 -14
- package/data/harnesses/code/skills/design/scripts/scaffold.mjs +242 -25
- package/data/harnesses/code/skills/design/scripts/self-integrity.mjs +81 -6
- package/data/harnesses/code/skills/design/scripts/snapshot-goldens.mjs +101 -5
- package/data/harnesses/code/skills/design/scripts/validate-combinations.mjs +142 -17
- package/data/harnesses/etna/CLAUDE.md +1 -1
- package/data/harnesses/etna/harness.json +2 -2
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# design-tokens — schema `design-tokens/
|
|
1
|
+
# design-tokens — schema `design-tokens/v2`
|
|
2
2
|
|
|
3
3
|
A look travels as a **design-tokens artifact**: three JSON files in the pinned **DTCG 2025.10 Stable**
|
|
4
4
|
grammar (W3C Design Tokens Community Group — the dated snapshot; the live drafts carry the spec's own
|
|
@@ -16,15 +16,21 @@ documentation format.
|
|
|
16
16
|
**One schema, every origin.** A project's own artifact, an extracted artifact, a shipped library
|
|
17
17
|
entry, and the Nurix default all use this identical shape; only the payload's `provenance` varies.
|
|
18
18
|
|
|
19
|
+
**v1 → v2 in one line:** everything structural survives; the vocabulary grows — **expression arrives
|
|
20
|
+
as tokens** (27 primitives, 36 color roles, a 7-step ramp, 4 elevation composites, an optional wash
|
|
21
|
+
pair, and the `theme-default` / `anchors` payload keys), so the no-literals rule and the token-lock
|
|
22
|
+
now guard a vocabulary with range. Existing v1 artifacts migrate mechanically:
|
|
23
|
+
`convert-design-md.mjs --upgrade-v2` (idempotent; the Migration section below).
|
|
24
|
+
|
|
19
25
|
---
|
|
20
26
|
|
|
21
27
|
## The three files
|
|
22
28
|
|
|
23
29
|
| File | Ships | Contents |
|
|
24
30
|
| --- | --- | --- |
|
|
25
|
-
| `design.tokens.json` | always | The base/light set: the
|
|
31
|
+
| `design.tokens.json` | always | The base/light set: the 27 visual primitives, the 36 color roles, the typography composites, the 4 elevation composites, the optional wash pair, and the file-level `$extensions["com.nurix.design"]` payload. The base set **is** the light rendering — light is never omitted, even when `theme-default` is `dark`. |
|
|
26
32
|
| `design.tokens.dark.json` | only when the look has dark values | The dark modifier set: the `color` group remapped, nothing else. |
|
|
27
|
-
| `design.resolver.json` | always | Sets, modifiers, and resolution order.
|
|
33
|
+
| `design.resolver.json` | always | Sets, modifiers, and resolution order. The theme modifier's `default` mirrors the payload's `theme-default`. |
|
|
28
34
|
|
|
29
35
|
The three files sit together in one directory: a project's artifact lives at the repo root; a library
|
|
30
36
|
entry lives at `data/design-tokens/<product-slug>/`; the Nurix default at `data/design-tokens/nurix/`.
|
|
@@ -47,19 +53,22 @@ the payload's `provenance` field:
|
|
|
47
53
|
A token is an object with `$type` and `$value`, plus optional `$description` and `$extensions`.
|
|
48
54
|
Groups nest; the file root is a group and carries the file-level `$extensions`. An alias is the
|
|
49
55
|
string `"{path.to.token}"` and may appear anywhere a value of the target's type is legal, including
|
|
50
|
-
composite sub-values; alias chains are legal, cycles are not. Exactly
|
|
51
|
-
**never invent a custom `$type
|
|
56
|
+
composite sub-values; alias chains are legal, cycles are not. Exactly ten `$type` values appear —
|
|
57
|
+
**never invent a custom `$type`** (`shadow` and `gradient` are DTCG 2025.10 composite types, not
|
|
58
|
+
inventions):
|
|
52
59
|
|
|
53
60
|
| `$type` | `$value` shape | Used by |
|
|
54
61
|
| --- | --- | --- |
|
|
55
|
-
| `color` | `{ "colorSpace": "oklch"\|"srgb", "components": [n, n, n], "alpha"?: n, "hex"?: "#rrggbb" }` | the
|
|
56
|
-
| `dimension` | `{ "value": n, "unit": "px" }` (`px`\|`rem` per spec; this artifact always writes `px`) | `radius`, `border-weight`, `icon-size-base`, the 8 layout primitives, ramp `fontSize
|
|
57
|
-
| `duration` | `{ "value": n, "unit": "ms" }` | the 4 duration primitives |
|
|
62
|
+
| `color` | `{ "colorSpace": "oklch"\|"srgb", "components": [n, n, n], "alpha"?: n, "hex"?: "#rrggbb" }` | the 36 color roles, shadow layer colors, gradient stop colors |
|
|
63
|
+
| `dimension` | `{ "value": n, "unit": "px" }` (`px`\|`rem` per spec; this artifact always writes `px`) | `radius`, `border-weight`, `icon-size-base`, the 8 layout primitives, ramp `fontSize`, shadow lengths |
|
|
64
|
+
| `duration` | `{ "value": n, "unit": "ms" }` | the 4 duration primitives, `stagger-unit` |
|
|
58
65
|
| `cubicBezier` | `[x1, y1, x2, y2]` (numbers; x in 0–1) | `ease-default` |
|
|
59
66
|
| `fontFamily` | array of family-name strings | `font-sans`, `font-mono`, `typography.family.*` |
|
|
60
67
|
| `fontWeight` | number 100–900 | `font-weight-normal`, `font-weight-strong`, ramp `fontWeight` |
|
|
61
68
|
| `number` | plain JSON number | `shadow-intensity`, `density-gap`, `density-inset`, `heading-tracking`, `contrast-boost`, `icon-stroke`, ramp `lineHeight` / `letterSpacing` |
|
|
62
|
-
| `typography` | composite `{ fontFamily, fontSize, fontWeight, letterSpacing, lineHeight }` | the
|
|
69
|
+
| `typography` | composite `{ fontFamily, fontSize, fontWeight, letterSpacing, lineHeight }` | the 7 ramp steps |
|
|
70
|
+
| `shadow` | composite **array of layers** `{ color, offsetX, offsetY, blur, spread }` (color/dimension sub-value shapes as above) | the 4 `elevation.*` tokens |
|
|
71
|
+
| `gradient` | composite **array of stops** `{ color, position }` (position a number 0–1) | the ≤ 2 `wash.*` tokens |
|
|
63
72
|
|
|
64
73
|
Color rules: an oklch source stays `"colorSpace": "oklch"` with `components` carrying the literal
|
|
65
74
|
`[L, C, H]` numbers from the source string; a hex/rgba (or any other CSS color) source becomes
|
|
@@ -86,15 +95,18 @@ Everything else in the artifact is spec-shaped.
|
|
|
86
95
|
|
|
87
96
|
One vendor namespace, reverse-domain, on the **base file's root group only** (the dark file carries no
|
|
88
97
|
payload; the resolver has no `$extensions`). It holds everything the look needs that is not a design
|
|
89
|
-
token:
|
|
98
|
+
token — twelve keys in v2, plus the optional `look` record:
|
|
90
99
|
|
|
91
100
|
| Key | Type | Legal values / shape |
|
|
92
101
|
| --- | --- | --- |
|
|
93
|
-
| `schema` | string | Literal `design-tokens/
|
|
102
|
+
| `schema` | string | Literal `design-tokens/v2`. A parser refuses an unknown value. |
|
|
94
103
|
| `dtcg` | string | Literal `2025.10` — the pinned DTCG snapshot this artifact conforms to. |
|
|
95
104
|
| `product` | string | The product name; slugified (lowercase, non-alphanumeric runs → `-`) it becomes the `data-style` value. |
|
|
96
105
|
| `provenance` | enum | `project` \| `extracted` \| `library` \| `default` |
|
|
97
106
|
| `sourced` | string | ISO date the artifact was authored, extracted, or last re-sourced. |
|
|
107
|
+
| `theme-default` | enum | `light` \| `dark` — which context the app boots in: scaffold writes it to `<html data-theme>`, and the resolver's `theme.default` mirrors it. **The brief's explicit theme direction is binding.** Absent → `light`. The base set remains the light rendering either way. |
|
|
108
|
+
| `anchors` | string[] | 2–3 non-empty entries — the named real-product anchors from intake ("Linear", "Stripe Dashboard"), recorded so improve/judge runs critique against the same gestalt targets create aimed at. **Required on `provenance: project` artifacts**; optional on library/default/extracted. |
|
|
109
|
+
| `look` | object | **Optional.** The full intake look block persisted by create-authored artifacts — `theme` (`light` \| `dark` \| `both`), `anchors` (2–3 named products), `delight` (`baseline` \| `expressive`), `resolution` (`project` \| `library:<slug>` \| `seed:<style>+<palette>` \| `default`), `resolution-mode` (`confirmed` \| `recorded`). A provenance record, not a projection: `theme-default` and the top-level `anchors` remain the canonical keys emission consumes. Enum-checked when present (rule 2; the enums mirror the intake gate's `validateLookBlock`). Conforming tools preserve unknown payload keys — this one included. |
|
|
98
110
|
| `verbosity` | enum | `full` \| `default` \| `compact` \| `expert` — emits `data-verbosity` on `<html>`. |
|
|
99
111
|
| `feedback` | enum | `subtle` \| `minimal` — emits `data-feedback`. |
|
|
100
112
|
| `density-tier` | enum | `spacious` \| `standard` \| `dense` — emits `data-density`; drives the density base multipliers. |
|
|
@@ -113,27 +125,28 @@ this vocabulary — a radius not on `radius-scale`, a spacing off `spacing-scale
|
|
|
113
125
|
| --- | --- | --- |
|
|
114
126
|
| `spacing-scale` | number[] (px) | Non-empty, ascending — on-scale values only. |
|
|
115
127
|
| `radius-scale` | number[] (px) | Non-empty, ascending. |
|
|
116
|
-
| `shadow-levels` | string[] | Non-empty; names drawn from `none` `2xs` `xs` `sm` `md` `lg` `xl` `2xl`, ordered by strictly increasing intensity. |
|
|
128
|
+
| `shadow-levels` | string[] | Non-empty; names drawn from `none` `2xs` `xs` `sm` `md` `lg` `xl` `2xl` `overlay`, ordered by strictly increasing intensity. The Nurix default's vocabulary is `["none", "sm", "md", "lg", "overlay"]`. |
|
|
117
129
|
| `motion` | `{ "durations": number[], "easings": string[] }` | Durations in ms, non-empty, ascending; easings non-empty CSS easing strings. |
|
|
118
130
|
| `icons` | `{ "family": string }` | The icon family, e.g. `lucide`. |
|
|
119
131
|
| `accent-count` | number | ≥ 1. |
|
|
120
132
|
|
|
121
133
|
The manifest carries only vocabulary that is not already a token — **it never restates a token
|
|
122
|
-
value**. The allowlists that are tokens are read from the tokens: the
|
|
134
|
+
value**. The allowlists that are tokens are read from the tokens: the 36 color values from the
|
|
123
135
|
`color` group, the font stacks from `primitive.font-sans` / `primitive.font-mono`, the icon stroke
|
|
124
|
-
from `primitive.icon-stroke
|
|
136
|
+
from `primitive.icon-stroke`, and the elevation/wash color literals from those groups' preserved
|
|
137
|
+
byte forms.
|
|
125
138
|
|
|
126
139
|
---
|
|
127
140
|
|
|
128
|
-
## `primitive.*` — the
|
|
141
|
+
## `primitive.*` — the 27 visual primitives
|
|
129
142
|
|
|
130
|
-
One group, `primitive`, holding all
|
|
143
|
+
One group, `primitive`, holding all 27 (leaf names are law — they map 1:1 to the emitted CSS
|
|
131
144
|
variables, without the leading `--`). Every token is either concrete or hinted.
|
|
132
145
|
|
|
133
146
|
| Family | Token (`primitive.…`) | `$type` | Notes | Example (source form) |
|
|
134
147
|
| --- | --- | --- | --- | --- |
|
|
135
148
|
| geometric | `radius` | `dimension` | | `6px` |
|
|
136
|
-
| | `shadow-intensity` | `number` | 0–1 multiplier on
|
|
149
|
+
| | `shadow-intensity` | `number` | 0–1 master multiplier on every emitted `--shadow-*` alpha; 0 legally flattens everything (flat looks stay expressible). New looks default 1.0 — the elevation composites carry designed alphas. | `0.4` |
|
|
137
150
|
| | `border-weight` | `dimension` | | `1px` |
|
|
138
151
|
| | `density-gap` | `number` | multiplier on the `0.25rem` spacing base | `1` |
|
|
139
152
|
| | `density-inset` | `number` | multiplier on component insets | `1` |
|
|
@@ -147,6 +160,7 @@ variables, without the leading `--`). Every token is either concrete or hinted.
|
|
|
147
160
|
| | `duration-normal` | `duration` | | `150ms` |
|
|
148
161
|
| | `duration-slow` | `duration` | | `250ms` |
|
|
149
162
|
| | `ease-default` | `cubicBezier` | carries `.css` | `cubic-bezier(0.4,0,0.2,1)` |
|
|
163
|
+
| | `stagger-unit` | `duration` | motion-moment base, authored per look; legal 0–60ms, default 30 (reads polished; 0 reads utilitarian) | `30ms` |
|
|
150
164
|
| surface | `contrast-boost` | `number` | multiplier, 1 = default | `1` |
|
|
151
165
|
| iconography | `icon-stroke` | `number` | px stroke-width | `1.75` |
|
|
152
166
|
| | `icon-size-base` | `dimension` | | `16px` |
|
|
@@ -159,46 +173,59 @@ variables, without the leading `--`). Every token is either concrete or hinted.
|
|
|
159
173
|
| | `form-max-width` | `dimension` | | `640px` |
|
|
160
174
|
| | `table-row-height` | `dimension` | | `40px` |
|
|
161
175
|
|
|
162
|
-
**Derived, never stored.** Emission computes `--ease-enter
|
|
163
|
-
|
|
164
|
-
|
|
176
|
+
**Derived, never stored.** Emission computes `--ease-enter` and `--ease-exit` from `ease-default`,
|
|
177
|
+
and `--stagger-delay` from `stagger-unit` (`--stagger-delay: var(--stagger-unit)` — the
|
|
178
|
+
`.motion-stagger` cascade base). They are not tokens, not authored, and not part of the coverage
|
|
179
|
+
gate. Do not add them.
|
|
165
180
|
|
|
166
181
|
---
|
|
167
182
|
|
|
168
|
-
## `color.*` — the
|
|
183
|
+
## `color.*` — the 36 color roles
|
|
169
184
|
|
|
170
|
-
One flat group, `color`, holding all
|
|
171
|
-
Every token is `$type: "color"` with a typed `$value` **and** mandatory preserved `.css
|
|
185
|
+
One flat group, `color`, holding all 36 (leaf names map 1:1 to the emitted `--<name>` variables).
|
|
186
|
+
Every token is `$type: "color"` with a typed `$value` **and** mandatory preserved `.css` (an aliased
|
|
187
|
+
role takes both from its target).
|
|
172
188
|
|
|
173
|
-
- **Semantic (
|
|
189
|
+
- **Semantic (19):** `background` · `foreground` · `card` · `card-foreground` · `muted` ·
|
|
174
190
|
`muted-foreground` · `border` · `input` · `ring` · `primary` · `primary-foreground` · `accent` ·
|
|
175
|
-
`accent-foreground` · `destructive` · `destructive-foreground` · `success` · `warning
|
|
191
|
+
`accent-foreground` · `destructive` · `destructive-foreground` · `success` · `warning` ·
|
|
192
|
+
`surface-raised` · `surface-overlay`.
|
|
176
193
|
- **Substrate (17):** `popover` · `popover-foreground` · `secondary` · `secondary-foreground` ·
|
|
177
194
|
`chart-1` · `chart-2` · `chart-3` · `chart-4` · `chart-5` · `sidebar` · `sidebar-foreground` ·
|
|
178
195
|
`sidebar-primary` · `sidebar-primary-foreground` · `sidebar-accent` · `sidebar-accent-foreground` ·
|
|
179
196
|
`sidebar-border` · `sidebar-ring`.
|
|
180
197
|
|
|
181
|
-
|
|
198
|
+
**`surface-raised` / `surface-overlay` — the dark ladder.** In light looks the two roles may alias
|
|
199
|
+
`{color.card}` / `{color.popover}` — zero visual change. In dark looks they carry the
|
|
200
|
+
**surface-lightness elevation ladder** (raised surfaces get *lighter*, ~+4–6 L points per step, hue
|
|
201
|
+
held — the physics dark cannot fake with shadows). Components express elevation as
|
|
202
|
+
`bg-surface-raised` / `bg-surface-overlay` instead of shadow-only. Both appear in the dark set (dark
|
|
203
|
+
completeness counts 36).
|
|
204
|
+
|
|
205
|
+
No consumed token falls back to a shadcn-gray default: emission sets all 36. A color token may alias
|
|
182
206
|
another (`"$value": "{color.primary}"`), in which case it takes the target's `.css`; mechanical
|
|
183
|
-
conversion writes literals, aliases are for authored files.
|
|
207
|
+
conversion writes literals, aliases are for authored files and the two ladder roles.
|
|
184
208
|
|
|
185
209
|
---
|
|
186
210
|
|
|
187
211
|
## `typography.*` — families and the ramp
|
|
188
212
|
|
|
189
213
|
The first-class machine form of the type scale, in two subgroups. It reaches CSS variables only
|
|
190
|
-
through the primitives it aliases; emitters
|
|
191
|
-
|
|
214
|
+
through the primitives it aliases; emitters read the ramp as data and materialize it as the seven
|
|
215
|
+
`text-*` utilities (the type-scale rule's fixed scale *is* the ramp — typ-09).
|
|
192
216
|
|
|
193
|
-
**`typography.family.*`** — the heading/body pairing plus mono,
|
|
217
|
+
**`typography.family.*`** — the heading/body pairing plus mono, plus an optional display slot, all
|
|
218
|
+
`$type: "fontFamily"`. The three-*job* rhythm is display / body / numeric-mono (typ-06: ≤ 3 families,
|
|
219
|
+
each with a distinct job); `heading` stays for compat and typically aliases `display` or `body`:
|
|
194
220
|
|
|
195
221
|
| Token | Value |
|
|
196
222
|
| --- | --- |
|
|
197
223
|
| `typography.family.heading` | `"{primitive.font-sans}"` — or its own stack when the look pairs a distinct heading family. |
|
|
198
224
|
| `typography.family.body` | `"{primitive.font-sans}"` |
|
|
199
225
|
| `typography.family.mono` | `"{primitive.font-mono}"` |
|
|
226
|
+
| `typography.family.display` | **optional** third-slot member; may alias `heading`. |
|
|
200
227
|
|
|
201
|
-
**`typography.ramp.*`** —
|
|
228
|
+
**`typography.ramp.*`** — seven fixed step names, each `$type: "typography"` carrying all five
|
|
202
229
|
sub-values:
|
|
203
230
|
|
|
204
231
|
| Step | Role |
|
|
@@ -209,6 +236,7 @@ sub-values:
|
|
|
209
236
|
| `body` | UI + prose |
|
|
210
237
|
| `small` | labels, meta, dense table |
|
|
211
238
|
| `caption` | uppercase eyebrow (text case is a usage rule; the composite carries no case slot) |
|
|
239
|
+
| `display` | the scale-drama step: KPI numerals, the one focal metric, a brand-forward page title. `fontSize` guidance 32–48px; `fontFamily` may alias `typography.family.display`. Usage is rule-gated, not schema-gated: display-step elements only on `brand-forward` surfaces, ≤ 1 per surface (typ-20). Baseline surfaces keep the 24px-h1 world. |
|
|
212
240
|
|
|
213
241
|
| Sub-value | Shape |
|
|
214
242
|
| --- | --- |
|
|
@@ -220,6 +248,48 @@ sub-values:
|
|
|
220
248
|
|
|
221
249
|
---
|
|
222
250
|
|
|
251
|
+
## `elevation.*` — depth as a designed system
|
|
252
|
+
|
|
253
|
+
Four tokens, each `$type: "shadow"`, `$value` an **array of layers** (`{ color, offsetX, offsetY,
|
|
254
|
+
blur, spread }` per layer — the DTCG shadow shape; color sub-values follow the color rules incl.
|
|
255
|
+
`alpha`). Leaf names are law → emitted as `--shadow-sm`, `--shadow-md`, `--shadow-lg`,
|
|
256
|
+
`--shadow-overlay`, plus the matching `@theme` shadow utilities.
|
|
257
|
+
|
|
258
|
+
| Token | Role | Authoring guidance (seeds carry real values) |
|
|
259
|
+
| --- | --- | --- |
|
|
260
|
+
| `elevation.sm` | hairline lift — hover on rows/cards | 1–2 layers, y ≤ 2px, blur ≤ 4px |
|
|
261
|
+
| `elevation.md` | raised panel, dropdown | 2 layers (ambient + key) |
|
|
262
|
+
| `elevation.lg` | brand-forward feature panel | 2–3 layers, tinted |
|
|
263
|
+
| `elevation.overlay` | side panel / dialog scrim companion | widest blur; the only level a modal may use |
|
|
264
|
+
|
|
265
|
+
Rules:
|
|
266
|
+
|
|
267
|
+
- Shadow color is **brand-tintable** — a low-alpha tint toward the brand hue (e.g.
|
|
268
|
+
`rgba(0,55,112,0.08)`), never pure black at alpha ≥ 0.15.
|
|
269
|
+
- `primitive.shadow-intensity` stays the master multiplier: emission wraps every layer alpha as
|
|
270
|
+
`calc(<alpha> * var(--shadow-intensity))`, so `prefers-contrast: more` (which zeroes the
|
|
271
|
+
multiplier) and flat looks keep working.
|
|
272
|
+
- The **token-level `.css`** is the full serialized shadow list — the preserved byte form, carrying
|
|
273
|
+
every layer color's byte form inside it (composite sub-values have no `$extensions` slot of their
|
|
274
|
+
own). Zero lengths serialize bare `0`; the alpha may ride the `calc(… * var(--shadow-intensity))`
|
|
275
|
+
wrapper in the byte form — parse-back strips it to the layer alpha.
|
|
276
|
+
- The lever manifest's `shadow-levels` key is unchanged in shape; `overlay` joins its legal name
|
|
277
|
+
vocabulary.
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## `wash.*` — the tasteful-gradient path (optional group)
|
|
282
|
+
|
|
283
|
+
At most two tokens, `$type: "gradient"` (the DTCG stops array; each stop's color follows the color
|
|
284
|
+
rules, `position` in 0–1). Emitted as `--wash-surface` / `--wash-emphasis`. **Optional presence**: a
|
|
285
|
+
look that declares no washes omits the group entirely — the coverage gate validates shape when
|
|
286
|
+
present, never requires it. The token-level `.css` carries the authored gradient (the stop list holds
|
|
287
|
+
no geometry — angle and shape live in the byte form). Consumption is budget-gated (only
|
|
288
|
+
`brand-forward` surfaces), and the anti-slop gradient ban (col-12 — neon purple/blue glows,
|
|
289
|
+
purple-on-white) applies to wash values: a wash is a low-chroma tint ramp, not a hero gradient.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
223
293
|
## The preserved-css pattern
|
|
224
294
|
|
|
225
295
|
Every formatting-sensitive value carries **two forms that must agree**: the typed `$value` (the
|
|
@@ -227,18 +297,22 @@ queryable semantic) and `$extensions["com.nurix.design"].css` — the exact sour
|
|
|
227
297
|
Emitters prefer the preserved string, so output is byte-stable across convert/emit round trips.
|
|
228
298
|
|
|
229
299
|
- **Mandatory `.css`:** every `color` token; `heading-tracking` (sources vary — `0`, `0em`,
|
|
230
|
-
`-0.015em`); `ease-default`; any `fontFamily` token carrying its own stack
|
|
300
|
+
`-0.015em`); `ease-default`; any `fontFamily` token carrying its own stack; every `elevation.*`
|
|
301
|
+
token (the serialized shadow list); every `wash.*` token (the authored gradient).
|
|
231
302
|
- **Omitted where serialization is deterministic:** `dimension` and `duration` serialize as
|
|
232
303
|
`<value><unit>`; `number` and `fontWeight` as the plain number; `cubicBezier` (absent `.css`) as
|
|
233
304
|
`cubic-bezier(x1, y1, x2, y2)`; `fontFamily` (absent `.css`) as the families joined with `", "`,
|
|
234
305
|
multi-word names double-quoted.
|
|
235
|
-
- **On a composite token**, `.css` is an object keyed by sub-value name
|
|
236
|
-
formatting-sensitive inline sub-values
|
|
237
|
-
takes its `.css` from the alias target
|
|
306
|
+
- **On a composite token**, `.css` is an object keyed by sub-value name for `typography` (present
|
|
307
|
+
for formatting-sensitive inline sub-values, e.g. `{ "letterSpacing": "0.02em" }`; an aliased
|
|
308
|
+
sub-value takes its `.css` from the alias target) and a single string — the full serialized value —
|
|
309
|
+
for `shadow` and `gradient`.
|
|
238
310
|
- **Parse-back verification is a converter requirement:** whatever writes a token must verify that
|
|
239
311
|
parsing `.css` reproduces `$value` exactly, and that re-serializing `$value` reproduces the byte
|
|
240
312
|
form — oklch components are the literal source numbers; srgb components round-trip through the
|
|
241
|
-
`hex` field
|
|
313
|
+
`hex` field; a shadow's `.css` parses back to its layer array (the intensity `calc` wrapper strips
|
|
314
|
+
to the layer alpha); a gradient's `.css` must contain each stop color's byte form. The gate
|
|
315
|
+
re-verifies.
|
|
242
316
|
|
|
243
317
|
## Hints and the coverage tri-state
|
|
244
318
|
|
|
@@ -254,6 +328,8 @@ words. Every gated token is in exactly one state:
|
|
|
254
328
|
|
|
255
329
|
Resolving a hint deletes the `hint` key and records the rationale in the token's `$description`, so
|
|
256
330
|
the resolved artifact is self-explaining and re-derivable. Judgment is recorded, never silent.
|
|
331
|
+
(`--emit --provisional` — the golden/regression flow only — emits hinted tokens at their provisional
|
|
332
|
+
`$values` instead of refusing; it never ships output.)
|
|
257
333
|
|
|
258
334
|
```json
|
|
259
335
|
{
|
|
@@ -273,23 +349,38 @@ resolves to:
|
|
|
273
349
|
}
|
|
274
350
|
```
|
|
275
351
|
|
|
276
|
-
## Per-value provenance
|
|
352
|
+
## Per-value provenance — the layer vocabulary
|
|
277
353
|
|
|
278
354
|
A value's provenance rationale — why it diverges from a brand default, what judgment resolved its
|
|
279
355
|
hint — lives in the token's `$description`. Mechanical conversion moves source comments here.
|
|
280
356
|
|
|
357
|
+
**The layer tags.** A `$description` that opens with a tag names the composition layer that set the
|
|
358
|
+
value (the layer model's audit surface): **`source:`** (the resolved source), **`brief:`** (a brief
|
|
359
|
+
binding), **`deviation:`** (a recorded deviation — the only layer that may contradict a domain owner
|
|
360
|
+
or leave the tables entirely; every such value carries its reason). Untagged values are inherited
|
|
361
|
+
from the resolved source or defaults. The mechanical-annotation prefixes the converters write
|
|
362
|
+
(`derived:`, `corpus:`, `rationale:`, `note:`, `synthesized:`, `brand:`, `dark:`) remain legal; any
|
|
363
|
+
other opening tag fails the gate. **Every off-manifest value carries `deviation:`** — an artifact
|
|
364
|
+
base value off its own lever-manifest allowlist without one fails rule 8. The artifact root's
|
|
365
|
+
`$description` carries the resolution record (which source won, the runner-up, the deciding
|
|
366
|
+
evidence).
|
|
367
|
+
|
|
281
368
|
---
|
|
282
369
|
|
|
283
370
|
## The dark set — `design.tokens.dark.json`
|
|
284
371
|
|
|
285
372
|
Ships **only when the look has dark values**; light is never omitted — the base set is the light
|
|
286
|
-
rendering
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
373
|
+
rendering (even for a `theme-default: dark` look; only the boot attribute and resolver default move).
|
|
374
|
+
When it ships:
|
|
375
|
+
|
|
376
|
+
- It carries exactly the `color` group, all 36 roles, same shape rules (typed `$value` + `.css`;
|
|
377
|
+
aliases legal — they resolve after overlay, so `"{color.card}"` in the dark file resolves to the
|
|
378
|
+
dark card) — and nothing else: no primitives, no typography, no elevation, no payload (the base
|
|
379
|
+
file is the payload's single carrier).
|
|
380
|
+
- Emission targets `:root[data-theme="dark"]` (specificity 0,2,0 — the same armor as the light
|
|
381
|
+
block; a lone attribute selector loses to the component library's later `:root` by source order),
|
|
382
|
+
remapping all 36; when the file is absent, no dark block is emitted and the project is light-only.
|
|
383
|
+
- The `surface-raised` / `surface-overlay` roles carry the dark surface-lightness ladder (above).
|
|
293
384
|
|
|
294
385
|
```json
|
|
295
386
|
{
|
|
@@ -300,7 +391,7 @@ rendering. When it ships:
|
|
|
300
391
|
}
|
|
301
392
|
```
|
|
302
393
|
|
|
303
|
-
(Fragment — a real dark file carries all
|
|
394
|
+
(Fragment — a real dark file carries all 36.)
|
|
304
395
|
|
|
305
396
|
---
|
|
306
397
|
|
|
@@ -319,10 +410,10 @@ place.
|
|
|
319
410
|
**Resolution order (deterministic):** concatenate the set files in array order; select one context
|
|
320
411
|
per modifier (the `default` when unstated); overlay the context's files in order — a later file
|
|
321
412
|
overrides an earlier one per token path, and an overlay may only override paths that exist in the
|
|
322
|
-
base, never introduce new ones. Aliases resolve after overlay. **
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
`"dark": ["design.tokens.dark.json"]`:
|
|
413
|
+
base, never introduce new ones. Aliases resolve after overlay. **The base set is always the light
|
|
414
|
+
rendering:** the `light` context is always the empty overlay `[]`, and `theme.default` mirrors the
|
|
415
|
+
payload's `theme-default` (`light` when absent — a dark-first look sets both to `dark`). A light-only
|
|
416
|
+
look carries only the `light` context; a look with dark adds `"dark": ["design.tokens.dark.json"]`:
|
|
326
417
|
|
|
327
418
|
```json
|
|
328
419
|
{
|
|
@@ -343,73 +434,134 @@ The slug is the payload's `product`, slugified. Emission order is this document'
|
|
|
343
434
|
|
|
344
435
|
| Source | Emitted target |
|
|
345
436
|
| --- | --- |
|
|
346
|
-
| `primitive.<name>` (
|
|
437
|
+
| `primitive.<name>` (25 static) | `[data-style="<slug>"] { --<name>: … }` — 1:1 by leaf name |
|
|
347
438
|
| `primitive.density-gap` / `density-inset` | governed refs (`var(--density-base-gap/inset)`); the tier writes the base |
|
|
348
|
-
| `color.<name>` (
|
|
349
|
-
| dark set `color.<name>` |
|
|
439
|
+
| `color.<name>` (36) | `:root, :root[data-theme="light"] { --<name>: … }` |
|
|
440
|
+
| dark set `color.<name>` | `:root[data-theme="dark"] { --<name>: … }` — only when the dark file ships |
|
|
441
|
+
| `elevation.<level>` (4) | `--shadow-<level>` in the `[data-style]` block **and** the `@theme` shadow utilities (`shadow-sm` … `shadow-overlay`); every alpha rides `calc(… * var(--shadow-intensity))` |
|
|
442
|
+
| `wash.<slot>` (≤ 2, optional) | `--wash-surface` / `--wash-emphasis` in the `[data-style]` block — consumed only on budget-granted surfaces |
|
|
443
|
+
| `typography.ramp.<step>` (7) | `@theme` text utilities `text-h1` … `text-display` carrying size / line-height / weight / tracking per step — the ramp becomes *classes* (typ-09: the fixed scale IS the ramp) |
|
|
444
|
+
| `primitive.stagger-unit` | `--stagger-delay: var(--stagger-unit)` (derived) + the named motion classes `.motion-lift` (hover −1px translateY + `elevation.sm` at `duration-fast`/`ease-default`) and `.motion-stagger` (children arrival cascade at `--stagger-delay`, capped at 8 children — mot-08), both inert under `prefers-reduced-motion` (mot-03) |
|
|
350
445
|
| `verbosity` / `feedback` / `density-tier` | `<html data-verbosity / data-feedback / data-density>` |
|
|
446
|
+
| `theme-default` | `<html data-theme>` (the boot theme; the toggle round-trips the same attribute) |
|
|
351
447
|
| `product` | `<html data-style="<slug>">` |
|
|
352
|
-
| `product-traits` | read by the front gate; never emitted |
|
|
353
|
-
| `typography.family.*`
|
|
354
|
-
| — (derived at emit) | `--ease-enter`, `--ease-exit
|
|
448
|
+
| `product-traits` / `anchors` | read by the front gate and improve/judge runs; never emitted |
|
|
449
|
+
| `typography.family.*` | data for emitters and exemplars; reaches CSS only through the primitives it aliases |
|
|
450
|
+
| — (derived at emit) | `--ease-enter`, `--ease-exit` from `ease-default`; `--stagger-delay` from `stagger-unit` |
|
|
355
451
|
|
|
356
452
|
---
|
|
357
453
|
|
|
358
454
|
## Validation — what `--check` enforces
|
|
359
455
|
|
|
360
456
|
The coverage gate. It exits non-zero and **names every offending path** so the fix is mechanical; an
|
|
361
|
-
artifact that passes is guaranteed derivable.
|
|
457
|
+
artifact that passes is guaranteed derivable. Rules 1–12 keep their v1 numbering (amended where the
|
|
458
|
+
surface grew); rules 13–15 are the v2 additions.
|
|
362
459
|
|
|
363
460
|
1. **Files** — `design.tokens.json` and `design.resolver.json` present and parse as JSON; every file
|
|
364
461
|
the resolver references exists beside it; `design.tokens.dark.json` exists exactly when the
|
|
365
462
|
resolver's `dark` context names it.
|
|
366
463
|
2. **Payload** — the base file's root `$extensions["com.nurix.design"]` present with: `schema` literal
|
|
367
|
-
`design-tokens/
|
|
464
|
+
`design-tokens/v2`; `dtcg` literal `2025.10`; `product` non-empty; `provenance` legal; `sourced` an
|
|
368
465
|
ISO date; `verbosity` / `feedback` / `density-tier` legal enum values (never hinted);
|
|
369
|
-
`product-traits` a non-empty array; `lever-manifest` carrying all six keys
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
466
|
+
`product-traits` a non-empty array; `lever-manifest` carrying all six keys; `theme-default` legal
|
|
467
|
+
when present and mirrored by the resolver's theme default; `anchors` an array of 2–3 non-empty
|
|
468
|
+
strings when present — **required for `provenance: project`** artifacts; the optional `look`
|
|
469
|
+
block enum-checked when present (theme / delight / resolution-mode enums, 2–3 named anchors,
|
|
470
|
+
the resolution shape).
|
|
471
|
+
3. **Primitive coverage** — all 27 `primitive.*` tokens present with the exact `$type` this schema
|
|
472
|
+
assigns, each assigned or hinted; `stagger-unit` within 0–60ms; fails naming every absent, empty,
|
|
473
|
+
or mistyped key.
|
|
474
|
+
4. **Color coverage** — all 36 `color.*` tokens present, `$type: "color"`, `colorSpace` one of
|
|
475
|
+
`oklch` | `srgb`, `.css` present (via the target when aliased), each assigned or hinted; fails
|
|
476
|
+
naming every missing token.
|
|
477
|
+
5. **Typography coverage** — `typography.family.heading|body|mono` present (`fontFamily`;
|
|
478
|
+
`family.display` optional but shape-checked when present);
|
|
479
|
+
`typography.ramp.h1|h2|h3|body|small|caption|display` present (`typography`), each composite
|
|
480
|
+
carrying all five sub-values.
|
|
377
481
|
6. **Alias integrity** — every `{path}` reference resolves within the artifact to a token of the
|
|
378
482
|
required type; no cycles.
|
|
379
483
|
7. **Manifest scales** — `spacing-scale`, `radius-scale`, `motion.durations` non-empty and ascending;
|
|
380
|
-
`motion.easings` non-empty; `shadow-levels` non-empty, drawn from the legal vocabulary
|
|
381
|
-
increasing in intensity; `icons.family` non-empty; `accent-count` ≥ 1.
|
|
484
|
+
`motion.easings` non-empty; `shadow-levels` non-empty, drawn from the legal vocabulary (`overlay`
|
|
485
|
+
included), strictly increasing in intensity; `icons.family` non-empty; `accent-count` ≥ 1.
|
|
382
486
|
8. **Manifest consistency** — the look's own base values sit on its own allowlist: `radius` ∈
|
|
383
487
|
`radius-scale`; `duration-fast` / `duration-normal` / `duration-slow` ∈ `motion.durations`;
|
|
384
|
-
`ease-default`'s serialization ∈ `motion.easings`.
|
|
488
|
+
`ease-default`'s serialization ∈ `motion.easings`. An off-manifest value is legal **only** with a
|
|
489
|
+
`deviation:` `$description` (the recorded-deviation rule).
|
|
385
490
|
9. **Parse-back** — for every token carrying `.css`, parsing the string reproduces `$value` and
|
|
386
|
-
re-serializing `$value` reproduces the byte form
|
|
491
|
+
re-serializing `$value` reproduces the byte form; a shadow `.css` parses back to its layer array
|
|
492
|
+
(the intensity `calc` wrapper strips to the layer alpha); a gradient `.css` contains each stop
|
|
493
|
+
color's byte form.
|
|
387
494
|
10. **Dark completeness** — the dark file, when present, carries exactly the `color` group with all
|
|
388
|
-
|
|
389
|
-
11. **Spec conformance** — only the
|
|
495
|
+
36 roles under the same shape rules (aliases legal), and nothing else.
|
|
496
|
+
11. **Spec conformance** — only the ten `$type` values this schema uses appear; no invented
|
|
390
497
|
`$type`; unknown `$extensions` namespaces are preserved, never authored.
|
|
391
498
|
12. **Emission precondition** — `--emit` runs only over a passing artifact and refuses while any
|
|
392
|
-
token carries a `hint`, naming every hinted path
|
|
499
|
+
token carries a `hint`, naming every hinted path (`--provisional` — golden/regression flows
|
|
500
|
+
only — emits provisional values instead).
|
|
501
|
+
13. **Elevation coverage** — all four `elevation.*` present, `$type: "shadow"`, ≥ 1 layer each,
|
|
502
|
+
every layer color rule-conformant (typed oklch|srgb; never pure black at alpha ≥ 0.15), lengths
|
|
503
|
+
dimension-shaped, the token-level `.css` present; each assigned or hinted; no level outside
|
|
504
|
+
`sm|md|lg|overlay`.
|
|
505
|
+
14. **Wash shape** — when the `wash` group exists: ≤ 2 tokens, slots ⊆ `surface|emphasis`,
|
|
506
|
+
`$type: "gradient"`, ≥ 2 stops each with a rule-conformant color and a position in 0–1, `.css`
|
|
507
|
+
present. Presence is never required.
|
|
508
|
+
15. **Provenance vocabulary** — a per-value `$description` opening with a tag uses a legal one
|
|
509
|
+
(`source:` / `brief:` / `deviation:` layer tags, or a mechanical-annotation prefix; untagged =
|
|
510
|
+
inherited); rule 8 enforces the off-manifest `deviation:` requirement.
|
|
511
|
+
|
|
512
|
+
Font binding and emitter completeness run at `--emit`, not `--check` — they compare the artifact
|
|
513
|
+
against emitted output and project dependencies (the emission contracts, not this file's gate).
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
## Migration — v1 artifacts to v2
|
|
518
|
+
|
|
519
|
+
`convert-design-md.mjs --upgrade-v2` is the mechanical, **idempotent** v1→v2 upgrade (it runs over
|
|
520
|
+
`data/design-tokens/*/`; the converter also applies the same transform to every fresh conversion):
|
|
521
|
+
|
|
522
|
+
1. `schema` literal → `design-tokens/v2`.
|
|
523
|
+
2. `elevation.*` derived from the v1 hardcoded slate recipes × the twin's `shadow-intensity` —
|
|
524
|
+
value-preserving (the emitted `--shadow-sm/md/lg` for an untouched twin are byte-identical before
|
|
525
|
+
and after migration, because the composites carry the recipe alphas and the intensity stays the
|
|
526
|
+
runtime `calc` multiplier), tagged `$description: "derived: v1 shadow-intensity migration"`.
|
|
527
|
+
Library entries whose design.md names real shadow values get **hinted** elevation instead,
|
|
528
|
+
resolved from the design.md prose at the data build.
|
|
529
|
+
3. `color.surface-raised` / `surface-overlay` → aliases to `{color.card}` / `{color.popover}` (in
|
|
530
|
+
the dark file too, where they resolve to the dark card/popover; the +4–6 L ladder is owed at
|
|
531
|
+
budget-time).
|
|
532
|
+
4. `typography.ramp.display` → **hinted** (provisional: h1 scaled ×1.75, weight from
|
|
533
|
+
`font-weight-strong`), hint text "display step unset — resolve from the product's design.md or
|
|
534
|
+
leave for budget-time". The Nurix default adopts the provisional derivation as its own look,
|
|
535
|
+
assigned.
|
|
536
|
+
5. `primitive.stagger-unit` → 30ms default (the prior derived clamp's value).
|
|
537
|
+
6. `theme-default` → `light` everywhere except twins whose design.md declares dark-first (only
|
|
538
|
+
twins carrying dark sets qualify; their design.md decides); the resolver's theme default mirrors
|
|
539
|
+
it.
|
|
540
|
+
7. Re-run parse-back verification + the v2 gate over every migrated twin.
|
|
541
|
+
|
|
542
|
+
Because migration is deterministic + hint-carrying, no hand-rewrite of the library is needed; hints
|
|
543
|
+
surface exactly where judgment is owed.
|
|
393
544
|
|
|
394
545
|
---
|
|
395
546
|
|
|
396
547
|
## Worked example — the Nurix default (abbreviated)
|
|
397
548
|
|
|
398
549
|
`data/design-tokens/nurix/design.tokens.json`, abbreviated to a representative subset of each token
|
|
399
|
-
family — the real file carries all
|
|
400
|
-
Nurix ships no dark set, so `design.tokens.dark.json` is absent and
|
|
401
|
-
`light` context.
|
|
550
|
+
family — the real file carries all 27 primitives, all 36 color roles, the full seven-step ramp, and
|
|
551
|
+
the four elevation composites. Nurix ships no dark set, so `design.tokens.dark.json` is absent and
|
|
552
|
+
the resolver carries only the `light` context.
|
|
402
553
|
|
|
403
554
|
```json
|
|
404
555
|
{
|
|
405
556
|
"$description": "Nurix default — the look a zero-input project wears. Abbreviated here; the shipped file is complete.",
|
|
406
557
|
"$extensions": {
|
|
407
558
|
"com.nurix.design": {
|
|
408
|
-
"schema": "design-tokens/
|
|
559
|
+
"schema": "design-tokens/v2",
|
|
409
560
|
"dtcg": "2025.10",
|
|
410
561
|
"product": "Nurix",
|
|
411
562
|
"provenance": "default",
|
|
412
563
|
"sourced": "2026-07-04",
|
|
564
|
+
"theme-default": "light",
|
|
413
565
|
"product-traits": ["operator-console", "crm", "queue", "dashboard", "admin"],
|
|
414
566
|
"verbosity": "default",
|
|
415
567
|
"feedback": "subtle",
|
|
@@ -417,7 +569,7 @@ Nurix ships no dark set, so `design.tokens.dark.json` is absent and the resolver
|
|
|
417
569
|
"lever-manifest": {
|
|
418
570
|
"spacing-scale": [2, 4, 8, 12, 16, 24, 32, 48, 64],
|
|
419
571
|
"radius-scale": [0, 4, 6, 12, 9999],
|
|
420
|
-
"shadow-levels": ["none", "sm", "md"],
|
|
572
|
+
"shadow-levels": ["none", "sm", "md", "lg", "overlay"],
|
|
421
573
|
"motion": { "durations": [100, 150, 250], "easings": ["cubic-bezier(0.4,0,0.2,1)"] },
|
|
422
574
|
"icons": { "family": "lucide" },
|
|
423
575
|
"accent-count": 1
|
|
@@ -435,44 +587,28 @@ Nurix ships no dark set, so `design.tokens.dark.json` is absent and the resolver
|
|
|
435
587
|
"$value": 0.4,
|
|
436
588
|
"$description": "brand is predominantly flat; shadow only lifts true overlays"
|
|
437
589
|
},
|
|
438
|
-
"border-weight": { "$type": "dimension", "$value": { "value": 1, "unit": "px" } },
|
|
439
|
-
"density-gap": { "$type": "number", "$value": 1 },
|
|
440
590
|
"font-sans": {
|
|
441
591
|
"$type": "fontFamily",
|
|
442
592
|
"$value": ["Inter", "system-ui", "sans-serif"],
|
|
443
593
|
"$description": "brand's body font; the single sans slot serves dense body/table chrome",
|
|
444
594
|
"$extensions": { "com.nurix.design": { "css": "Inter, system-ui, sans-serif" } }
|
|
445
595
|
},
|
|
446
|
-
"font-mono": {
|
|
447
|
-
"$type": "fontFamily",
|
|
448
|
-
"$value": ["Geist Mono", "ui-monospace", "SFMono-Regular", "monospace"],
|
|
449
|
-
"$description": "Geist monospace sibling — IDs, codes, log lines",
|
|
450
|
-
"$extensions": { "com.nurix.design": { "css": "Geist Mono, ui-monospace, SFMono-Regular, monospace" } }
|
|
451
|
-
},
|
|
452
|
-
"font-weight-normal": {
|
|
453
|
-
"$type": "fontWeight",
|
|
454
|
-
"$value": 400,
|
|
455
|
-
"$description": "brand base 500 is display-tuned; dense operator body reads cleaner at 400"
|
|
456
|
-
},
|
|
457
|
-
"font-weight-strong": { "$type": "fontWeight", "$value": 600 },
|
|
458
596
|
"heading-tracking": {
|
|
459
597
|
"$type": "number",
|
|
460
598
|
"$value": -0.01,
|
|
461
599
|
"$description": "em multiplier; brand display tracks 0 — at operator heading sizes Inter reads tighter",
|
|
462
600
|
"$extensions": { "com.nurix.design": { "css": "-0.01em" } }
|
|
463
601
|
},
|
|
464
|
-
"duration-unit": { "$type": "duration", "$value": { "value": 50, "unit": "ms" } },
|
|
465
602
|
"ease-default": {
|
|
466
603
|
"$type": "cubicBezier",
|
|
467
604
|
"$value": [0.4, 0, 0.2, 1],
|
|
468
605
|
"$extensions": { "com.nurix.design": { "css": "cubic-bezier(0.4,0,0.2,1)" } }
|
|
469
606
|
},
|
|
470
|
-
"
|
|
471
|
-
"$type": "
|
|
472
|
-
"$value":
|
|
473
|
-
"$description": "
|
|
607
|
+
"stagger-unit": {
|
|
608
|
+
"$type": "duration",
|
|
609
|
+
"$value": { "value": 30, "unit": "ms" },
|
|
610
|
+
"$description": "derived: v1→v2 migration — the 30ms default stagger base (0–60ms legal)"
|
|
474
611
|
},
|
|
475
|
-
"sidebar-width-expanded": { "$type": "dimension", "$value": { "value": 248, "unit": "px" } },
|
|
476
612
|
"table-row-height": {
|
|
477
613
|
"$type": "dimension",
|
|
478
614
|
"$value": { "value": 40, "unit": "px" },
|
|
@@ -485,42 +621,21 @@ Nurix ships no dark set, so `design.tokens.dark.json` is absent and the resolver
|
|
|
485
621
|
"$value": { "colorSpace": "oklch", "components": [1, 0, 0] },
|
|
486
622
|
"$extensions": { "com.nurix.design": { "css": "oklch(1 0 0)" } }
|
|
487
623
|
},
|
|
488
|
-
"foreground": {
|
|
489
|
-
"$type": "color",
|
|
490
|
-
"$value": { "colorSpace": "oklch", "components": [0.2077, 0.0398, 265.75] },
|
|
491
|
-
"$extensions": { "com.nurix.design": { "css": "oklch(0.2077 0.0398 265.75)" } }
|
|
492
|
-
},
|
|
493
|
-
"border": {
|
|
494
|
-
"$type": "color",
|
|
495
|
-
"$value": { "colorSpace": "oklch", "components": [0.9288, 0.0126, 255.51] },
|
|
496
|
-
"$description": "brand #E2E8F0 — every separator, grid line, and field edge",
|
|
497
|
-
"$extensions": { "com.nurix.design": { "css": "oklch(0.9288 0.0126 255.51)" } }
|
|
498
|
-
},
|
|
499
624
|
"primary": {
|
|
500
625
|
"$type": "color",
|
|
501
626
|
"$value": { "colorSpace": "oklch", "components": [0.5211, 0.2145, 263.89] },
|
|
502
627
|
"$description": "brand #245AE2 — the one accent",
|
|
503
628
|
"$extensions": { "com.nurix.design": { "css": "oklch(0.5211 0.2145 263.89)" } }
|
|
504
629
|
},
|
|
505
|
-
"
|
|
506
|
-
"$type": "color",
|
|
507
|
-
"$value": { "colorSpace": "oklch", "components": [1, 0, 0] },
|
|
508
|
-
"$extensions": { "com.nurix.design": { "css": "oklch(1 0 0)" } }
|
|
509
|
-
},
|
|
510
|
-
"warning": {
|
|
511
|
-
"$type": "color",
|
|
512
|
-
"$value": { "colorSpace": "oklch", "components": [0.6658, 0.1574, 58.32] },
|
|
513
|
-
"$extensions": { "com.nurix.design": { "css": "oklch(0.6658 0.1574 58.32)" } }
|
|
514
|
-
},
|
|
515
|
-
"chart-2": {
|
|
630
|
+
"surface-raised": {
|
|
516
631
|
"$type": "color",
|
|
517
|
-
"$value": {
|
|
518
|
-
"$
|
|
632
|
+
"$value": "{color.card}",
|
|
633
|
+
"$description": "derived: v1→v2 migration — light aliases card; dark looks carry the surface-lightness ladder"
|
|
519
634
|
},
|
|
520
|
-
"
|
|
635
|
+
"surface-overlay": {
|
|
521
636
|
"$type": "color",
|
|
522
|
-
"$value": {
|
|
523
|
-
"$
|
|
637
|
+
"$value": "{color.popover}",
|
|
638
|
+
"$description": "derived: v1→v2 migration — light aliases popover; dark looks carry the surface-lightness ladder"
|
|
524
639
|
}
|
|
525
640
|
},
|
|
526
641
|
"typography": {
|
|
@@ -540,29 +655,29 @@ Nurix ships no dark set, so `design.tokens.dark.json` is absent and the resolver
|
|
|
540
655
|
"letterSpacing": "{primitive.heading-tracking}"
|
|
541
656
|
}
|
|
542
657
|
},
|
|
543
|
-
"
|
|
544
|
-
"$type": "typography",
|
|
545
|
-
"$value": {
|
|
546
|
-
"fontFamily": "{typography.family.body}",
|
|
547
|
-
"fontSize": { "value": 14, "unit": "px" },
|
|
548
|
-
"lineHeight": 1.45,
|
|
549
|
-
"fontWeight": "{primitive.font-weight-normal}",
|
|
550
|
-
"letterSpacing": 0
|
|
551
|
-
}
|
|
552
|
-
},
|
|
553
|
-
"caption": {
|
|
658
|
+
"display": {
|
|
554
659
|
"$type": "typography",
|
|
555
660
|
"$value": {
|
|
556
|
-
"fontFamily": "{typography.family.
|
|
557
|
-
"fontSize": { "value":
|
|
558
|
-
"lineHeight": 1.
|
|
661
|
+
"fontFamily": "{typography.family.heading}",
|
|
662
|
+
"fontSize": { "value": 42, "unit": "px" },
|
|
663
|
+
"lineHeight": 1.25,
|
|
559
664
|
"fontWeight": "{primitive.font-weight-strong}",
|
|
560
|
-
"letterSpacing":
|
|
665
|
+
"letterSpacing": "{primitive.heading-tracking}"
|
|
561
666
|
},
|
|
562
|
-
"$description": "
|
|
563
|
-
"$extensions": { "com.nurix.design": { "css": { "letterSpacing": "0.02em" } } }
|
|
667
|
+
"$description": "derived: v2 display step — the provisional h1 × 1.75 derivation adopted as the default's own look (KPI numerals, the one focal metric)"
|
|
564
668
|
}
|
|
565
669
|
}
|
|
670
|
+
},
|
|
671
|
+
"elevation": {
|
|
672
|
+
"sm": {
|
|
673
|
+
"$type": "shadow",
|
|
674
|
+
"$value": [
|
|
675
|
+
{ "color": { "colorSpace": "srgb", "components": [0.05882, 0.0902, 0.16471], "alpha": 0.1, "hex": "#0f172a" }, "offsetX": { "value": 0, "unit": "px" }, "offsetY": { "value": 1, "unit": "px" }, "blur": { "value": 3, "unit": "px" }, "spread": { "value": 0, "unit": "px" } },
|
|
676
|
+
{ "color": { "colorSpace": "srgb", "components": [0.05882, 0.0902, 0.16471], "alpha": 0.1, "hex": "#0f172a" }, "offsetX": { "value": 0, "unit": "px" }, "offsetY": { "value": 1, "unit": "px" }, "blur": { "value": 2, "unit": "px" }, "spread": { "value": -1, "unit": "px" } }
|
|
677
|
+
],
|
|
678
|
+
"$description": "derived: v1 shadow-intensity migration",
|
|
679
|
+
"$extensions": { "com.nurix.design": { "css": "0 1px 3px 0 rgb(15 23 42 / calc(0.10 * var(--shadow-intensity))), 0 1px 2px -1px rgb(15 23 42 / calc(0.10 * var(--shadow-intensity)))" } }
|
|
680
|
+
}
|
|
566
681
|
}
|
|
567
682
|
}
|
|
568
683
|
```
|