remarque-tokens 0.12.0 → 0.14.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/CHANGELOG.md CHANGED
@@ -4,6 +4,70 @@ All notable changes to `remarque-tokens` are documented here. Token value
4
4
  changes always state the design rationale — downstream sites pin against
5
5
  these entries when syncing.
6
6
 
7
+ ## 0.14.0 — 2026-07-23
8
+
9
+ Triage bundle — prose/`.content-reading` pairing documented, `--motion-easing` bridged into the Tailwind v4 utility layer, USWDS/WCAG source citations added to the accessibility section (closes #23, closes #31, closes #32).
10
+
11
+ ### Added
12
+ - **`ease-remarque` Tailwind v4 utility** — `theme.css`'s `@theme inline` block now maps `--ease-remarque: var(--motion-easing)`, mirroring the `transitionTimingFunction.remarque` mapping the v3 `tailwind.config.js` already carried. Utility-class users of `duration-[var(--motion-fast)]` previously fell back to Tailwind's default `ease` timing function instead of the token (#31).
13
+ - **Demo site** — every `duration-[var(--motion-fast)]` call site (22, across `site/src/pages/*`, `layouts/BaseLayout.astro`, `components/Meta.astro`) now pairs with `ease-remarque` so the rendered CSS carries `var(--motion-easing)` instead of Tailwind's default.
14
+
15
+ ### Documentation
16
+ - **REMARQUE.md "Editorial Microtypography"** — states that `.remarque-prose` is typography-only (font, line height, numeral register, optical wrapping) and does not center the column; pair it with `.content-reading`, with a minimal code sample (#23).
17
+ - **REMARQUE.md "USWDS Accessibility Compliance"** — every cited WCAG 2.1 success criterion (1.4.3 Contrast Minimum, 1.4.11 Non-text Contrast, 2.5.5 Target Size, 2.3.3 Animation from Interactions, the last in the adjacent "Motion Rules" section) and USWDS claim (16px font-size floor, the overall accessibility approach) now links to its source (`w3.org/WAI/WCAG21/Understanding/*`, `designsystem.digital.gov`) (#32).
18
+
19
+ ## 0.13.0 — 2026-07-23
20
+
21
+ Syntax-highlighting palette — 9 `--color-syntax-*` slots, ANSI-derived, golden-gated (closes #53; consensus-ratified 3-0 with a binding taxonomy design record, 2026-07-23).
22
+
23
+ ### Added
24
+ - **`--color-syntax-keyword/string/constant/comment/function/type/punctuation/variable/link`** —
25
+ 9 new palette-tier slots in `tokens-palette.css`, hand-authored in both
26
+ themes, quiet/low-chroma (chroma capped at 0.14, the same ceiling as
27
+ `--color-accent`). Every slot is ≥ 4.5:1 against `--color-code-bg`
28
+ (not `--color-bg`) in both themes — a new dimension the audit didn't
29
+ cover before this release.
30
+ - **`scripts/audit.mjs`** — 9 new `CHECKS` pairings (each syntax slot vs.
31
+ `color-code-bg`, 4.5:1, both themes). `scripts/test-audit.mjs`'s
32
+ fixture palettes carry the 9 slots and a new must-fail case proves
33
+ they're actually wired into `CHECKS`, not just parsed.
34
+ - **`scripts/theme.mjs` (`remarque-theme`)** — derives all 9 slots from a
35
+ source theme's 16 ANSI colors (`keyword`←blue, `string`←green,
36
+ `constant`←yellow, `function`←purple, `type`←cyan, `comment`←`brightBlack`
37
+ in dark themes / a solved muted neutral in light themes,
38
+ `punctuation`←a derived neutral, `variable`←fg-adjacent,
39
+ `link`←the derived accent, aliased when it clears 4.5:1 on `code-bg`),
40
+ same keep-if-passing-else-solve pattern and in-gamut chroma clamping as
41
+ every other slot. `scripts/test-theme.mjs`'s 61-pair corpus (every
42
+ counterpart-paired theme in the installed `oklch-terminal-themes`
43
+ dataset) now runs the extended audit — 61/61 pass.
44
+ - **`scripts/palette-golden.mjs`** — extends the ΔE2000 ≤ 2.0 golden gate
45
+ to the 9 syntax slots (now 24 `--color-*` tokens × 2 themes). The
46
+ hand-authored defaults are the cleaned round-number serialization of
47
+ values derived from `remarque-light`/`remarque-dark`'s ANSI colors,
48
+ same identity/serialization contract as the rest of the default
49
+ palette (#76).
50
+ - **REMARQUE.md "Syntax Highlighting"** — the slot table with Shiki
51
+ `createCssVariablesTheme` and Prism class mappings, the exact Astro
52
+ wiring snippet (pass the theme *object*, never the `'css-variables'`
53
+ string — Astro renames its prefix), the CSS variable bridge required
54
+ to make that wiring actually render (Shiki's `variablePrefix` prepends
55
+ rather than renames its internal `token-*` names), a full Prism CSS
56
+ mapping block, the three documented divergences (`type` ≡ `function`
57
+ under Shiki, plain operators inherit foreground, diff markers out of
58
+ scope), and the comment-contrast policy (4.5:1 held; GitHub
59
+ light/dark-default and VS Code Dark+ all clear it).
60
+ - **Demo site (`site/`)** — wired `astro.config.mjs`'s `markdown.shikiConfig`
61
+ and the Type Specimen page's new "Syntax Highlighting" section with
62
+ the `createCssVariablesTheme` recipe and its CSS bridge
63
+ (`site/src/styles/globals.css`), so at least one built page exercises
64
+ every slot in both themes. `shiki` added as an explicit `site`
65
+ devDependency (Astro already carries it transitively; pinned
66
+ explicitly so the resolution is intentional, not an accident of
67
+ hoisting). Playwright baselines updated for the 4 `specimen-*`
68
+ screenshots (the only page with a highlighted code block); the other
69
+ 16 baselines are unchanged.
70
+
7
71
  ## 0.12.0 — 2026-07-23
8
72
 
9
73
  TypeScript types and a README "Used By" section (closes #34, closes #35).
package/README.md CHANGED
@@ -119,6 +119,39 @@ See the [live demo](https://williamzujkowski.github.io/remarque/) for the origin
119
119
  | Border radius | 0.5rem max | Restrained. No "friendly SaaS" over-rounding |
120
120
  | Motion | 120ms/180ms | Hover and theme transitions only. No scroll-triggered, entrance, or staggered animations |
121
121
 
122
+ ## Graduation — when a site invention becomes system vocabulary
123
+
124
+ Consumer sites invent things (the flagship's sidenotes and theme deck, tsundoku's gallery
125
+ grid). Some of those inventions belong upstream; most are the site's own remarque in the
126
+ margin and should stay there. A local invention is upstreamable when **all four** hold:
127
+
128
+ 1. **Wanted by 2+ sites** (or one site plus a ratified case that it generalizes).
129
+ 2. **Consumes only tokens** — no hardcoded colors, sizes, or faces.
130
+ 3. **Passes the audit tooling** (`remarque-audit`, and `remarque-drift` reports it as
131
+ palette-tier INFO, not a core FAIL).
132
+ 4. **Violates no Never-list entry** in the spec — or arrives together with an explicit
133
+ spec amendment arguing the change.
134
+
135
+ **Three destinations.** Core spec (rare — core is identity, the bar is a fork-level
136
+ argument); optional module (sidenotes, TOC rail, palette deck); or *documented site-local
137
+ pattern* — explicitly not shared. The zine layer that inspired this checklist is the
138
+ exemplar of the third: sanctioned personality that would be diluted by generalizing it.
139
+
140
+ **Process (single-maintainer scale, no ceremony):** open an issue with before/after
141
+ screenshots and the token-purity check; record the decision in the changelog. Historical
142
+ examples: measure compensation and the Gallery archetype graduated; the theme deck was
143
+ re-scoped on the way up ([#56](https://github.com/williamzujkowski/remarque/issues/56));
144
+ the zine layer deliberately stayed local.
145
+
146
+ **Standing rulings** (ratified 3-0, 2026-07-23) on patterns the reviews kept re-flagging:
147
+
148
+ | Pattern | Ruling |
149
+ |---------|--------|
150
+ | Share links in post footers | Site-local, never a system module. If you build one: URL-encode every interpolated value and reject non-`https` schemes — title/frontmatter injection into share-intent URLs is the classic mistake. |
151
+ | Cross-hue hover accents | Not sanctioned anywhere. The hover move is a lightness/chroma shift within the accent hue (`--color-accent-hover` models it). A cross-hue hover is a documented deviation, not personalization. |
152
+ | Ambient/decorative animation | Site-local only, never in system modules — and the `prefers-reduced-motion` guard is mandatory, not advisory. A future audit scan may flag unguarded infinite animations (as a heuristic check, not a soundness proof). |
153
+ | Landing-page entry counts | Permitted as meta-voice data in the Landing archetype **only when computed from real inventory at build time**. A hardcoded count is a violation — counts drift (see the upstream count-drift bug class this system just fixed in its own theme provider). |
154
+
122
155
  ## Named For
123
156
 
124
157
  In fine art printmaking, a *remarque* is a small original drawing made by the artist in the margin of a proof print. Found only in limited editions, it is the mark that says: this was made by someone specific, with intention.
package/REMARQUE.md CHANGED
@@ -80,7 +80,7 @@ The palette tier (all `--color-*` tokens) can be *supplied* rather than hand-aut
80
80
  npx remarque-theme <light-slug> [--dark <dark-slug>] [-o palette-override.css]
81
81
  ```
82
82
 
83
- Terminal themes carry only `background/foreground/cursor/selection` + 16 ANSI slots — a few of Remarque's 15 semantic slots map directly, and most themes fail the AAA `fg-muted` 7:1 line as authored. So `remarque-theme` *derives* rather than maps: **hue and chroma carry the theme's personality; lightness is KEEP-IF-PASSING**. For the slots that have a meaningful "theme's own value" — `fg`, `accent`, `accent-hover`, and (in dark) `selection-fg`/`code-fg` — the theme's own authored lightness is kept as-is when it already clears the slot's contrast target; lightness is solved (binary search, same targets as the Enforcement Checklist below, with in-gamut chroma clamping) *only* when it doesn't. Every other slot (`fg-muted`, `muted`, `border-bold`, the bg ladder, `selection-bg`, `accent-subtle`) is solved/derived unconditionally, as it always was — those don't carry an independent authored value to preserve. This means a well-designed input theme comes through close to its own feel instead of being flattened to the exact threshold; a theme that fails a target still gets a value that passes, exactly as before. The accent hue comes from the theme's cursor color if it's chromatic, otherwise the most saturated classic ANSI color, and is kept consistent between the light and dark half of a pair.
83
+ Terminal themes carry only `background/foreground/cursor/selection` + 16 ANSI slots — a few of Remarque's 24 semantic slots map directly, and most themes fail the AAA `fg-muted` 7:1 line as authored. So `remarque-theme` *derives* rather than maps: **hue and chroma carry the theme's personality; lightness is KEEP-IF-PASSING**. For the slots that have a meaningful "theme's own value" — `fg`, `accent`, `accent-hover`, and (in dark) `selection-fg`/`code-fg` — the theme's own authored lightness is kept as-is when it already clears the slot's contrast target; lightness is solved (binary search, same targets as the Enforcement Checklist below, with in-gamut chroma clamping) *only* when it doesn't. Every other slot (`fg-muted`, `muted`, `border-bold`, the bg ladder, `selection-bg`, `accent-subtle`) is solved/derived unconditionally, as it always was — those don't carry an independent authored value to preserve. This means a well-designed input theme comes through close to its own feel instead of being flattened to the exact threshold; a theme that fails a target still gets a value that passes, exactly as before. The accent hue comes from the theme's cursor color if it's chromatic, otherwise the most saturated classic ANSI color, and is kept consistent between the light and dark half of a pair.
84
84
 
85
85
  The audit remains the gate regardless of provenance — `remarque-theme` self-verifies its own output against the same checks before it will emit anything, but a site that hand-edits a generated palette (or points at a provider with pathological input colors) still runs through `remarque-audit` in CI like any other palette.
86
86
 
@@ -124,6 +124,14 @@ This is why the measure is palette-tier: it is a property of the font choice, no
124
124
 
125
125
  The book-craft layer: numerals, small caps, drop caps, pull quotes, and optical wrapping. These are the details that separate typeset prose from a web page that merely uses nice fonts.
126
126
 
127
+ `.remarque-prose` supplies typography only — font, line height, numeral register, optical wrapping — and stops short of centering the column, so pair it with `.content-reading` wherever the reading column needs to sit centered on the page:
128
+
129
+ ```html
130
+ <div class="remarque-prose content-reading">
131
+ <!-- article body -->
132
+ </div>
133
+ ```
134
+
127
135
  ### Numerals
128
136
 
129
137
  Two numeral registers, chosen by context:
@@ -231,6 +239,113 @@ Charts are not yet a first-class Remarque surface, but tools built with the syst
231
239
 
232
240
  ---
233
241
 
242
+ ## Syntax Highlighting
243
+
244
+ Code blocks are a first-class Remarque surface, not a place to reach for a vendor Shiki/Prism theme: `tokens-palette.css` defines 9 `--color-syntax-*` slots, hand-authored in both themes and golden-gated against the upstream `remarque-light`/`remarque-dark` themes' ANSI colors the same way the rest of the default palette is (issue #53). Because they are ordinary palette-tier tokens, they inherit palette swaps and theme-deck runtime switches for free — a vendor theme never does.
245
+
246
+ ### Slots
247
+
248
+ | Slot | Shiki (`createCssVariablesTheme`) | Prism |
249
+ |---|---|---|
250
+ | `--color-syntax-keyword` | `token-keyword` | `.keyword`, `.atrule` |
251
+ | `--color-syntax-string` | `token-string` + `token-string-expression` (aliased — regex/template/interpolated are string-like) | `.string`, `.char`, `.attr-value` |
252
+ | `--color-syntax-constant` | `token-constant` | `.number`, `.boolean`, `.constant`, `.symbol` |
253
+ | `--color-syntax-comment` | `token-comment` | `.comment`, `.prolog`, `.doctype`, `.cdata` |
254
+ | `--color-syntax-function` | `token-function` | `.function` |
255
+ | `--color-syntax-type` | (same Shiki variable as `function` — see divergence below) | `.class-name` |
256
+ | `--color-syntax-punctuation` | `token-punctuation` | `.operator`, `.punctuation` |
257
+ | `--color-syntax-variable` | `token-parameter` | `.variable`, `.property`, `.tag`, `.attr-name` |
258
+ | `--color-syntax-link` | `token-link` | `.url` |
259
+
260
+ Slot names were validated against Shiki's `theme-css-variables.ts` and Prism's reference-theme token vocabulary before being frozen (panel condition on #53) — every name above maps to a real token both highlighters actually emit.
261
+
262
+ ### Derivation
263
+
264
+ `remarque-theme` derives all 9 slots from the source theme's 16 ANSI colors — the same keep-if-passing-else-solve pattern as every other slot, targeted at `--color-code-bg` (not `--color-bg`) at 4.5:1: `keyword`←blue, `string`←green, `constant`←yellow (the terminal convention for numbers/booleans), `function`←purple, `type`←cyan, `comment`←`brightBlack` in dark themes (solved to a muted neutral on the theme's own fg hue in light themes, where `brightBlack` is often too pale to clear 4.5:1 on `code-bg`), `punctuation`←a derived neutral in the muted family, `variable`←the theme's own fg hue/chroma with a slight lightness offset, and `link`←the already-derived accent triple, aliased with `var(--color-accent)` when it clears 4.5:1 on `code-bg` (solved standalone when it doesn't — accent's guarantee is against `--color-bg`, and `code-bg` sits close enough to drift below the line on some corpus themes). Chroma is capped at 0.14, the same ceiling as `--color-accent`, so syntax colors stay in the system's quiet, one-accent register. `scripts/palette-golden.mjs` extends its ΔE2000 ≤ 2.0 gate to all 9 slots, exactly like the rest of the default palette.
265
+
266
+ ### Astro / Shiki wiring
267
+
268
+ Shiki v1+ ships `createCssVariablesTheme` — pass it as an object, not the `'css-variables'` string (Astro renames the string form's variable prefix to `--astro-code-*`, which silently breaks the mapping):
269
+
270
+ ```js
271
+ import { createCssVariablesTheme } from 'shiki'; // or '@shikijs/core'
272
+
273
+ const theme = createCssVariablesTheme({
274
+ name: 'remarque',
275
+ variablePrefix: '--color-syntax-',
276
+ fontStyle: true,
277
+ });
278
+
279
+ // markdown/MDX: { shikiConfig: { theme } }
280
+ // <Code> component: <Code code={...} lang="..." theme={theme} />
281
+ ```
282
+
283
+ **The CSS bridge (required, not optional):** `createCssVariablesTheme`'s `variablePrefix` is a plain string prepend, not a rename — Shiki's own internal slot names already start with `token-` (`token-keyword`, `token-string-expression`, `token-constant`, …) plus a bare `foreground`/`background`, so `variablePrefix: '--color-syntax-'` makes Shiki bake `color:var(--color-syntax-token-keyword)` into every span, not `var(--color-syntax-keyword)`. Left undefined, that variable resolves to nothing and the span silently inherits its parent's color — the highlighting looks like it's missing, not broken. One small bridge block closes the gap (Astro's Shiki integration puts an `.astro-code` class on the `<pre>`; scope to it so these implementation-detail names don't leak into the page's global custom-property namespace):
284
+
285
+ ```css
286
+ .astro-code {
287
+ --color-syntax-foreground: var(--color-code-fg);
288
+ --color-syntax-background: var(--color-code-bg);
289
+ --color-syntax-token-keyword: var(--color-syntax-keyword);
290
+ --color-syntax-token-string: var(--color-syntax-string);
291
+ --color-syntax-token-string-expression: var(--color-syntax-string);
292
+ --color-syntax-token-constant: var(--color-syntax-constant);
293
+ --color-syntax-token-comment: var(--color-syntax-comment);
294
+ --color-syntax-token-function: var(--color-syntax-function);
295
+ --color-syntax-token-parameter: var(--color-syntax-variable);
296
+ --color-syntax-token-punctuation: var(--color-syntax-punctuation);
297
+ --color-syntax-token-link: var(--color-syntax-link);
298
+ /* token-inserted/-deleted/-changed intentionally left undefined —
299
+ diff markers, out of scope (see "Documented divergences" below);
300
+ they fall back to inherited foreground, which is a safe default. */
301
+ }
302
+ ```
303
+
304
+ There is no separate `token-type` — see the `type` ≡ `function` divergence below; `--color-syntax-type` is real for Prism but never appears as a Shiki variable.
305
+
306
+ ### Prism CSS mapping
307
+
308
+ Prism ships no CSS-variables mode — wire the classes directly:
309
+
310
+ ```css
311
+ .token.keyword,
312
+ .token.atrule { color: var(--color-syntax-keyword); }
313
+ .token.string,
314
+ .token.char,
315
+ .token.attr-value { color: var(--color-syntax-string); }
316
+ .token.number,
317
+ .token.boolean,
318
+ .token.constant,
319
+ .token.symbol { color: var(--color-syntax-constant); }
320
+ .token.comment,
321
+ .token.prolog,
322
+ .token.doctype,
323
+ .token.cdata { color: var(--color-syntax-comment); }
324
+ .token.function { color: var(--color-syntax-function); }
325
+ .token.class-name { color: var(--color-syntax-type); }
326
+ .token.operator,
327
+ .token.punctuation { color: var(--color-syntax-punctuation); }
328
+ .token.variable,
329
+ .token.property,
330
+ .token.tag,
331
+ .token.attr-name { color: var(--color-syntax-variable); }
332
+ .token.url { color: var(--color-syntax-link); }
333
+ ```
334
+
335
+ ### Documented divergences
336
+
337
+ These are deliberate, not bugs:
338
+
339
+ - **`type` ≡ `function` under Shiki.** Shiki's css-variables mode folds `entity.name.type` into the function-scoped variable, so the two slots render identically in Shiki output even though Prism (`.class-name` vs. `.function`) distinguishes them. The tokens stay separate because Prism needs them separate.
340
+ - **Plain operators inherit the foreground.** Shiki has no scope for a bare operator token; it renders in `--color-fg` like surrounding text rather than `--color-syntax-punctuation`. Prism's `.operator` class does map to punctuation.
341
+ - **Diff markers are out of scope.** `token-inserted`/`token-deleted`/`token-changed` (Shiki) and equivalent Prism diff classes are not covered by this token set — a future `--color-diff-*` family would own them if a concrete consumer needs one.
342
+
343
+ ### Comment-contrast policy
344
+
345
+ The 4.5:1 floor is held, not relaxed, for `--color-syntax-comment` — muted comments are an editorial convention, not a license to fail AA. Survey of shipping syntax themes: GitHub's light-default theme holds 4.55:1, GitHub's dark-default holds 6.15:1, and VS Code's Dark+ holds 5.0:1 — all pass. Only legacy/neglected themes fail (Prism's own default theme sits at 3.64:1; the legacy `github-dark` theme sits at 3.05:1). A muted-comment convention is achievable within AA; Remarque does not carry a documented exception for this slot.
346
+
347
+ ---
348
+
234
349
  ## Motion Rules
235
350
 
236
351
  Motion in Remarque is nearly invisible. The only permitted motion:
@@ -246,19 +361,19 @@ Motion in Remarque is nearly invisible. The only permitted motion:
246
361
  - Parallax effects
247
362
  - Any animation on non-interactive elements
248
363
 
249
- **Reduced motion:** all motion must respect `prefers-reduced-motion` (WCAG 2.3.3). Because every sanctioned transition uses the two duration tokens, tokens.css zeroes `--motion-fast`/`--motion-normal` under `prefers-reduced-motion: reduce` — motion authored with the tokens is automatically safe. Never hardcode durations; any future opt-in animation must live inside a `(prefers-reduced-motion: no-preference)` query.
364
+ **Reduced motion:** all motion must respect `prefers-reduced-motion` ([WCAG 2.3.3](https://www.w3.org/WAI/WCAG21/Understanding/animation-from-interactions.html)). Because every sanctioned transition uses the two duration tokens, tokens.css zeroes `--motion-fast`/`--motion-normal` under `prefers-reduced-motion: reduce` — motion authored with the tokens is automatically safe. Never hardcode durations; any future opt-in animation must live inside a `(prefers-reduced-motion: no-preference)` query.
250
365
 
251
366
  ---
252
367
 
253
368
  ## USWDS Accessibility Compliance
254
369
 
255
- Remarque adopts typography and accessibility standards from the US Web Design System (USWDS) and WCAG 2.1:
370
+ Remarque adopts typography and accessibility standards from the [US Web Design System (USWDS)](https://designsystem.digital.gov/documentation/accessibility/) and [WCAG 2.1](https://www.w3.org/WAI/WCAG21/Understanding/):
256
371
 
257
372
  ### Font Size Floors
258
373
 
259
374
  | Token | Value | USWDS Rationale |
260
375
  |-------|-------|-----------------|
261
- | `--text-body` | 17px (1.0625rem) | Exceeds USWDS 16px minimum. Non-negotiable floor. |
376
+ | `--text-body` | 17px (1.0625rem) | Exceeds [USWDS 16px minimum](https://designsystem.digital.gov/components/typography/). Non-negotiable floor. |
262
377
  | `--text-meta` | 14px (0.875rem) | USWDS minimum for small text. Used sparingly for metadata, labels, captions. |
263
378
  | `--text-micro` | 13px (0.8125rem) | USWDS floor for timestamps and fine print only. Never for body content. |
264
379
 
@@ -266,13 +381,13 @@ Remarque adopts typography and accessibility standards from the US Web Design Sy
266
381
 
267
382
  | Pairing | Requirement | Notes |
268
383
  |---------|-------------|-------|
269
- | Normal text on background | 4.5:1 minimum | All text below 24px regular / 18.5px bold. Applies on `--color-surface` too, not just `--color-bg` |
270
- | Large text on background | 3:1 minimum | Display and title text (24px+) |
271
- | Functional non-text elements | 3:1 minimum | WCAG 1.4.11 — use `--color-border-bold` (meets 3:1). `--color-border` is decorative-only by design and exempt; never the sole boundary of an interactive element |
384
+ | Normal text on background | 4.5:1 minimum | [WCAG 1.4.3](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html) — all text below 24px regular / 18.5px bold. Applies on `--color-surface` too, not just `--color-bg` |
385
+ | Large text on background | 3:1 minimum | [WCAG 1.4.3](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html) — display and title text (24px+) |
386
+ | Functional non-text elements | 3:1 minimum | [WCAG 1.4.11](https://www.w3.org/WAI/WCAG21/Understanding/non-text-contrast.html) — use `--color-border-bold` (meets 3:1). `--color-border` is decorative-only by design and exempt; never the sole boundary of an interactive element |
272
387
 
273
388
  ### Touch Targets
274
389
 
275
- All interactive elements (links, buttons, inputs) must have a minimum touch target of **44x44px** (WCAG 2.5.5 AAA). This applies to nav links, theme toggles, footer links, chips, and any clickable element.
390
+ All interactive elements (links, buttons, inputs) must have a minimum touch target of **44x44px** ([WCAG 2.5.5](https://www.w3.org/WAI/WCAG21/Understanding/target-size.html) AAA). This applies to nav links, theme toggles, footer links, chips, and any clickable element.
276
391
 
277
392
  ### Line Height
278
393
 
@@ -303,6 +418,7 @@ Every PR that ships Remarque pages MUST pass (`npm run audit` automates the colo
303
418
  - [ ] `font-variant-numeric: tabular-nums lining-nums` on all metadata that mixes with dates/counts.
304
419
  - [ ] No hardcoded hex/rgb colors — only `var(--color-*)` tokens.
305
420
  - [ ] Body line-height ≥ 1.5 (Remarque target: 1.75).
421
+ - [ ] Every `--color-syntax-*` slot ≥ 4.5:1 against `--color-code-bg` in both themes (see "Syntax Highlighting").
306
422
 
307
423
  Agents reviewing PRs should reject changes that violate any line above without explicit rationale.
308
424
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remarque-tokens",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "engines": {
5
5
  "node": ">=18"
6
6
  },
package/scripts/audit.mjs CHANGED
@@ -113,6 +113,15 @@ const CHECKS = [
113
113
  ['color-code-fg', 'color-code-bg', 4.5, 'code text'],
114
114
  ['color-border-bold', 'color-bg', 3.0, 'functional borders (WCAG 1.4.11)'],
115
115
  ['color-selection-fg', 'color-selection-bg', 4.5, 'selected text'],
116
+ ['color-syntax-keyword', 'color-code-bg', 4.5, 'syntax: keyword'],
117
+ ['color-syntax-string', 'color-code-bg', 4.5, 'syntax: string'],
118
+ ['color-syntax-constant', 'color-code-bg', 4.5, 'syntax: constant'],
119
+ ['color-syntax-comment', 'color-code-bg', 4.5, 'syntax: comment'],
120
+ ['color-syntax-function', 'color-code-bg', 4.5, 'syntax: function'],
121
+ ['color-syntax-type', 'color-code-bg', 4.5, 'syntax: type'],
122
+ ['color-syntax-punctuation', 'color-code-bg', 4.5, 'syntax: punctuation'],
123
+ ['color-syntax-variable', 'color-code-bg', 4.5, 'syntax: variable'],
124
+ ['color-syntax-link', 'color-code-bg', 4.5, 'syntax: link'],
116
125
  ];
117
126
 
118
127
  for (const [themeName, decls] of [['light', lightDecls], ['dark', darkDecls]]) {
package/scripts/theme.mjs CHANGED
@@ -7,7 +7,7 @@
7
7
  * npx remarque-theme <light-slug> --dark <dark-slug> [-o out.css]
8
8
  *
9
9
  * Terminal themes carry only background/foreground/cursor/selection + 16
10
- * ANSI slots — most of Remarque's 15 semantic slots don't map directly,
10
+ * ANSI slots — most of Remarque's 24 semantic slots don't map directly,
11
11
  * and most themes fail the AAA fg-muted 7:1 line as authored. So this
12
12
  * DERIVES rather than maps: hue + chroma come from the theme (its
13
13
  * personality); lightness is KEEP-IF-PASSING — a few load-bearing slots
@@ -19,9 +19,11 @@
19
19
  * still gets a value that passes, same as before (#76). Every other
20
20
  * slot (fg-muted, muted, border-bold, the bg ladder, selection-bg,
21
21
  * accent-subtle) is solved/derived as before — those don't have a
22
- * meaningful "theme's own value" to preserve. Output passes
23
- * remarque-audit BY CONSTRUCTION this script self-verifies the same
24
- * pairings before it will emit anything.
22
+ * meaningful "theme's own value" to preserve. The 9 --color-syntax-*
23
+ * slots (issue #53) are derived straight from the theme's 16 ANSI
24
+ * colors instead see the "Syntax-highlighting slots" section below.
25
+ * Output passes remarque-audit BY CONSTRUCTION — this script
26
+ * self-verifies the same pairings before it will emit anything.
25
27
  *
26
28
  * Security (non-negotiable, per the panel security review on #75):
27
29
  * - Both slugs are checked against the package's own index.json BEFORE
@@ -325,6 +327,66 @@ function withHeadroom([L0, , H], delta, dir, C) {
325
327
  return [L, fitChroma(L, C, H), r1(H)];
326
328
  }
327
329
 
330
+ /* ── Syntax-highlighting slots (issue #53) ───────────────────────────
331
+ * Terminal ANSI colors ARE syntax colors in their native domain, so the
332
+ * 9 --color-syntax-* slots are derived straight from a theme's 16 ANSI
333
+ * colors rather than invented fresh — zero new upstream schema work.
334
+ * Every slot targets --color-code-bg (not --color-bg) at 4.5:1, same
335
+ * keep-if-passing-else-solve pattern as every other slot above: the
336
+ * ANSI color's own lightness is kept when it already clears the ratio,
337
+ * solved (same binary search, same in-gamut chroma clamping) only when
338
+ * it doesn't. Chroma is capped at 0.14 — the same ceiling already used
339
+ * for --color-accent — so syntax colors stay in the system's "quiet,
340
+ * one-accent" register rather than turning carnival-bright. */
341
+
342
+ const SYNTAX_CHROMA_CAP = 0.14;
343
+
344
+ /* keyword/string/constant/function/type: a direct ANSI slot, kept if it
345
+ * already passes vs code-bg, solved (same hue) if it doesn't. */
346
+ function deriveSyntaxAnsi(t, ansiName, label, codeBg, dir) {
347
+ const [L0, C0, H0] = slotLch(t, ansiName, label);
348
+ const C = Math.min(C0, SYNTAX_CHROMA_CAP);
349
+ return keepOrSolve(L0, C, H0, codeBg, 4.5, dir);
350
+ }
351
+
352
+ /* comment: brightBlack in dark themes (the ANSI convention); in light
353
+ * themes brightBlack is often too pale to hold 4.5:1 on code-bg, so the
354
+ * fallback solves a muted neutral on the theme's own fg hue instead of
355
+ * brightBlack's hue. */
356
+ function deriveSyntaxComment(t, label, codeBg, dir, fallbackC, fallbackH) {
357
+ const [L0, C0, H0] = slotLch(t, 'brightBlack', label);
358
+ const C = Math.min(C0, SYNTAX_CHROMA_CAP);
359
+ const L = r3(Math.min(1, Math.max(0, L0)));
360
+ const c = fitChroma(L, C, H0);
361
+ if (ratio([L, c, H0], codeBg) >= 4.5) return [L, c, r1(H0)];
362
+ return solveL(fallbackC, fallbackH, codeBg, 4.5, dir);
363
+ }
364
+
365
+ /* punctuation: a derived neutral in the muted family (fg hue, low
366
+ * chroma), targeted at code-bg rather than surface. */
367
+ function deriveSyntaxPunctuation(fgC, fgH, codeBg, dir) {
368
+ return solveL(Math.min(fgC, 0.02), fgH, codeBg, 4.5, dir);
369
+ }
370
+
371
+ /* variable: fg-adjacent — the theme's own fg hue/chroma, offset a little
372
+ * so it reads as "quieter than plain text," verified (and solved if
373
+ * needed) against code-bg rather than assumed from the fg/surface pass. */
374
+ function deriveSyntaxVariable(fg, codeBg, dir, offset) {
375
+ return keepOrSolve(fg[0] + offset, fg[1], fg[2], codeBg, 4.5, dir);
376
+ }
377
+
378
+ /* link: alias the already-derived accent triple when it clears 4.5:1 on
379
+ * code-bg (accent is only guaranteed against --color-bg, and code-bg
380
+ * sits a little off from bg, so this isn't automatic) — solve a
381
+ * standalone value at the accent's own hue/chroma only when it doesn't,
382
+ * same as every other slot. Aliasing means a site's accent-hue swap
383
+ * (REMARQUE.md "Changing the Accent Hue") carries the link color with
384
+ * it for free whenever the alias holds. */
385
+ function deriveSyntaxLink(accent, codeBg, dir) {
386
+ if (ratio(accent, codeBg) >= 4.5) return { ref: 'color-accent' };
387
+ return keepOrSolve(accent[0], accent[1], accent[2], codeBg, 4.5, dir);
388
+ }
389
+
328
390
  /* ── LIGHT derivation ─────────────────────────────────────────────── */
329
391
 
330
392
  function deriveLight(t) {
@@ -351,6 +413,18 @@ function deriveLight(t) {
351
413
  // accent-hover: still the designed -0.08 offset from accent, but that
352
414
  // offset is verified against 4.5:1 on bg and solve-adjusted if it breaks.
353
415
  const accentHover = keepOrSolve(accent[0] - 0.08, acC * 0.8, ac.h, bg, 4.5, 'darker');
416
+ const codeBg = [r3(bg[0] - 0.03), bg[1], bg[2]];
417
+ // Syntax-highlighting slots (issue #53) — see the derivation helpers
418
+ // above. All verified against code-bg (not bg) at 4.5:1.
419
+ const synKeyword = deriveSyntaxAnsi(t, 'blue', 'light', codeBg, 'darker');
420
+ const synString = deriveSyntaxAnsi(t, 'green', 'light', codeBg, 'darker');
421
+ const synConstant = deriveSyntaxAnsi(t, 'yellow', 'light', codeBg, 'darker');
422
+ const synFunction = deriveSyntaxAnsi(t, 'purple', 'light', codeBg, 'darker');
423
+ const synType = deriveSyntaxAnsi(t, 'cyan', 'light', codeBg, 'darker');
424
+ const synComment = deriveSyntaxComment(t, 'light', codeBg, 'darker', fgC, fgH);
425
+ const synPunctuation = deriveSyntaxPunctuation(fgC, fgH, codeBg, 'darker');
426
+ const synVariable = deriveSyntaxVariable(fg, codeBg, 'darker', 0.08);
427
+ const synLink = deriveSyntaxLink(accent, codeBg, 'darker');
354
428
  const raw = {
355
429
  'color-bg': bg,
356
430
  'color-bg-subtle': [r3(bg[0] - 0.02), bg[1], bg[2]],
@@ -365,8 +439,17 @@ function deriveLight(t) {
365
439
  'color-accent-subtle': [0.95, fitChroma(0.95, 0.02, ac.h), r1(ac.h)],
366
440
  'color-selection-bg': [0.92, fitChroma(0.92, 0.04, ac.h), r1(ac.h)],
367
441
  'color-selection-fg': { ref: 'color-fg' },
368
- 'color-code-bg': [r3(bg[0] - 0.03), bg[1], bg[2]],
442
+ 'color-code-bg': codeBg,
369
443
  'color-code-fg': { ref: 'color-fg' },
444
+ 'color-syntax-keyword': synKeyword,
445
+ 'color-syntax-string': synString,
446
+ 'color-syntax-constant': synConstant,
447
+ 'color-syntax-comment': synComment,
448
+ 'color-syntax-function': synFunction,
449
+ 'color-syntax-type': synType,
450
+ 'color-syntax-punctuation': synPunctuation,
451
+ 'color-syntax-variable': synVariable,
452
+ 'color-syntax-link': synLink,
370
453
  };
371
454
  const tokens = {};
372
455
  for (const [name, v] of Object.entries(raw)) tokens[name] = v.ref ? `var(--${v.ref})` : fmt(v);
@@ -409,6 +492,17 @@ function deriveDark(t, accentHueLight) {
409
492
  // code-fg: "slightly dimmer than fg" — same pattern, -0.02, verified
410
493
  // against the AAA 7:1 line on code-bg.
411
494
  const codeFg = keepOrSolve(fg[0] - 0.02, fg[1], fg[2], codeBg, 7.0, 'lighter');
495
+ // Syntax-highlighting slots (issue #53) — mirrors the light derivation,
496
+ // direction flipped ('lighter': text on a dark code-bg).
497
+ const synKeyword = deriveSyntaxAnsi(t, 'blue', 'dark', codeBg, 'lighter');
498
+ const synString = deriveSyntaxAnsi(t, 'green', 'dark', codeBg, 'lighter');
499
+ const synConstant = deriveSyntaxAnsi(t, 'yellow', 'dark', codeBg, 'lighter');
500
+ const synFunction = deriveSyntaxAnsi(t, 'purple', 'dark', codeBg, 'lighter');
501
+ const synType = deriveSyntaxAnsi(t, 'cyan', 'dark', codeBg, 'lighter');
502
+ const synComment = deriveSyntaxComment(t, 'dark', codeBg, 'lighter', fgC, fgH);
503
+ const synPunctuation = deriveSyntaxPunctuation(fgC, fgH, codeBg, 'lighter');
504
+ const synVariable = deriveSyntaxVariable(fg, codeBg, 'lighter', -0.08);
505
+ const synLink = deriveSyntaxLink(accent, codeBg, 'lighter');
412
506
  const raw = {
413
507
  'color-bg': bg,
414
508
  'color-bg-subtle': surface,
@@ -425,9 +519,18 @@ function deriveDark(t, accentHueLight) {
425
519
  'color-selection-fg': selFg,
426
520
  'color-code-bg': codeBg,
427
521
  'color-code-fg': codeFg,
522
+ 'color-syntax-keyword': synKeyword,
523
+ 'color-syntax-string': synString,
524
+ 'color-syntax-constant': synConstant,
525
+ 'color-syntax-comment': synComment,
526
+ 'color-syntax-function': synFunction,
527
+ 'color-syntax-type': synType,
528
+ 'color-syntax-punctuation': synPunctuation,
529
+ 'color-syntax-variable': synVariable,
530
+ 'color-syntax-link': synLink,
428
531
  };
429
532
  const tokens = {};
430
- for (const [name, v] of Object.entries(raw)) tokens[name] = fmt(v);
533
+ for (const [name, v] of Object.entries(raw)) tokens[name] = v.ref ? `var(--${v.ref})` : fmt(v);
431
534
  tokens['weight-display'] = '500';
432
535
  return { raw, tokens };
433
536
  }
@@ -452,6 +555,15 @@ const CHECKS = [
452
555
  ['color-code-fg', 'color-code-bg', 4.5, 'code text'],
453
556
  ['color-border-bold', 'color-bg', 3.0, 'functional borders (WCAG 1.4.11)'],
454
557
  ['color-selection-fg', 'color-selection-bg', 4.5, 'selected text'],
558
+ ['color-syntax-keyword', 'color-code-bg', 4.5, 'syntax: keyword'],
559
+ ['color-syntax-string', 'color-code-bg', 4.5, 'syntax: string'],
560
+ ['color-syntax-constant', 'color-code-bg', 4.5, 'syntax: constant'],
561
+ ['color-syntax-comment', 'color-code-bg', 4.5, 'syntax: comment'],
562
+ ['color-syntax-function', 'color-code-bg', 4.5, 'syntax: function'],
563
+ ['color-syntax-type', 'color-code-bg', 4.5, 'syntax: type'],
564
+ ['color-syntax-punctuation', 'color-code-bg', 4.5, 'syntax: punctuation'],
565
+ ['color-syntax-variable', 'color-code-bg', 4.5, 'syntax: variable'],
566
+ ['color-syntax-link', 'color-code-bg', 4.5, 'syntax: link'],
455
567
  ];
456
568
 
457
569
  function resolveRaw(raw, name, seen = new Set()) {
package/theme.css CHANGED
@@ -24,6 +24,9 @@
24
24
  * is never overridden (mt-12 stays 3rem; use mt-remarque-9 for 6rem).
25
25
  * Motion utilities: use arbitrary values with the tokens —
26
26
  * duration-[var(--motion-fast)] — so reduced-motion zeroing applies.
27
+ * Pair every duration-[var(--motion-*)] utility with ease-remarque
28
+ * (mapped below from --motion-easing) — without it Tailwind falls back
29
+ * to its default `ease` timing function instead of the token (issue #31).
27
30
  */
28
31
 
29
32
  @theme inline {
@@ -74,4 +77,8 @@
74
77
  /* Radius (rounded-sm / rounded-md — the system's ceiling) */
75
78
  --radius-sm: var(--radius-sm);
76
79
  --radius-md: var(--radius-md);
80
+
81
+ /* Easing (ease-remarque) — pair with duration-[var(--motion-*)] so
82
+ transitions use the token instead of Tailwind's default `ease` */
83
+ --ease-remarque: var(--motion-easing);
77
84
  }
@@ -16,6 +16,7 @@
16
16
  * - Colors: every replacement palette must pass the contrast audit
17
17
  * (npm run audit) — fg-muted ≥ 7:1, muted ≥ 4.5:1 on bg AND
18
18
  * surface, accent + accent-hover ≥ 4.5:1, border-bold ≥ 3:1,
19
+ * every --color-syntax-* slot ≥ 4.5:1 on --color-code-bg,
19
20
  * in BOTH themes.
20
21
  * - Accent: change the hue by re-deriving accent/accent-hover/
21
22
  * accent-subtle/selection at the SAME lightness values used here,
@@ -77,6 +78,22 @@
77
78
  --color-selection-fg: var(--color-fg);
78
79
  --color-code-bg: oklch(0.945 0.005 80); /* inline code background */
79
80
  --color-code-fg: var(--color-fg);
81
+
82
+ /* Syntax highlighting (issue #53) — quiet, low-chroma slots derived
83
+ from remarque-light's ANSI colors (scripts/theme.mjs), serialized
84
+ as round numbers (golden-gated: scripts/palette-golden.mjs). Every
85
+ slot ≥ 4.5:1 on --color-code-bg. Mapping recipe (Shiki css-variables
86
+ + Prism) is documented in REMARQUE.md's "Syntax Highlighting"
87
+ section. */
88
+ --color-syntax-keyword: oklch(0.51 0.12 250); /* ANSI blue — 4.88:1 on code-bg */
89
+ --color-syntax-string: oklch(0.50 0.12 145); /* ANSI green — 4.85:1 */
90
+ --color-syntax-constant: oklch(0.51 0.105 85); /* ANSI yellow (numbers/booleans convention) — 4.94:1 */
91
+ --color-syntax-comment: oklch(0.52 0.01 80); /* solved muted neutral (fg hue) — brightBlack is too pale on code-bg here — 4.69:1 */
92
+ --color-syntax-function: oklch(0.52 0.12 310); /* ANSI purple — 4.96:1 */
93
+ --color-syntax-type: oklch(0.50 0.085 196); /* ANSI cyan — 4.90:1 */
94
+ --color-syntax-punctuation: oklch(0.52 0.01 80); /* derived neutral, muted family — 4.69:1 */
95
+ --color-syntax-variable: oklch(0.26 0.01 80); /* fg-adjacent (slight L offset) — 13.23:1 */
96
+ --color-syntax-link: var(--color-accent); /* accent already clears code-bg — 4.5:1+ */
80
97
  }
81
98
 
82
99
  /* ─── Color: Dark theme ─────────────────────────────────── */
@@ -110,6 +127,18 @@
110
127
  --color-selection-fg: oklch(0.92 0.005 80);
111
128
  --color-code-bg: oklch(0.20 0.005 80);
112
129
  --color-code-fg: oklch(0.88 0.005 80);
130
+
131
+ /* Syntax highlighting (issue #53) — derived from remarque-dark's
132
+ ANSI colors; see the light block above for the full rationale. */
133
+ --color-syntax-keyword: oklch(0.61 0.11 250); /* ANSI blue — 4.80:1 on code-bg */
134
+ --color-syntax-string: oklch(0.60 0.11 145); /* ANSI green — 4.80:1 */
135
+ --color-syntax-constant: oklch(0.61 0.11 84); /* ANSI yellow — 4.73:1 */
136
+ --color-syntax-comment: oklch(0.60 0.005 80); /* ANSI brightBlack — 4.59:1 */
137
+ --color-syntax-function: oklch(0.62 0.11 310); /* ANSI purple — 4.75:1 */
138
+ --color-syntax-type: oklch(0.60 0.10 195); /* ANSI cyan — 4.81:1 */
139
+ --color-syntax-punctuation: oklch(0.60 0.005 80); /* derived neutral, muted family — 4.59:1 */
140
+ --color-syntax-variable: oklch(0.82 0.005 80); /* fg-adjacent (slight L offset) — 10.37:1 */
141
+ --color-syntax-link: var(--color-accent); /* accent already clears code-bg */
113
142
  }
114
143
  }
115
144
 
@@ -141,4 +170,16 @@
141
170
  --color-selection-fg: oklch(0.92 0.005 80);
142
171
  --color-code-bg: oklch(0.20 0.005 80);
143
172
  --color-code-fg: oklch(0.88 0.005 80);
173
+
174
+ /* Syntax highlighting (issue #53) — derived from remarque-dark's
175
+ ANSI colors; see the light block above for the full rationale. */
176
+ --color-syntax-keyword: oklch(0.61 0.11 250);
177
+ --color-syntax-string: oklch(0.60 0.11 145);
178
+ --color-syntax-constant: oklch(0.61 0.11 84);
179
+ --color-syntax-comment: oklch(0.60 0.005 80);
180
+ --color-syntax-function: oklch(0.62 0.11 310);
181
+ --color-syntax-type: oklch(0.60 0.10 195);
182
+ --color-syntax-punctuation: oklch(0.60 0.005 80);
183
+ --color-syntax-variable: oklch(0.82 0.005 80);
184
+ --color-syntax-link: var(--color-accent);
144
185
  }
package/tokens.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Remarque design tokens — GENERATED from tokens.json by
3
- * scripts/tokens-json.mjs (v0.12.0). Do not edit — the CSS
3
+ * scripts/tokens-json.mjs (v0.14.0). Do not edit — the CSS
4
4
  * (tokens-core.css + tokens-palette.css) is the source of truth;
5
5
  * tokens.json is the intermediate machine-readable form this file is
6
6
  * generated from. Regenerate with: node scripts/tokens-json.mjs
@@ -81,7 +81,16 @@ export type RemarquePaletteToken =
81
81
  | 'color-selection-bg'
82
82
  | 'color-selection-fg'
83
83
  | 'color-code-bg'
84
- | 'color-code-fg';
84
+ | 'color-code-fg'
85
+ | 'color-syntax-keyword'
86
+ | 'color-syntax-string'
87
+ | 'color-syntax-constant'
88
+ | 'color-syntax-comment'
89
+ | 'color-syntax-function'
90
+ | 'color-syntax-type'
91
+ | 'color-syntax-punctuation'
92
+ | 'color-syntax-variable'
93
+ | 'color-syntax-link';
85
94
 
86
95
  /** Every Remarque token name, core + palette. */
87
96
  export type RemarqueToken = RemarqueCoreToken | RemarquePaletteToken;
@@ -287,6 +296,42 @@ export interface RemarqueTokenValues {
287
296
  readonly light: "var(--color-fg)";
288
297
  readonly dark: "oklch(0.88 0.005 80)";
289
298
  };
299
+ readonly 'color-syntax-keyword': {
300
+ readonly light: "oklch(0.51 0.12 250)";
301
+ readonly dark: "oklch(0.61 0.11 250)";
302
+ };
303
+ readonly 'color-syntax-string': {
304
+ readonly light: "oklch(0.50 0.12 145)";
305
+ readonly dark: "oklch(0.60 0.11 145)";
306
+ };
307
+ readonly 'color-syntax-constant': {
308
+ readonly light: "oklch(0.51 0.105 85)";
309
+ readonly dark: "oklch(0.61 0.11 84)";
310
+ };
311
+ readonly 'color-syntax-comment': {
312
+ readonly light: "oklch(0.52 0.01 80)";
313
+ readonly dark: "oklch(0.60 0.005 80)";
314
+ };
315
+ readonly 'color-syntax-function': {
316
+ readonly light: "oklch(0.52 0.12 310)";
317
+ readonly dark: "oklch(0.62 0.11 310)";
318
+ };
319
+ readonly 'color-syntax-type': {
320
+ readonly light: "oklch(0.50 0.085 196)";
321
+ readonly dark: "oklch(0.60 0.10 195)";
322
+ };
323
+ readonly 'color-syntax-punctuation': {
324
+ readonly light: "oklch(0.52 0.01 80)";
325
+ readonly dark: "oklch(0.60 0.005 80)";
326
+ };
327
+ readonly 'color-syntax-variable': {
328
+ readonly light: "oklch(0.26 0.01 80)";
329
+ readonly dark: "oklch(0.82 0.005 80)";
330
+ };
331
+ readonly 'color-syntax-link': {
332
+ readonly light: "var(--color-accent)";
333
+ readonly dark: "var(--color-accent)";
334
+ };
290
335
  };
291
336
  }
292
337
 
package/tokens.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$description": "Remarque design tokens — GENERATED from tokens-core.css + tokens-palette.css by scripts/tokens-json.mjs. Do not edit; the CSS is the source of truth.",
3
3
  "$extensions": {
4
4
  "remarque": {
5
- "version": "0.12.0",
5
+ "version": "0.14.0",
6
6
  "tiers": {
7
7
  "core": "immutable identity — overriding forks the system",
8
8
  "palette": "sanctioned personalization surface — override freely, then run remarque-audit"
@@ -421,6 +421,87 @@
421
421
  "dark": {
422
422
  "$value": "oklch(0.88 0.005 80)"
423
423
  }
424
+ },
425
+ "color-syntax-keyword": {
426
+ "$type": "color",
427
+ "light": {
428
+ "$value": "oklch(0.51 0.12 250)"
429
+ },
430
+ "dark": {
431
+ "$value": "oklch(0.61 0.11 250)"
432
+ }
433
+ },
434
+ "color-syntax-string": {
435
+ "$type": "color",
436
+ "light": {
437
+ "$value": "oklch(0.50 0.12 145)"
438
+ },
439
+ "dark": {
440
+ "$value": "oklch(0.60 0.11 145)"
441
+ }
442
+ },
443
+ "color-syntax-constant": {
444
+ "$type": "color",
445
+ "light": {
446
+ "$value": "oklch(0.51 0.105 85)"
447
+ },
448
+ "dark": {
449
+ "$value": "oklch(0.61 0.11 84)"
450
+ }
451
+ },
452
+ "color-syntax-comment": {
453
+ "$type": "color",
454
+ "light": {
455
+ "$value": "oklch(0.52 0.01 80)"
456
+ },
457
+ "dark": {
458
+ "$value": "oklch(0.60 0.005 80)"
459
+ }
460
+ },
461
+ "color-syntax-function": {
462
+ "$type": "color",
463
+ "light": {
464
+ "$value": "oklch(0.52 0.12 310)"
465
+ },
466
+ "dark": {
467
+ "$value": "oklch(0.62 0.11 310)"
468
+ }
469
+ },
470
+ "color-syntax-type": {
471
+ "$type": "color",
472
+ "light": {
473
+ "$value": "oklch(0.50 0.085 196)"
474
+ },
475
+ "dark": {
476
+ "$value": "oklch(0.60 0.10 195)"
477
+ }
478
+ },
479
+ "color-syntax-punctuation": {
480
+ "$type": "color",
481
+ "light": {
482
+ "$value": "oklch(0.52 0.01 80)"
483
+ },
484
+ "dark": {
485
+ "$value": "oklch(0.60 0.005 80)"
486
+ }
487
+ },
488
+ "color-syntax-variable": {
489
+ "$type": "color",
490
+ "light": {
491
+ "$value": "oklch(0.26 0.01 80)"
492
+ },
493
+ "dark": {
494
+ "$value": "oklch(0.82 0.005 80)"
495
+ }
496
+ },
497
+ "color-syntax-link": {
498
+ "$type": "color",
499
+ "light": {
500
+ "$value": "var(--color-accent)"
501
+ },
502
+ "dark": {
503
+ "$value": "var(--color-accent)"
504
+ }
424
505
  }
425
506
  }
426
507
  }