@navecss/tokens 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,7 +26,7 @@ This emits the token layer's custom properties (`--nave-*`) inside `@layer token
26
26
 
27
27
  ## What ships
28
28
 
29
- - **Colour** — the semantic colours consumers use (`action`, `content`, `surface`, `border`, `feedback`, and foregrounds for the primary action and the feedback roles), each with a light and a dark value through `light-dark()`. The `neutral`, `primary` and `danger` scales they are derived from stay inside the build: no step of them is emitted as a custom property. The neutral colours re-hue live from one custom property, under Theming below.
29
+ - **Colour** — the semantic colours consumers use (`action`, `content`, `surface`, `border`, `feedback`, a scrim for behind a modal panel (`backdrop`), and foregrounds for the primary action and the feedback roles), each with a light and a dark value through `light-dark()`. The `neutral`, `primary` and `danger` scales they are derived from stay inside the build: no step of them is emitted as a custom property. The neutral colours re-hue live from one custom property, under Theming below.
30
30
  - **Typography, spacing, size, shape, shadow, motion, z-index, opacity** — the rest of the token taxonomy, authored once in `tokens.json` and compiled by the first-party DTCG 2025.10 reader.
31
31
  - **Breakpoints** — a JS-only export (`@navecss/tokens/breakpoints`): custom properties cannot hold media-query ranges, so these ship as typed constants and prebuilt Level 4 media-query strings instead.
32
32
 
package/TOKENS.md CHANGED
@@ -167,6 +167,9 @@ non-colour value is exactly what its name says.
167
167
  | `--nave-color-action-primary-active` | no |
168
168
  | `--nave-color-on-action-primary` | yes |
169
169
  | `--nave-color-action-secondary` | yes |
170
+ | `--nave-color-action-secondary-hover` | yes |
171
+ | `--nave-color-action-secondary-active` | yes |
172
+ | `--nave-color-action-highlight` | yes |
170
173
  | `--nave-color-feedback-danger` | no |
171
174
  | `--nave-color-feedback-danger-foreground` | no |
172
175
  | `--nave-color-on-feedback-danger` | yes |
@@ -179,3 +182,4 @@ non-colour value is exactly what its name says.
179
182
  | `--nave-color-feedback-info` | yes |
180
183
  | `--nave-color-feedback-info-foreground` | yes |
181
184
  | `--nave-color-on-feedback-info` | yes |
185
+ | `--nave-color-backdrop-modal` | yes |
@@ -2,7 +2,7 @@
2
2
  "formatVersion": 1,
3
3
  "producer": {
4
4
  "name": "@navecss/core",
5
- "version": "0.3.0"
5
+ "version": "0.5.0"
6
6
  },
7
7
  "tokens": [
8
8
  "--nave-border-width-focus",
package/dist/css.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {}
@@ -46,6 +46,11 @@ export const ADJACENCY_EXCLUSIONS = {
46
46
  'and is not answered here.',
47
47
  'border.default': 'decorative divider: excluded as a decorative divider or card edge, and the ' +
48
48
  'exclusion is void the moment such a border is applied to a control.',
49
+ 'backdrop.modal': 'scrim: excluded from the checked pair set because it is translucent, so the colour ' +
50
+ 'it renders is a blend with whatever the page shows beneath it, which no pair in this ' +
51
+ 'list can describe. The exclusion holds only while the scrim is painted behind a fully ' +
52
+ 'modal panel, over a page the reader cannot reach; it is void the moment text, a ' +
53
+ 'control or a focus indicator the reader can reach is drawn on it.',
49
54
  };
50
55
  export const ADJACENCY_OPEN = {
51
56
  'border.strong': 'open: whether this border can identify a control, and therefore whether its ' +
@@ -1,7 +1,8 @@
1
1
  import { type Adjacency } from './adjacency-source.ts';
2
+ import { type SlotMapping } from './semantics.ts';
2
3
  export type { Adjacency, MeasuredOpenSlots, OrphanCheckResult } from './adjacency-source.ts';
3
4
  export { ADJACENCY, ADJACENCY_EXCLUSIONS, ADJACENCY_OPEN, assertNoOrphanedSemanticSlot, } from './adjacency-source.ts';
4
- declare const MINIMUM_COVERAGE_CATEGORIES: readonly ["contentOnSurface", "inversePair", "onActionOnAction", "textOnFeedback", "sunken", "borderControl", "borderFocus", "filledAction", "filledFeedback"];
5
+ declare const MINIMUM_COVERAGE_CATEGORIES: readonly ["contentOnSurface", "inversePair", "onActionOnAction", "textOnFeedback", "sunken", "borderControl", "borderFocus", "filledAction", "filledFeedback", "secondaryForegroundOnStates"];
5
6
  export type CoverageCategory = (typeof MINIMUM_COVERAGE_CATEGORIES)[number];
6
7
  export declare function coverageCategories(adjacency?: readonly Adjacency[]): Record<CoverageCategory, boolean>;
7
8
  export declare function assertCoverageFloor(adjacency?: readonly Adjacency[]): void;
@@ -15,3 +16,4 @@ export interface ForbiddenAdjacencyRule {
15
16
  export declare function findForbiddenAdjacencyRule(subject: string, against: string): ForbiddenAdjacencyRule | undefined;
16
17
  export declare function assertNoForbiddenAdjacency(adjacency?: readonly Adjacency[]): void;
17
18
  export declare function assertNoFocusableAdjacentToActionFill(adjacency?: readonly Adjacency[]): void;
19
+ export declare function assertNoTranslucentSlotInDeclaredPair(adjacency?: readonly Adjacency[], mapping?: Readonly<Record<string, SlotMapping>>, achromaticMapping?: Readonly<Record<string, SlotMapping>>): void;
@@ -1,5 +1,6 @@
1
1
  import { ADJACENCY } from "./adjacency-source.js";
2
- import { SEMANTIC_SLOTS } from "./semantics.js";
2
+ import { roundedAlpha } from "./neutral.js";
3
+ import { ACHROMATIC_MAPPING, CHROMATIC_MAPPING, SEMANTIC_SLOTS, } from "./semantics.js";
3
4
  export { ADJACENCY, ADJACENCY_EXCLUSIONS, ADJACENCY_OPEN, assertNoOrphanedSemanticSlot, } from "./adjacency-source.js";
4
5
  const MINIMUM_COVERAGE_CATEGORIES = [
5
6
  'contentOnSurface',
@@ -11,7 +12,9 @@ const MINIMUM_COVERAGE_CATEGORIES = [
11
12
  'borderFocus',
12
13
  'filledAction',
13
14
  'filledFeedback',
15
+ 'secondaryForegroundOnStates',
14
16
  ];
17
+ const ACTION_SECONDARY_STATE_FILLS = ['action.secondary.hover', 'action.secondary.active'];
15
18
  export function coverageCategories(adjacency = ADJACENCY) {
16
19
  return {
17
20
  contentOnSurface: adjacency.some((a) => a.subject.startsWith('content.') && a.against.startsWith('surface.')),
@@ -23,6 +26,7 @@ export function coverageCategories(adjacency = ADJACENCY) {
23
26
  borderFocus: adjacency.some((a) => a.subject === 'border.focus'),
24
27
  filledAction: adjacency.some((a) => a.subject === 'action.primary' && a.class === 'non-text'),
25
28
  filledFeedback: adjacency.some((a) => a.subject === 'feedback.danger' && a.class === 'non-text'),
29
+ secondaryForegroundOnStates: ACTION_SECONDARY_STATE_FILLS.every((fill) => adjacency.some((a) => a.subject === 'action.secondary.foreground' && a.against === fill && a.class === 'text')),
26
30
  };
27
31
  }
28
32
  const COVERAGE_FLOOR_VIOLATION_REASON = 'missing from the declared adjacency set. This set has a required minimum: every ' +
@@ -131,3 +135,39 @@ export function assertNoFocusableAdjacentToActionFill(adjacency = ADJACENCY) {
131
135
  }
132
136
  }
133
137
  }
138
+ const TRANSLUCENT_SLOT_IN_PAIR_REASON = 'The contrast harness reads opaque colours only, and a translucent slot renders as a ' +
139
+ 'blend with whatever lies beneath it, which no pair can describe. This rule records a ' +
140
+ 'decision about which colour pairs this design system will declare and test; it is not ' +
141
+ 'a check to silence or narrow to make a build pass. Remove the declaration, or, if you ' +
142
+ 'need this pair, open an issue describing it and why, and leave it out of the pull ' +
143
+ 'request until that issue is answered.';
144
+ const MAX_ALIAS_DEPTH = 10;
145
+ function isTranslucentWhenResolved(ref, scheme, lookup) {
146
+ let current = ref;
147
+ for (let depth = 0; depth < MAX_ALIAS_DEPTH; depth++) {
148
+ if (!('alias' in current))
149
+ return current.alpha !== undefined && roundedAlpha(current.alpha) < 1;
150
+ current = lookup(current.alias)[scheme];
151
+ }
152
+ throw new Error('Alias chain too deep — likely a cycle in the semantic mapping.');
153
+ }
154
+ function translucentSlots(mapping, achromaticMapping) {
155
+ const lookup = (isAchromaticBranch) => (slot) => {
156
+ const resolved = (isAchromaticBranch ? achromaticMapping[slot] : undefined) ?? mapping[slot];
157
+ if (!resolved)
158
+ throw new Error(`Unknown semantic slot: ${slot}`);
159
+ return resolved;
160
+ };
161
+ return new Set(Object.keys(mapping).filter((slot) => [false, true].some((isAchromaticBranch) => ['light', 'dark'].some((scheme) => isTranslucentWhenResolved(lookup(isAchromaticBranch)(slot)[scheme], scheme, lookup(isAchromaticBranch))))));
162
+ }
163
+ export function assertNoTranslucentSlotInDeclaredPair(adjacency = ADJACENCY, mapping = CHROMATIC_MAPPING, achromaticMapping = ACHROMATIC_MAPPING) {
164
+ const translucent = translucentSlots(mapping, achromaticMapping);
165
+ for (const pair of adjacency) {
166
+ for (const slot of [pair.subject, pair.against]) {
167
+ if (translucent.has(slot)) {
168
+ throw new Error(`Adjacency rule violation: the translucent slot "${slot}" is declared in the pair ` +
169
+ `(${pair.subject}, ${pair.against}). ${TRANSLUCENT_SLOT_IN_PAIR_REASON}`);
170
+ }
171
+ }
172
+ }
173
+ }
@@ -1,6 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import { collectDescriptionsFromSources } from "../dtcg-descriptions.js";
3
- import { assertCoverageFloor, assertNoFocusableAdjacentToActionFill, assertNoForbiddenAdjacency, assertNoOrphanedSemanticSlot, assertShippedSurfaceCountProvenance, } from "./adjacency.js";
3
+ import { assertCoverageFloor, assertNoFocusableAdjacentToActionFill, assertNoForbiddenAdjacency, assertNoOrphanedSemanticSlot, assertNoTranslucentSlotInDeclaredPair, assertShippedSurfaceCountProvenance, } from "./adjacency.js";
4
4
  import { composeBuildRecord } from "./build-record.js";
5
5
  import { composeContactSheet } from "./contact-sheet.js";
6
6
  import { assertContrastFloors, assertFloorProvenance, assertNoSameStepViolations, checkSameStepLint, runContrastHarness, } from "./contrast.js";
@@ -20,6 +20,7 @@ import { TOKENS_JSON_PATH } from "./tokens-source.js";
20
20
  function runSourceGuards(tokensSourcePaths) {
21
21
  assertNoForbiddenAdjacency();
22
22
  assertNoFocusableAdjacentToActionFill();
23
+ assertNoTranslucentSlotInDeclaredPair();
23
24
  assertCoverageFloor();
24
25
  const orphanCheck = assertNoOrphanedSemanticSlot(SEMANTIC_SLOTS);
25
26
  assertOnStarShape();
@@ -1,4 +1,12 @@
1
1
  export const SLOT_DESCRIPTIONS = new Map([
2
+ [
3
+ 'action.highlight',
4
+ 'Fill for a control under the pointer, the keyboard or a press: a highlighted list or menu item, or a ghost button. Never the only cue for focus or for a state such as selected, checked, open or current, and not for disabled controls.',
5
+ ],
6
+ [
7
+ 'backdrop.modal',
8
+ 'Translucent layer between a modal panel and the page, which it recedes. Only behind a fully modal panel; never under text, a control or a focus indicator the reader can reach.',
9
+ ],
2
10
  [
3
11
  'border.control',
4
12
  'Identifying boundary for a control (inputs, form boundaries, and the secondary action control).',
@@ -1,4 +1,4 @@
1
- import { ADJACENCY, assertCoverageFloor, assertNoFocusableAdjacentToActionFill, assertNoForbiddenAdjacency, assertNoOrphanedSemanticSlot, } from "./adjacency.js";
1
+ import { ADJACENCY, assertCoverageFloor, assertNoFocusableAdjacentToActionFill, assertNoForbiddenAdjacency, assertNoOrphanedSemanticSlot, assertNoTranslucentSlotInDeclaredPair, } from "./adjacency.js";
2
2
  import { assertContrastFloors, assertFloorProvenance, assertNoSameStepViolations, checkSameStepLint, runContrastHarness, } from "./contrast.js";
3
3
  import { assertNoticeIsEmitted, RETHEMING_NOTICE } from "./copy-lint.js";
4
4
  import { TINT_SEED_COMMENT_STEM } from "./emit.js";
@@ -76,6 +76,13 @@ export const ACCESSIBILITY_GUARD_MESSAGE_PROBES = [
76
76
  { subject: 'border.focus', against: 'action.primary', class: 'non-text' },
77
77
  ])),
78
78
  },
79
+ {
80
+ label: 'the translucent-slot-in-a-declared-pair output',
81
+ capture: () => capturedFailureMessage(() => assertNoTranslucentSlotInDeclaredPair([
82
+ ...ADJACENCY,
83
+ { subject: 'content.primary', against: 'backdrop.modal', class: 'text' },
84
+ ])),
85
+ },
79
86
  {
80
87
  label: "the same-step lint's real violation output (build-step.ts's runResultGuards)",
81
88
  capture: () => {
@@ -9,4 +9,5 @@ export interface NeutralStep {
9
9
  }
10
10
  export declare function generateNeutralRampLiterals(ceiling?: number): readonly NeutralStep[];
11
11
  export declare function resolveNeutralStep(step: number, hue: number, literals?: readonly NeutralStep[]): Oklch;
12
- export declare function neutralRcsFormula(step: number, literals: readonly NeutralStep[]): string;
12
+ export declare function neutralRcsFormula(step: number, literals: readonly NeutralStep[], alpha?: number): string;
13
+ export declare function roundedAlpha(alpha: number): number;
@@ -64,11 +64,17 @@ export function resolveNeutralStep(step, hue, literals = generateNeutralRampLite
64
64
  throw new Error(`Neutral step ${step} is not in the shared step table.`);
65
65
  return { l: found.l, c: found.c, h: hue };
66
66
  }
67
- export function neutralRcsFormula(step, literals) {
67
+ export function neutralRcsFormula(step, literals, alpha = 1) {
68
68
  const found = literals.find((s) => s.step === step);
69
69
  if (!found)
70
70
  throw new Error(`Neutral step ${step} is not in the shared step table.`);
71
- return `oklch(from var(--nave-color-tint) ${roundTo(found.l, 4)} ${roundTo(found.c, 4)} h / 1)`;
71
+ if (!Number.isFinite(alpha) || alpha < 0 || alpha > 1) {
72
+ throw new Error(`The alpha for neutral step ${step} must be a finite number from 0 to 1, got ${alpha}.`);
73
+ }
74
+ return `oklch(from var(--nave-color-tint) ${roundTo(found.l, 4)} ${roundTo(found.c, 4)} h / ${roundedAlpha(alpha)})`;
75
+ }
76
+ export function roundedAlpha(alpha) {
77
+ return roundTo(alpha, 4);
72
78
  }
73
79
  function roundTo(n, d) {
74
80
  const f = 10 ** d;
@@ -14,13 +14,15 @@ interface PipelineRecords {
14
14
  achromaticBranch: boolean;
15
15
  tintHue: number;
16
16
  }
17
+ interface ResolvedStep {
18
+ scale: 'danger' | 'neutral' | 'primary';
19
+ step: number;
20
+ alpha?: number;
21
+ }
17
22
  export interface ResolvedSlot {
18
23
  slot: string;
19
24
  branch: 'dark' | 'light';
20
- resolved: {
21
- scale: 'danger' | 'neutral' | 'primary';
22
- step: number;
23
- };
25
+ resolved: ResolvedStep;
24
26
  literal: Oklch;
25
27
  css: string;
26
28
  }
@@ -16,7 +16,13 @@ function resolveStepRef(ref, branch, isAchromaticBranch) {
16
16
  function cssForResolved(resolved, ctx) {
17
17
  if (resolved.scale === 'neutral') {
18
18
  const colour = resolveNeutralStep(resolved.step, ctx.tintHue, ctx.neutralLiterals);
19
- return { css: neutralRcsFormula(resolved.step, ctx.neutralLiterals), literal: colour };
19
+ return {
20
+ css: neutralRcsFormula(resolved.step, ctx.neutralLiterals, resolved.alpha),
21
+ literal: colour,
22
+ };
23
+ }
24
+ if (resolved.alpha !== undefined) {
25
+ throw new Error(`The ${resolved.scale} scale has no translucent form: alpha is written on neutral steps only.`);
20
26
  }
21
27
  const ramp = resolved.scale === 'primary' ? ctx.primaryRamp : ctx.dangerRamp;
22
28
  const colour = rampStep(ramp, resolved.step);
@@ -1,5 +1,6 @@
1
1
  type Scale = 'neutral' | 'primary' | 'danger';
2
2
  export type StepRef = {
3
+ alpha?: number;
3
4
  scale: Scale;
4
5
  step: number;
5
6
  } | {
@@ -7,6 +7,11 @@ export const ACHROMATIC_BRANCH_SLOTS = [
7
7
  'border.focus',
8
8
  ];
9
9
  const ref = (scale, step) => ({ scale, step });
10
+ const translucentRef = (step, alpha) => ({
11
+ scale: 'neutral',
12
+ step,
13
+ alpha,
14
+ });
10
15
  const aliasRef = (slot) => ({ alias: slot });
11
16
  export const CHROMATIC_MAPPING = {
12
17
  'surface.base': { light: ref('neutral', 0), dark: ref('neutral', 900) },
@@ -30,6 +35,9 @@ export const CHROMATIC_MAPPING = {
30
35
  'action.primary.active': { light: ref('primary', 800), dark: ref('primary', 150) },
31
36
  'on-action.primary': { light: ref('neutral', 0), dark: ref('neutral', 900) },
32
37
  'action.secondary': { light: ref('neutral', 100), dark: ref('neutral', 800) },
38
+ 'action.secondary.hover': { light: ref('neutral', 150), dark: ref('neutral', 700) },
39
+ 'action.secondary.active': { light: ref('neutral', 200), dark: ref('neutral', 850) },
40
+ 'action.highlight': { light: ref('neutral', 100), dark: ref('neutral', 700) },
33
41
  'feedback.danger': { light: ref('danger', 600), dark: ref('danger', 300) },
34
42
  'feedback.danger.foreground': { light: ref('danger', 600), dark: ref('danger', 300) },
35
43
  'on-feedback.danger': { light: ref('neutral', 0), dark: ref('neutral', 900) },
@@ -48,6 +56,7 @@ export const CHROMATIC_MAPPING = {
48
56
  dark: aliasRef('feedback.warning.foreground'),
49
57
  },
50
58
  'on-feedback.info': { light: ref('neutral', 0), dark: ref('neutral', 900) },
59
+ 'backdrop.modal': { light: translucentRef(950, 0.3), dark: translucentRef(950, 0.6) },
51
60
  };
52
61
  export const ACHROMATIC_MAPPING = {
53
62
  'action.primary': { light: ref('neutral', 800), dark: ref('neutral', 200) },
@@ -180,6 +180,32 @@
180
180
  "$type": "color",
181
181
  "$value": "oklch(from var(--nave-color-tint) 0.27 0.0067 h / 1)"
182
182
  },
183
+ "color.action.secondary.hover.light": {
184
+ "$type": "color",
185
+ "$value": "oklch(from var(--nave-color-tint) 0.94 0.0067 h / 1)"
186
+ },
187
+ "color.action.secondary.hover.dark": {
188
+ "$type": "color",
189
+ "$value": "oklch(from var(--nave-color-tint) 0.37 0.0067 h / 1)"
190
+ },
191
+ "color.action.secondary.active.light": {
192
+ "$type": "color",
193
+ "$value": "oklch(from var(--nave-color-tint) 0.905 0.0067 h / 1)"
194
+ },
195
+ "color.action.secondary.active.dark": {
196
+ "$type": "color",
197
+ "$value": "oklch(from var(--nave-color-tint) 0.225 0.0067 h / 1)"
198
+ },
199
+ "color.action.highlight.light": {
200
+ "$type": "color",
201
+ "$value": "oklch(from var(--nave-color-tint) 0.965 0.0067 h / 1)",
202
+ "$description": "Fill for a control under the pointer, the keyboard or a press: a highlighted list or menu item, or a ghost button. Never the only cue for focus or for a state such as selected, checked, open or current, and not for disabled controls."
203
+ },
204
+ "color.action.highlight.dark": {
205
+ "$type": "color",
206
+ "$value": "oklch(from var(--nave-color-tint) 0.37 0.0067 h / 1)",
207
+ "$description": "Fill for a control under the pointer, the keyboard or a press: a highlighted list or menu item, or a ghost button. Never the only cue for focus or for a state such as selected, checked, open or current, and not for disabled controls."
208
+ },
183
209
  "color.feedback.danger.light": {
184
210
  "$type": "color",
185
211
  "$value": "oklch(0.47 0.1455 15.02)",
@@ -289,5 +315,15 @@
289
315
  "color.on-feedback.info.dark": {
290
316
  "$type": "color",
291
317
  "$value": "oklch(from var(--nave-color-tint) 0.18 0.0067 h / 1)"
318
+ },
319
+ "color.backdrop.modal.light": {
320
+ "$type": "color",
321
+ "$value": "oklch(from var(--nave-color-tint) 0.12 0.0067 h / 0.3)",
322
+ "$description": "Translucent layer between a modal panel and the page, which it recedes. Only behind a fully modal panel; never under text, a control or a focus indicator the reader can reach."
323
+ },
324
+ "color.backdrop.modal.dark": {
325
+ "$type": "color",
326
+ "$value": "oklch(from var(--nave-color-tint) 0.12 0.0067 h / 0.6)",
327
+ "$description": "Translucent layer between a modal panel and the page, which it recedes. Only behind a fully modal panel; never under text, a control or a focus indicator the reader can reach."
292
328
  }
293
329
  }
package/dist/tokens.css CHANGED
@@ -423,6 +423,9 @@
423
423
  --nave-color-action-primary-active: light-dark(oklch(0.27 0.0398 186.17), oklch(0.94 0.0574 186.17));
424
424
  --nave-color-on-action-primary: light-dark(oklch(from var(--nave-color-tint) 1 0 h / 1), oklch(from var(--nave-color-tint) 0.18 0.0067 h / 1));
425
425
  --nave-color-action-secondary: light-dark(oklch(from var(--nave-color-tint) 0.965 0.0067 h / 1), oklch(from var(--nave-color-tint) 0.27 0.0067 h / 1));
426
+ --nave-color-action-secondary-hover: light-dark(oklch(from var(--nave-color-tint) 0.94 0.0067 h / 1), oklch(from var(--nave-color-tint) 0.37 0.0067 h / 1));
427
+ --nave-color-action-secondary-active: light-dark(oklch(from var(--nave-color-tint) 0.905 0.0067 h / 1), oklch(from var(--nave-color-tint) 0.225 0.0067 h / 1));
428
+ --nave-color-action-highlight: light-dark(oklch(from var(--nave-color-tint) 0.965 0.0067 h / 1), oklch(from var(--nave-color-tint) 0.37 0.0067 h / 1));
426
429
  --nave-color-feedback-danger: light-dark(oklch(0.47 0.1455 15.02), oklch(0.835 0.0661 15.02));
427
430
  --nave-color-feedback-danger-foreground: light-dark(oklch(0.47 0.1455 15.02), oklch(0.835 0.0661 15.02));
428
431
  --nave-color-on-feedback-danger: light-dark(oklch(from var(--nave-color-tint) 1 0 h / 1), oklch(from var(--nave-color-tint) 0.18 0.0067 h / 1));
@@ -436,5 +439,6 @@
436
439
  --nave-color-feedback-info: light-dark(oklch(from var(--nave-color-tint) 0.47 0.0067 h / 1), oklch(from var(--nave-color-tint) 0.835 0.0067 h / 1));
437
440
  --nave-color-feedback-info-foreground: light-dark(oklch(from var(--nave-color-tint) 0.47 0.0067 h / 1), oklch(from var(--nave-color-tint) 0.835 0.0067 h / 1));
438
441
  --nave-color-on-feedback-info: light-dark(oklch(from var(--nave-color-tint) 1 0 h / 1), oklch(from var(--nave-color-tint) 0.18 0.0067 h / 1));
442
+ --nave-color-backdrop-modal: light-dark(oklch(from var(--nave-color-tint) 0.12 0.0067 h / 0.3), oklch(from var(--nave-color-tint) 0.12 0.0067 h / 0.6));
439
443
  }
440
444
  }
package/dist/tokens.d.ts CHANGED
@@ -99,10 +99,14 @@ export declare type TokenValue = typeof tokens[TokenName]
99
99
  * `TokenName | ColorPropertyName`.
100
100
  */
101
101
  export declare type ColorPropertyName =
102
+ | '--nave-color-action-highlight'
102
103
  | '--nave-color-action-primary'
103
104
  | '--nave-color-action-primary-active'
104
105
  | '--nave-color-action-primary-hover'
105
106
  | '--nave-color-action-secondary'
107
+ | '--nave-color-action-secondary-active'
108
+ | '--nave-color-action-secondary-hover'
109
+ | '--nave-color-backdrop-modal'
106
110
  | '--nave-color-border-control'
107
111
  | '--nave-color-border-default'
108
112
  | '--nave-color-border-disabled'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@navecss/tokens",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "DTCG 2025.10 design tokens for Nave: seed-derived semantic colours, typography, spacing, and the rest of the token layer, built by a first-party DTCG 2025.10 reader.",
5
5
  "keywords": [
6
6
  "css",
@@ -24,7 +24,10 @@
24
24
  "import": "./dist/tokens.js",
25
25
  "default": "./dist/tokens.js"
26
26
  },
27
- "./css": "./dist/tokens.css",
27
+ "./css": {
28
+ "types": "./dist/css.d.ts",
29
+ "default": "./dist/tokens.css"
30
+ },
28
31
  "./js": {
29
32
  "types": "./dist/tokens.d.ts",
30
33
  "import": "./dist/tokens.js",
@@ -51,11 +54,11 @@
51
54
  "TOKENS.md"
52
55
  ],
53
56
  "devDependencies": {
54
- "@types/node": "^26.6.4",
55
- "@vitest/coverage-v8": "5.0.3",
56
- "prettier": "3.9.9",
57
+ "@types/node": "^26.6.6",
58
+ "@vitest/coverage-v8": "5.0.5",
59
+ "prettier": "3.9.10",
57
60
  "typescript": "6.0.3",
58
- "vitest": "5.0.3"
61
+ "vitest": "5.0.5"
59
62
  },
60
63
  "engines": {
61
64
  "node": ">=22.18"
@@ -79,6 +82,6 @@
79
82
  "lint": "eslint build.ts generate-tokens-doc.ts src test --max-warnings 0",
80
83
  "lint:fix": "eslint build.ts generate-tokens-doc.ts src test --fix",
81
84
  "test": "vitest run",
82
- "check:pack": "node ../../scripts/without-dry-run.mjs publint && node ../../scripts/without-dry-run.mjs attw --pack . --profile esm-only --exclude-entrypoints css"
85
+ "check:pack": "node ../../scripts/without-dry-run.mjs publint && node ../../scripts/without-dry-run.mjs attw --pack . --profile esm-only"
83
86
  }
84
87
  }
package/tokens.json CHANGED
@@ -37,6 +37,10 @@
37
37
  {
38
38
  "against": "surface.overlay",
39
39
  "class": "text"
40
+ },
41
+ {
42
+ "against": "action.highlight",
43
+ "class": "text"
40
44
  }
41
45
  ],
42
46
  "content.secondary": [
@@ -51,6 +55,10 @@
51
55
  {
52
56
  "against": "surface.overlay",
53
57
  "class": "text"
58
+ },
59
+ {
60
+ "against": "action.highlight",
61
+ "class": "text"
54
62
  }
55
63
  ],
56
64
  "content.tertiary": [
@@ -81,6 +89,14 @@
81
89
  {
82
90
  "against": "action.secondary",
83
91
  "class": "text"
92
+ },
93
+ {
94
+ "against": "action.secondary.hover",
95
+ "class": "text"
96
+ },
97
+ {
98
+ "against": "action.secondary.active",
99
+ "class": "text"
84
100
  }
85
101
  ],
86
102
  "content.link": [