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 +64 -0
- package/README.md +33 -0
- package/REMARQUE.md +124 -8
- package/package.json +1 -1
- package/scripts/audit.mjs +9 -0
- package/scripts/theme.mjs +118 -6
- package/theme.css +7 -0
- package/tokens-palette.css +41 -0
- package/tokens.d.ts +47 -2
- package/tokens.json +82 -1
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
|
|
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 |
|
|
270
|
-
| Large text on background | 3:1 minimum |
|
|
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
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
|
|
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.
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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':
|
|
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
|
}
|
package/tokens-palette.css
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
}
|