remarque-tokens 0.21.0 → 0.23.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/AGENT_RULES.md +11 -0
- package/CHANGELOG.md +34 -0
- package/README.md +3 -0
- package/REMARQUE.md +268 -4
- package/package.json +14 -4
- package/registry/broadsheet.json +66 -0
- package/registry/essay.json +56 -0
- package/registry/forms.json +61 -0
- package/registry/palette-deck.json +21 -0
- package/registry-item.schema.json +75 -0
- package/registry.json +44 -0
- package/registry.schema.json +40 -0
- package/scripts/audit.mjs +9 -0
- package/scripts/build-registry.mjs +156 -0
- package/scripts/lib/registry-extract.mjs +87 -0
- package/scripts/theme.mjs +121 -3
- package/tokens-palette.css +41 -1
- package/tokens.d.ts +32 -2
- package/tokens.json +55 -1
package/AGENT_RULES.md
CHANGED
|
@@ -6,6 +6,17 @@ Read `REMARQUE.md` first for the full system specification. This file tells you
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## Prefer the Registry Over Transcribing Prose
|
|
10
|
+
|
|
11
|
+
For the Essay Module, the Broadsheet pattern, and Forms (`remarque-tokens/essay`, `remarque-tokens/broadsheet`, `remarque-tokens/forms`), a machine-readable markup-contract registry ships alongside the spec (REMARQUE.md "The Registry"; issue #100). **Agents building a Remarque page SHOULD fetch the relevant registry item and apply its `usage.html`/CSS `content` verbatim, rather than re-typing the markup from this file's or REMARQUE.md's prose.**
|
|
12
|
+
|
|
13
|
+
- Fetch `https://williamzujkowski.github.io/remarque/registry/<name>.json` (`essay`, `broadsheet`, `forms`, or `palette-deck`), or read it from an installed package at `node_modules/remarque-tokens/registry/<name>.json` — same content either way.
|
|
14
|
+
- The known-good markup lives in `files[].content` where `type` is `"remarque:markup"`; the CSS module (if any) is in the `"remarque:css"` entry. Both are the exact, versioned, hash-pinned (`integrity`) bytes — copy them, don't re-derive them from memory.
|
|
15
|
+
- This exists specifically to prevent transcription bugs like #89 (a sidenote `aria-label` that drifted out of DOM order because an agent hand-copied ~80 lines of spec prose instead of applying known-good markup). Reading REMARQUE.md's prose for the *reasoning* behind a contract is still expected; reading it as the *source of the markup you type* is the failure mode the registry exists to close.
|
|
16
|
+
- No CLI installer ships this round — there is no `npx` command that writes these files into a project for you. Fetch the JSON, then apply the markup with your own editing tools.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
9
20
|
## Build Order
|
|
10
21
|
|
|
11
22
|
Execute in this exact order. Do not skip steps. Do not reorder.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,40 @@ 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.23.0 — 2026-07-23
|
|
8
|
+
|
|
9
|
+
Machine-readable markup-contract registry (closes #100) — the ratified program's flagship item. Funded on the panel's amended rationale: it mechanically prevents the markup-contract transcription bug class (the sidenote DOM-order/aria-label failure found in the flagship migration, #89), not on "category-defining" positioning.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **`registry.json` + `registry/{essay,broadsheet,forms,palette-deck}.json`** — four items, shaped like shadcn/ui's `registry-item.json`/`registry.json` (`https://ui.shadcn.com/schema/registry-item.json`, `.../registry.json`), adopted where the shape fits and explicitly NOT a parallel invented schema. Per-item: `name`/`type`/`title`/`description`/`dependencies`/`cssVars`/`docs`/`files` kept from shadcn's vocabulary; `type` mints one honest value (`remarque:contract` — none of shadcn's `registry:lib/component/ui/hook/...` describe a CSS+HTML contract with no installable component); `cssVars` is repurposed from "values to merge" to "the flat list of `--custom-property` names this item's CSS reads via `var(...)`, mechanically grepped"; `docs` is repurposed from a markdown blob to a single HTTPS URL into REMARQUE.md. `registry.json` is a lightweight index (pointer + pinned version/hash per item), not shadcn's full-item embedding — full payloads live only in `registry/<name>.json`. Every field-level adaptation and omission (`tailwind`/`css`/`envVars`/`font`/`extends`/`style`/`iconLibrary`/`baseColor`/`theme`/`files[].target`/`meta` — all omitted, none apply) is documented inline in the two vendored schemas and in REMARQUE.md's new "The Registry" section.
|
|
13
|
+
- **Security (panel-mandated, blocking), all enforced by `scripts/test-registry.mjs`:** every item pins a `version` (the exact package release) and an `integrity` hash (`sha256-<base64>`, W3C Subresource Integrity's own shape, computed over `files` at build time and independently recomputed in the test gate — not just carried over); every `docs`/`homepage` URL is `https://`; no item contains executable content anywhere (`scripts/test-registry.mjs` asserts no file's `content`, across every item, contains the substring `<script`). **`deck.js`** — the Palette Deck's runtime module, the one JS asset this package ships — is deliberately EXCLUDED from the registry entirely rather than included-and-hashed: the panel's "no executable content... files are HTML/CSS data" condition reads as absolute, so the honest call is a zero-`<script>`-tag bar with no case-by-case exception. `registry/palette-deck.json` ships only the `data-theme`/`data-palette` HTML fragment, not the FOUC-restore `<script>` sample documented beside it in REMARQUE.md — consumers still get `deck.js` via the ordinary `remarque-tokens/deck` package import.
|
|
14
|
+
- **Single-sourcing.** Every item's CSS `content` is read verbatim from the real `.css` file at build time (`essay.css`/`broadsheet.css`/`forms.css`) — never a second hand-copied literal. Every item's `usage.html` is extracted from REMARQUE.md's own "Markup contract" fenced samples via a `<!-- registry-usage:<name> -->` marker comment (new `scripts/lib/registry-extract.mjs`, `extractUsageHtml`) — the spec prose stays the ONE hand-authored copy; the registry is a derived build artifact, the same relationship `tokens.json` already has to the CSS.
|
|
15
|
+
- **`scripts/build-registry.mjs`** (`--check` for CI freshness, mirroring `scripts/tokens-json.mjs`) generates `registry.json` + `registry/*.json` in one pass from the CSS + REMARQUE.md markers + `package.json`'s version.
|
|
16
|
+
- **`scripts/test-registry.mjs`** (the enforce-don't-instruct gate this issue is funded on) — schema validation (`ajv`, draft 2020-12, with negative fixtures proving the schemas actually reject malformed input) plus mechanical re-derivation of the exact contract lines #89 was about, parsed straight out of each generated `usage.html`: every essay `.remarque-sidenote-ref` carries `aria-label="Note N"` and refs/notes strictly alternate in DOM order; the essay TOC rail carries `aria-label`; every broadsheet `.remarque-entry-numeral` carries `data-entry-number`; every forms `<input id>` has a matching `<label for>` and every `aria-describedby` target id exists in the sample.
|
|
17
|
+
- **Vendored schemas** `registry-item.schema.json` + `registry.schema.json` (draft 2020-12, hand-authored/adapted from shadcn's — see "Added" above for the field-by-field rationale).
|
|
18
|
+
- **CI wiring** (`deploy.yml`): `registry.json`/`registry/*.json` freshness-gated alongside `tokens.json` (`node scripts/build-registry.mjs --check`); `scripts/test-registry.mjs` runs alongside the other fixture suites.
|
|
19
|
+
- **Served by the demo site** (`site/scripts/copy-tokens-json.mjs`, extended): `registry.json`, `registry/*.json`, and both schemas copy into `public/` alongside `tokens.json`, published at `/registry.json`, `/registry/<name>.json`, `/registry-item.schema.json`, `/registry.schema.json`.
|
|
20
|
+
- **npm packaging:** `package.json` `files`/`exports` extended — `remarque-tokens/registry.json`, `remarque-tokens/registry/*` (subpath pattern → `registry/*.json`), `remarque-tokens/registry-item.schema.json`, `remarque-tokens/registry.schema.json`.
|
|
21
|
+
- **Docs.** REMARQUE.md's new "The Registry" section (what it is, the shape adaptation, the security model, the single-sourcing mechanism, what it deliberately is NOT — no CLI installer, no archetype "starter-page" items this round — and the fetch URLs). AGENT_RULES.md's new "Prefer the Registry Over Transcribing Prose" section — agents building Remarque pages should fetch a registry item rather than re-typing markup from spec prose. README's "For AI Agents" section and Files table updated with the registry's live endpoints.
|
|
22
|
+
- **Scope note:** the mandatory four items (essay, broadsheet, forms, palette-deck) are the whole of this release — per-archetype "starter-page" items were floated as an IF-cheap extra and SKIPPED: archetype pages live as demo-specific Astro files, not as a reusable module with one canonical markup sample, so there was nothing to single-source without inventing a new "canonical" sample from scratch.
|
|
23
|
+
- Version-only regeneration of `tokens.json`/`tokens.d.ts` (no CSS/token changes this release).
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- **`peerDependencies` range for `@williamzujkowski/oklch-terminal-themes` was stale and broke every real consumer install.** Set to `>=0.1.0 <0.3.0` back when the dataset was at 0.1.0 and never widened as it shipped 0.4.0 (issue #94's dataviz fields) and 0.5.0 (the current devDependency pin) — `npm install remarque-tokens @williamzujkowski/oklch-terminal-themes@0.5.0` failed with `ERESOLVE` for any consumer following the documented "Color Providers" instructions, with no `--legacy-peer-deps` escape hatch offered or implied anywhere in the docs. Invisible from inside this repo because `npm ci` here installs both packages directly as siblings (root devDependency + peerDependency), which never exercises npm's peer-resolution algorithm the way a consumer's plain `npm install` does. Widened to `>=0.1.0 <1.0.0` — the dataset's changes have been strictly additive since 0.1.0 and `remarque-theme`/`remarque-audit` runtime-validate every field they read (see e.g. the dataviz-fallback error path added in 0.22.0), so the honest ceiling is the dataset's own 1.0 breaking-change boundary, not a minor-by-minor cap that recreates this exact break on every upstream release.
|
|
27
|
+
- **New `scripts/test-pack.mjs`** — a real consumer smoke test closing the gap that let the above go unnoticed: `npm pack`s the actual tarball (not the repo checkout), installs it plus the pinned dataset devDependency version into a scratch project with plain `npm install` (deliberately no `--legacy-peer-deps` — the assertion IS that peer resolution succeeds), then runs `npx remarque-theme remarque-light -o out.css` and `npx remarque-audit --palette out.css --src . --json` from that install and asserts `passed: true`. Wired into `deploy.yml` after the other gates (needs registry network access, which the existing devDependency-install step already has).
|
|
28
|
+
|
|
29
|
+
Dataviz categorical tokens — bridge-derived, golden-gated (closes #94). The syntax-palette pattern's fourth application, closing the "Dataviz Tokens" section's long-standing "guidance, not a palette" gap now that the upstream dataset ships a per-theme `dataviz` block (`@williamzujkowski/oklch-terminal-themes` 0.5.0).
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
- **6 `--color-viz-1..6` palette-tier slots**, hand-authored in both themes from `remarque-light`/`remarque-dark`'s `dataviz.categorical` block, serialized as round numbers (identity/serialization contract, same as `--color-syntax-*`). 6 slots, not 8 — the dataset's own floor (516 of 633 corpus themes ship exactly 6). Every slot ≥ **3:1** against `--color-bg` in both themes — Carbon's mark-on-background line, not text's 4.5:1 (marks are read at a glance, not continuously like prose). Added to `tokens-palette.css`'s `:root`, `@media (prefers-color-scheme: dark)`, `[data-theme="dark"]`/`:root.dark`, and the demo's `[data-theme="light"]` mirror.
|
|
33
|
+
- **`remarque-theme` dataviz derivation.** Derives all 6 slots from the source theme's own `dataviz.categorical` array (first 6 entries, dataset order, per theme independently — order is **not** reconciled hue-for-hue across a light/dark pair; verified the upstream order itself differs between `remarque-light` and `remarque-dark`, consistent with this file's existing "dark mode is independently tuned, not inverted" stance). Same keep-if-passing-else-solve pattern as every other slot, chroma capped at 0.14. **Fallback:** an installed dataset older than 0.5.0 (still legal under the `>=0.1.0` peerDependencies floor) has no `dataviz` block — `remarque-theme` errors loudly naming the missing field and the upgrade path rather than synthesizing a guessed ramp from raw ANSI colors (the upstream categorical-selection algorithm is undocumented; guessing at it could silently diverge from a real 0.5.0+ dataset).
|
|
34
|
+
- **`remarque-theme --dataviz`** (optional, judgment-call item). Emits `--viz-sequential-N`/`--viz-diverging-N` custom properties from the dataset's `dataviz.sequential`/`.diverging` ramps — gamut-clamped but NOT contrast-solved (a sequential ramp's low end is designed to sit near `--color-bg`) and NOT audited/golden-gated like `--color-viz-1..6` (these are per-use ramps, not identity tokens). Purely additive: omitting the flag leaves the derived `--color-*` output byte-identical to before.
|
|
35
|
+
- **Audit extension** (`scripts/audit.mjs`, `scripts/theme.mjs` self-verify) — 6 new `CHECKS` pairings (`color-viz-1..6` vs `color-bg` at 3.0). `scripts/test-audit.mjs` fixtures updated with the 6 slots plus a must-fail case (`viz-slot-fails.css`) proving the pairings are actually wired in.
|
|
36
|
+
- **Golden gate** (`scripts/palette-golden.mjs`) extended to the 6 new slots — ΔE2000 = 0.000 in both themes (the hand-authored values are literal transcriptions of the dataset's own numbers, unlike the solved slots elsewhere in the palette).
|
|
37
|
+
- **REMARQUE.md "Dataviz Tokens"** rewritten: the 6 shipped tokens with light/dark values and ratios, the 3:1 mark-threshold rationale, a mandatory non-color-redundancy rule (shape/pattern/label — never color alone, per Carbon and tsundoku's precedent), and a documented recipe for sequential/diverging ramps (direct JSON consumption from the dataset, or `remarque-theme --dataviz`). New Enforcement Checklist line.
|
|
38
|
+
- **Demo** (`site/src/pages/tokens.astro`): a "Dataviz" section rendering all 6 swatches in both themes with contrast ratios and a shape-coded legend (circle/square/triangle) demonstrating the non-color-redundancy rule directly. Placed on `tokens.astro`, which carries zero visual-regression baselines — this ships with no new/changed screenshot baselines.
|
|
39
|
+
- **Repin**: `@williamzujkowski/oklch-terminal-themes` devDependency 0.4.0 → 0.5.0 (exact). Corpus test (`scripts/test-theme.mjs`) still 65/65 pairs passing — the dataset gained fields (`dataviz` on every theme), not themes, matching the repin pattern's usual shape.
|
|
40
|
+
|
|
7
41
|
## 0.21.0 — 2026-07-23
|
|
8
42
|
|
|
9
43
|
Forced-colors (Windows High Contrast Mode) + `prefers-contrast: more` support (closes #93) — the ratified program's highest-value accessibility item.
|
package/README.md
CHANGED
|
@@ -71,6 +71,7 @@ Packaging for agent tooling:
|
|
|
71
71
|
- **npm exports:** `remarque-tokens/agent-rules` (→ `AGENT_RULES.md`) and `remarque-tokens/spec` (→ `REMARQUE.md`), alongside the existing `remarque-tokens/tokens.json`, so a project can point an agent at `node_modules/remarque-tokens/AGENT_RULES.md` without hardcoding a filename.
|
|
72
72
|
- **Claude Code skill:** [`.claude/skills/remarque/SKILL.md`](.claude/skills/remarque/SKILL.md) — triggers on "remarque" / "design system" / new-page work, loads all three files, and states the tier rules, the audit command, and the two build-time pitfalls (unlayered-token-import, string-form `@import`) that pass a green build while silently breaking.
|
|
73
73
|
- **Live tokens endpoint:** the demo site serves the current `tokens.json` at **https://williamzujkowski.github.io/remarque/tokens.json**, and its schema at **https://williamzujkowski.github.io/remarque/tokens.schema.json** — a remote agent can fetch current token values (and validate their shape) directly instead of trusting training data.
|
|
74
|
+
- **Markup-contract registry:** a shadcn-`registry-item.json`-shaped, version-pinned, hash-verified registry of known-good markup for the Essay/Broadsheet/Forms/Palette Deck modules — **https://williamzujkowski.github.io/remarque/registry.json** (index) and **https://williamzujkowski.github.io/remarque/registry/essay.json** (per item; also `broadsheet`/`forms`/`palette-deck`). Fetch and apply the markup instead of transcribing it from spec prose — see REMARQUE.md's "The Registry" and AGENT_RULES.md's "Prefer the Registry Over Transcribing Prose."
|
|
74
75
|
|
|
75
76
|
## Files
|
|
76
77
|
|
|
@@ -86,6 +87,8 @@ Packaging for agent tooling:
|
|
|
86
87
|
| `scripts/drift-check.mjs` | `npx remarque-drift` — token drift check for consumers (`--json` for structured output) |
|
|
87
88
|
| `tokens.json` + `tokens.d.ts` | Generated machine-readable token inventory + TypeScript types (`scripts/tokens-json.mjs`) |
|
|
88
89
|
| `tokens.schema.json` | Generated JSON Schema (draft 2020-12) for `tokens.json`, published alongside it |
|
|
90
|
+
| `registry.json` + `registry/` | Generated markup-contract registry index + per-item files for essay/broadsheet/forms/palette-deck (`scripts/build-registry.mjs`) — see REMARQUE.md "The Registry" |
|
|
91
|
+
| `registry-item.schema.json` + `registry.schema.json` | Generated JSON Schemas (draft 2020-12) for the per-item and index registry files |
|
|
89
92
|
| `fonts.css` + `fonts/` | Self-hosted @font-face declarations and woff2 files (no CDN requests) |
|
|
90
93
|
| `tailwind.config.js` | Tailwind CSS **v3** configuration (v4 projects use an `@theme` block instead) |
|
|
91
94
|
| `package.json` | npm package manifest for `remarque-tokens` |
|
package/REMARQUE.md
CHANGED
|
@@ -146,6 +146,8 @@ Remarque uses a three-slot font system. Each slot has a strict role, and each sl
|
|
|
146
146
|
|
|
147
147
|
This is why the measure is palette-tier: it is a property of the font choice, not of the system's identity. The *target character count* is the invariant.
|
|
148
148
|
|
|
149
|
+
**Known limitation: Latin script only.** The table above, and the `ch`-based measure math behind it, assume Latin-alphabet conventions — character width roughly proportional to em-height, word-space-separated tokens, left-to-right flow. None of that holds for CJK, Arabic, or Devanagari body text: CJK measure is conventionally counted in full-width characters per line, a different unit than a Latin `ch` entirely; Arabic's contextual letterforms and right-to-left flow change what "character count" even means; Devanagari's conjunct consonants and matras make per-character width too irregular for a single ch-target to describe. Remarque has not derived a measure-compensation row for any non-Latin script — a site setting CJK/Arabic/Devanagari body copy needs to work out its own measure the way this table works out Latin ones; the ch-target does not transfer by assumption. Named here honestly, in the same spirit as this spec's Butterick line-spacing deviation write-up further down, rather than left for a future non-Latin consumer to discover the hard way.
|
|
150
|
+
|
|
149
151
|
---
|
|
150
152
|
|
|
151
153
|
## Editorial Microtypography
|
|
@@ -256,14 +258,45 @@ Reference/Docs and Project Dossier pages often need to show several screenshots
|
|
|
256
258
|
|
|
257
259
|
## Dataviz Tokens
|
|
258
260
|
|
|
259
|
-
Charts are
|
|
261
|
+
Charts are a first-class Remarque surface: `tokens-palette.css` ships 6 hand-authored `--color-viz-*` categorical slots, bridge-derived and golden-gated against the upstream `remarque-light`/`remarque-dark` themes' `dataviz.categorical` block the same way the rest of the default palette is (issue #94 — the syntax-palette pattern's fourth application). The rest of a chart still maps onto vocabulary that already exists, so it survives a palette swap the same way prose and chrome do:
|
|
260
262
|
|
|
261
263
|
- **Grid lines** use `--color-border` — never a separate "chart gray." Grid lines are structural, not decorative, and already have a sanctioned quiet role.
|
|
262
264
|
- **Axis text and tick labels** use `--font-mono` at `--text-meta`, `--color-muted` — the same register as metadata rows and captions, never the body or display face.
|
|
263
|
-
- **Categorical color
|
|
264
|
-
|
|
265
|
+
- **Categorical color** uses `--color-viz-1` through `--color-viz-6` (below) — never `--color-accent`, so chart color is never mistaken for the system's one interactive signal.
|
|
266
|
+
|
|
267
|
+
### Slots
|
|
268
|
+
|
|
269
|
+
6 slots, not 8 — the upstream dataset's own floor: 516 of the 633 corpus themes ship exactly 6 `dataviz.categorical` entries, so shipping 8 would leave 2 unfillable on most themes. Every slot is verified ≥ **3:1** against `--color-bg` in both themes — Carbon's mark-on-background line, not text's 4.5:1. The distinction is deliberate: a chart mark is a small area read at a glance and identified by hue difference from its neighbors, not a string of characters read continuously letter-by-letter, so it gets the WCAG 1.4.11 non-text threshold (the same one `--color-border-bold` already uses for functional borders), not 1.4.3's text threshold.
|
|
270
|
+
|
|
271
|
+
| Slot | Light | Dark |
|
|
272
|
+
|---|---|---|
|
|
273
|
+
| `--color-viz-1` | `oklch(0.538 0.121 250.5)` — 4.74:1 | `oklch(0.708 0.129 249.5)` — 7.53:1 |
|
|
274
|
+
| `--color-viz-2` | `oklch(0.541 0.111 85.5)` — 4.73:1 | `oklch(0.712 0.13 85.3)` — 7.54:1 |
|
|
275
|
+
| `--color-viz-3` | `oklch(0.524 0.12 24.6)` — 5.35:1 | `oklch(0.724 0.129 310.2)` — 7.55:1 |
|
|
276
|
+
| `--color-viz-4` | `oklch(0.499 0.12 144.8)` — 5.32:1 | `oklch(0.725 0.129 25.4)` — 7.55:1 |
|
|
277
|
+
| `--color-viz-5` | `oklch(0.524 0.121 309.6)` — 5.33:1 | `oklch(0.696 0.129 144.9)` — 7.53:1 |
|
|
278
|
+
| `--color-viz-6` | `oklch(0.528 0.09 195.8)` — 4.74:1 | `oklch(0.697 0.119 194.8)` — 7.57:1 |
|
|
279
|
+
|
|
280
|
+
**Order is not reconciled hue-for-hue across the pair.** The upstream `dataviz.categorical` order for `remarque-light` (blue, yellow, red, green, purple, cyan) does not match `remarque-dark`'s (blue, yellow, purple, red, green, cyan) — verified directly against the shipped dataset. `--color-viz-3` is reddish in light mode and purplish in dark mode. This is the same stance the rest of this file already takes for every other slot: **dark mode is independently tuned, not an inversion** — it is not a bug that a toggle recolors series 3, any more than it is a bug that `--color-fg`'s exact hue differs by theme.
|
|
281
|
+
|
|
282
|
+
**Non-color redundancy (Carbon precedent, mandatory):** never use `--color-viz-*` as the *sole* distinguisher between series. Pair every categorical color with a second channel — a distinct marker shape (circle/square/triangle), a line dash pattern, or a direct end-of-line/legend label — so the chart still communicates under color-vision deficiency or `forced-colors: active` (which the marks themselves do not attempt to survive; see "Forced Colors & Contrast Preferences" for what *is* covered). This mirrors [tsundoku](https://github.com/williamzujkowski/tsundoku)'s `DESIGN-NOTES.md`, which documents an 8-hue "orthogonal category system" kept deliberately separate from its accent — the categorical slots above formalize that instinct as an audited, bridge-derived token set instead of a hand-picked one-off.
|
|
283
|
+
|
|
284
|
+
### Derivation
|
|
265
285
|
|
|
266
|
-
|
|
286
|
+
`remarque-theme` derives all 6 slots from the source theme's own `dataviz.categorical` array (dataset 0.5.0+) — first 6 entries, in the dataset's order, same keep-if-passing-else-solve pattern as every other slot, targeted at `--color-bg` (not `--color-surface`) at 3:1. Chroma is capped at 0.14, the same ceiling as `--color-accent`/`--color-syntax-*`. `scripts/palette-golden.mjs` extends its ΔE2000 ≤ 2.0 gate to all 6 slots.
|
|
287
|
+
|
|
288
|
+
**Fallback (dataset predates the field):** an installed `@williamzujkowski/oklch-terminal-themes` older than 0.5.0 — still legal under this package's `>=0.1.0` peerDependencies floor — has no `dataviz` block at all. `remarque-theme` does **not** attempt to synthesize a categorical ramp from raw ANSI colors in that case: the upstream categorical-selection algorithm is undocumented, and approximating it could silently diverge from what a real 0.5.0+ dataset ships. It errors loudly instead, naming the missing field and the upgrade path — the smaller, honest option over a plausible-looking guess.
|
|
289
|
+
|
|
290
|
+
### Sequential / diverging ramps
|
|
291
|
+
|
|
292
|
+
Sequential and diverging ramps are **not** shipped as tokens — a fixed `--color-viz-seq-N` token set would misrepresent them, because a ramp's *step count* is dataset-defined (6-8, whatever the source theme carries) and a specific ramp is chosen per-use (a heatmap's sequential scale, a signed-difference chart's diverging scale), not once per theme like a categorical identity slot. Two ways to consume them:
|
|
293
|
+
|
|
294
|
+
1. **Direct JSON consumption** — read the dataset's `dataviz.sequential`/`dataviz.diverging` arrays straight from the installed package:
|
|
295
|
+
```js
|
|
296
|
+
const { sequential, diverging } = require('@williamzujkowski/oklch-terminal-themes/themes/remarque-light.json').dataviz;
|
|
297
|
+
```
|
|
298
|
+
Each entry carries `oklch` (structured `{l, c, h}`) and `oklchCss` (a ready-to-use `oklch(...)` string) — no parsing required.
|
|
299
|
+
2. **`remarque-theme --dataviz`** — emits `--viz-sequential-1..N` and `--viz-diverging-1..N` custom properties (`N` = the source theme's own ramp length) alongside the normal derived palette. Gamut-clamped but **not** contrast-solved (a sequential ramp's low end is designed to sit near `--color-bg`; solving it to a contrast floor would defeat the ramp) — advisory output, not audited or golden-gated like `--color-viz-1..6`. A future `remarque-audit --json`/`remarque-theme --json` could expose the same arrays as structured output; today, `--dataviz`'s CSS-property emission and direct JSON consumption above cover the need.
|
|
267
300
|
|
|
268
301
|
---
|
|
269
302
|
|
|
@@ -491,11 +524,20 @@ Every PR that ships Remarque pages MUST pass (`npm run audit` automates the colo
|
|
|
491
524
|
- [ ] Body line-height ≥ 1.5 (Remarque target: 1.75).
|
|
492
525
|
- [ ] Every `--color-syntax-*` slot ≥ 4.5:1 against `--color-code-bg` in both themes (see "Syntax Highlighting").
|
|
493
526
|
- [ ] Every `--color-error`/`--color-success`/`--color-warning`/`--color-disabled` ≥ 4.5:1 against `--color-bg` **and** `--color-surface` in both themes; every `-subtle` background keeps `--color-fg` ≥ 4.5:1 on it (see "State Colors"). State colors used only for feedback moments, never decoration.
|
|
527
|
+
- [ ] Every `--color-viz-*` slot ≥ 3:1 against `--color-bg` in both themes (see "Dataviz Tokens") — the mark-on-background line, not text's 4.5:1. Never used as the sole distinguisher between series — pair with a non-color redundancy (shape, pattern, or a direct label).
|
|
494
528
|
- [ ] No bare `z-index` number in consumer CSS — reference `tokens-core.css`'s `--z-*` scale (see "Stacking").
|
|
495
529
|
- [ ] Any new color-only affordance (hover/focus signal, state indicator, structural divider) is checked against forced-colors mode — does it survive, or does it need a `@media (forced-colors: active)` fallback? (see "Forced Colors & Contrast Preferences")
|
|
496
530
|
|
|
497
531
|
Agents reviewing PRs should reject changes that violate any line above without explicit rationale.
|
|
498
532
|
|
|
533
|
+
### APCA: Considered, Not Adopted (2026-07-23)
|
|
534
|
+
|
|
535
|
+
APCA (Accessible Perceptual Contrast Algorithm) was pulled into the WCAG3 working draft in 2023 and remains exploratory — no ratified conformance model specifies it, and that status is unchanged as of this writing. Remarque holds **WCAG 2.x's ratio-based contrast** as the enforcement gate (see "Contrast Ratios" above and the Enforcement Checklist) rather than building the audit toward an unratified algorithm.
|
|
536
|
+
|
|
537
|
+
This is a deliberate policy/data split, not an oversight: the upstream `@williamzujkowski/oklch-terminal-themes` dataset is adding APCA Lc values as *data* on its themes (oklch-terminal-themes#151) — a reasonable thing for a dataset to expose so consumers can experiment. Remarque's own audit gate does not consume it. Data availability and policy adoption are two different questions, and this project is only answering one of them right now.
|
|
538
|
+
|
|
539
|
+
**Revisit trigger:** a ratified WCAG3 conformance model that specifies an actual APCA (or successor) algorithm and threshold set. Until then, this is dated rather than re-litigated on every PR that mentions contrast.
|
|
540
|
+
|
|
499
541
|
---
|
|
500
542
|
|
|
501
543
|
## Lineage: Butterick's Five Rules
|
|
@@ -721,6 +763,10 @@ for the value-by-value mapping).
|
|
|
721
763
|
|
|
722
764
|
### Markup contract
|
|
723
765
|
|
|
766
|
+
<!-- registry-usage:essay — scripts/build-registry.mjs (issue #100) extracts
|
|
767
|
+
the fenced block immediately below into registry/essay.json's
|
|
768
|
+
usage.html verbatim. Keep this the single, hand-authored known-good
|
|
769
|
+
sample — do not fork a second copy for the registry. -->
|
|
724
770
|
```html
|
|
725
771
|
<article class="remarque-essay">
|
|
726
772
|
<header class="content-reading">
|
|
@@ -902,6 +948,9 @@ Four primitives, composed independently — a page can use any subset:
|
|
|
902
948
|
|
|
903
949
|
### Markup contract
|
|
904
950
|
|
|
951
|
+
<!-- registry-usage:broadsheet — scripts/build-registry.mjs (issue #100)
|
|
952
|
+
extracts the fenced block immediately below into
|
|
953
|
+
registry/broadsheet.json's usage.html verbatim. -->
|
|
905
954
|
```html
|
|
906
955
|
<header class="remarque-masthead">
|
|
907
956
|
<p class="remarque-masthead-kicker"><span>Field Notes</span></p>
|
|
@@ -1032,6 +1081,9 @@ existing literal.
|
|
|
1032
1081
|
|
|
1033
1082
|
### Markup contract
|
|
1034
1083
|
|
|
1084
|
+
<!-- registry-usage:forms — scripts/build-registry.mjs (issue #100) extracts
|
|
1085
|
+
the fenced block immediately below into registry/forms.json's
|
|
1086
|
+
usage.html verbatim. -->
|
|
1035
1087
|
```html
|
|
1036
1088
|
<div class="remarque-field">
|
|
1037
1089
|
<label class="remarque-field-label" for="email">Email</label>
|
|
@@ -1239,6 +1291,15 @@ Set (or clear) `data-palette` on `<html>`, alongside the existing
|
|
|
1239
1291
|
carries *both* light and dark halves, so the light/dark toggle keeps
|
|
1240
1292
|
working no matter which palette (or none) is active:
|
|
1241
1293
|
|
|
1294
|
+
<!-- registry-usage:palette-deck — scripts/build-registry.mjs (issue #100)
|
|
1295
|
+
extracts the fenced block immediately below into
|
|
1296
|
+
registry/palette-deck.json's usage.html verbatim. The FOUC-safe
|
|
1297
|
+
`<script>` snippet a few lines down is deliberately NOT marked for
|
|
1298
|
+
extraction — see REMARQUE.md "The Registry" and the PR description
|
|
1299
|
+
for why (the registry's blocking no-executable-content condition is
|
|
1300
|
+
held to an unambiguous zero-`<script>`-tag bar across every item;
|
|
1301
|
+
that snippet's full text still lives here, one section up, and is
|
|
1302
|
+
linked from the registry item's `docs` URL). -->
|
|
1242
1303
|
```html
|
|
1243
1304
|
<html data-theme="dark" data-palette="gruvbox">
|
|
1244
1305
|
```
|
|
@@ -1291,6 +1352,175 @@ switching between them became the only piece worth a shared module.
|
|
|
1291
1352
|
|
|
1292
1353
|
---
|
|
1293
1354
|
|
|
1355
|
+
## The Registry
|
|
1356
|
+
|
|
1357
|
+
A machine-readable markup-contract registry — `registry.json` (index) +
|
|
1358
|
+
`registry/<name>.json` (four full items: `essay`, `broadsheet`, `forms`,
|
|
1359
|
+
`palette-deck`) — shipped for the ratified program's flagship item
|
|
1360
|
+
(issue #100). The funding rationale is narrow and mechanical, not
|
|
1361
|
+
"category-defining": it exists to prevent the exact bug class found in
|
|
1362
|
+
the flagship migration's sidenote implementation (#89,
|
|
1363
|
+
williamzujkowski.github.io#380) — an aria-label that drifted out of DOM
|
|
1364
|
+
order because the markup contract lived only as ~80 lines of spec prose
|
|
1365
|
+
an agent had to transcribe by hand. A registry item is that same known-
|
|
1366
|
+
good markup as versioned, hash-pinned DATA an agent fetches and applies
|
|
1367
|
+
instead.
|
|
1368
|
+
|
|
1369
|
+
**Shape.** Adopted from shadcn/ui's `registry-item.json`/`registry.json`
|
|
1370
|
+
(`https://ui.shadcn.com/schema/registry-item.json`,
|
|
1371
|
+
`https://ui.shadcn.com/schema/registry.json`) — field NAMES that fit a
|
|
1372
|
+
CSS+HTML contract are kept as-is (`name`, `title`, `description`,
|
|
1373
|
+
`dependencies`, `registryDependencies`, `files[].path`/`content`/`type`,
|
|
1374
|
+
`cssVars`, `docs`); this is deliberately **not** a parallel invented
|
|
1375
|
+
schema. The two vendored, hand-authored schemas
|
|
1376
|
+
(`registry-item.schema.json`, `registry.schema.json` at the package
|
|
1377
|
+
root) document every adaptation and omission inline, in the property
|
|
1378
|
+
descriptions themselves — the summary:
|
|
1379
|
+
|
|
1380
|
+
- **`type` is `"remarque:contract"`**, not one of shadcn's `registry:*`
|
|
1381
|
+
values (`registry:lib`/`component`/`ui`/`hook`/`page`/...). None of
|
|
1382
|
+
those describe a dependency-free CSS module plus its markup sample —
|
|
1383
|
+
every shadcn value assumes an installable React-ecosystem artifact.
|
|
1384
|
+
One honest value covers all four items today, since they're all the
|
|
1385
|
+
same KIND of thing; a second value would only be minted for a future
|
|
1386
|
+
item that is a genuinely different kind of contract.
|
|
1387
|
+
- **`cssVars` is repurposed**: shadcn's `cssVars` holds theme
|
|
1388
|
+
{`theme`/`light`/`dark`} VALUES to merge into a consumer's stylesheet.
|
|
1389
|
+
This registry ships whole CSS files verbatim instead (single-sourced
|
|
1390
|
+
from the real `.css` at build time — see "Single-sourcing" below), so
|
|
1391
|
+
there's nothing to merge; `cssVars` here is the flat, deduped list of
|
|
1392
|
+
`--custom-property` NAMES the item's CSS actually reads via `var(...)`
|
|
1393
|
+
— the palette-tier tokens it consumes, mechanically grepped, never
|
|
1394
|
+
hand-maintained.
|
|
1395
|
+
- **`docs` is a URL, not markdown.** shadcn's `docs` is a documentation
|
|
1396
|
+
blob; this registry's `docs` is a single HTTPS link into this file's
|
|
1397
|
+
relevant section. The registry doesn't duplicate spec prose — it
|
|
1398
|
+
points back at the one place that prose lives.
|
|
1399
|
+
- **Omitted fields** (documented once here, not repeated per-field):
|
|
1400
|
+
`tailwind`/`css`/`envVars` (no Tailwind config, direct CSS injection,
|
|
1401
|
+
or env vars — these items ship whole files, not fragments to splice
|
|
1402
|
+
in), `font`/`extends`/`style`/`iconLibrary`/`baseColor`/`theme` (all
|
|
1403
|
+
`registry:font`/`registry:base`-specific — not applicable to any item
|
|
1404
|
+
here), `files[].target` (no CLI installer — see "What this deliberately
|
|
1405
|
+
is NOT"), `meta` (no per-item metadata that doesn't already have a
|
|
1406
|
+
named field).
|
|
1407
|
+
- **`registry.json` is a lightweight index**, not shadcn's full-item
|
|
1408
|
+
embedding. shadcn's `items` array holds complete registry-item objects
|
|
1409
|
+
inline; this index instead lists one pointer per item (`name`, `type`,
|
|
1410
|
+
`title`, `description`, `version`, `integrity`, `file`) and leaves the
|
|
1411
|
+
full payload — all file content — in `registry/<name>.json` only, so a
|
|
1412
|
+
consumer can discover what exists and check its pinned hash before
|
|
1413
|
+
deciding whether to fetch the full item.
|
|
1414
|
+
|
|
1415
|
+
**Security (panel-mandated, blocking).** Every item carries a `version`
|
|
1416
|
+
(the exact `remarque-tokens` release it was generated from) and an
|
|
1417
|
+
`integrity` hash (`sha256-<base64>`, the same shape as W3C Subresource
|
|
1418
|
+
Integrity — computed over the item's `files` at build time by
|
|
1419
|
+
`scripts/lib/registry-extract.mjs`'s `sha256OfFiles`); `registry.json`'s
|
|
1420
|
+
index mirrors both per item, so a consumer can check the pin before
|
|
1421
|
+
fetching the full payload and must recheck it against what it actually
|
|
1422
|
+
receives. Every `docs`/`homepage` URL is `https://`. No item contains
|
|
1423
|
+
executable content — `scripts/test-registry.mjs` asserts no file's
|
|
1424
|
+
`content`, across every item without exception, contains the substring
|
|
1425
|
+
`<script`.
|
|
1426
|
+
|
|
1427
|
+
That last rule is why `deck.js` — the Palette Deck's runtime module, a
|
|
1428
|
+
dependency-free ~60-line ESM file, and the one JS asset this package
|
|
1429
|
+
ships — is **not embedded in the registry at all**, even hashed and
|
|
1430
|
+
labeled. The panel's condition reads as an absolute ("no executable
|
|
1431
|
+
content in registry items ... files are HTML/CSS data"), and the honest
|
|
1432
|
+
call under a *blocking* security condition is to hold that line without
|
|
1433
|
+
exception rather than carve out a documented-but-included special case.
|
|
1434
|
+
Concretely: `registry/palette-deck.json` ships **only** the
|
|
1435
|
+
`data-theme`/`data-palette` HTML fragment from this file's "Markup/wiring
|
|
1436
|
+
contract" section — it does *not* include the FOUC-safe restore
|
|
1437
|
+
`<script>` sample documented a few lines below that fragment, even
|
|
1438
|
+
though that snippet is (arguably) still "just an HTML example." Holding
|
|
1439
|
+
every item to a zero-`<script>`-tag bar, with no case-by-case judgment
|
|
1440
|
+
call about which scripts "count," is a simpler rule to mechanically
|
|
1441
|
+
enforce forever than "no script tags, except this one, which is fine
|
|
1442
|
+
because—". Consumers keep getting `deck.js` the normal way — the
|
|
1443
|
+
`remarque-tokens/deck` package import, listed as an ordinary
|
|
1444
|
+
`dependencies` entry on every item — and the FOUC snippet's full text
|
|
1445
|
+
from this section, linked via the item's `docs` URL.
|
|
1446
|
+
|
|
1447
|
+
**Modeling the tokens dependency.** essay/broadsheet/forms all consume
|
|
1448
|
+
palette-tier tokens, and the issue asked whether `tokens` should be a
|
|
1449
|
+
fifth registry item or a documented external dependency. It's the
|
|
1450
|
+
latter: every item's `dependencies` array lists `"remarque-tokens"` — an
|
|
1451
|
+
ordinary npm dependency, the same field shadcn uses for npm packages a
|
|
1452
|
+
component needs. Modeling tokens as a *registry item* would mean
|
|
1453
|
+
re-embedding (a subset of, or all of) `tokens.json`'s content a second
|
|
1454
|
+
time in a differently-shaped artifact; `tokens.json`/`tokens.schema.json`
|
|
1455
|
+
already exist as the purpose-built machine-readable form of the tokens
|
|
1456
|
+
(issue #99), served at the same site alongside this registry. Depending
|
|
1457
|
+
on the package that ships them is the smaller, honest mechanism.
|
|
1458
|
+
|
|
1459
|
+
**Single-sourcing.** Every item's CSS `content` is read verbatim from the
|
|
1460
|
+
real `.css` file at build time (`essay.css`, `broadsheet.css`,
|
|
1461
|
+
`forms.css`) — never a second hand-copied literal. Every item's
|
|
1462
|
+
`usage.html` is extracted from THIS file's own "Markup contract" fenced
|
|
1463
|
+
```html blocks via an HTML-comment marker
|
|
1464
|
+
(`<!-- registry-usage:<name> -->`) placed immediately before the fence —
|
|
1465
|
+
`scripts/lib/registry-extract.mjs`'s `extractUsageHtml` finds the marker,
|
|
1466
|
+
then the next fenced block, and lifts its content verbatim. This spec's
|
|
1467
|
+
prose stays the ONE hand-authored copy of each known-good sample; the
|
|
1468
|
+
registry is a build artifact derived from it, exactly like `tokens.json`
|
|
1469
|
+
is derived from the CSS. `scripts/build-registry.mjs --check` gates
|
|
1470
|
+
freshness in CI the same way `scripts/tokens-json.mjs --check` already
|
|
1471
|
+
does for tokens.
|
|
1472
|
+
|
|
1473
|
+
**Validation (the enforce-don't-instruct house pattern).**
|
|
1474
|
+
`scripts/test-registry.mjs` is the CI gate, wired alongside the other
|
|
1475
|
+
freshness/fixture checks in `deploy.yml`. It re-derives, mechanically,
|
|
1476
|
+
the exact contract lines this issue is funded on — not by trusting the
|
|
1477
|
+
prose, by parsing the generated `usage.html` itself:
|
|
1478
|
+
|
|
1479
|
+
- Every essay `.remarque-sidenote-ref` carries `aria-label="Note N"`, and
|
|
1480
|
+
refs/notes strictly alternate in DOM order — the precise shape of the
|
|
1481
|
+
#89 bug.
|
|
1482
|
+
- The essay TOC rail (`.remarque-toc-rail`) carries `aria-label`.
|
|
1483
|
+
- Every broadsheet `.remarque-entry-numeral` carries `data-entry-number`.
|
|
1484
|
+
- Every forms `<input id="...">` has a matching `<label for="...">`, and
|
|
1485
|
+
every `aria-describedby` target id exists in the sample.
|
|
1486
|
+
- No file, in any item, contains `<script` (the no-executable-content
|
|
1487
|
+
rule above).
|
|
1488
|
+
|
|
1489
|
+
Plus the structural gates any generated artifact needs: schema
|
|
1490
|
+
validation (via `ajv`, the same devDependency `tokens.schema.json`
|
|
1491
|
+
already uses) with negative fixtures proving the schemas actually reject
|
|
1492
|
+
malformed input, and an independent recomputation of every `integrity`
|
|
1493
|
+
hash proving it wasn't just carried over from a stale build.
|
|
1494
|
+
|
|
1495
|
+
**What this deliberately is NOT.** No CLI installer — there is no
|
|
1496
|
+
`npx remarque-registry add essay` command, and no `files[].target`
|
|
1497
|
+
field to resolve an install path against. An agent's expected workflow
|
|
1498
|
+
is fetch the JSON, read `usage.html`/the CSS `content`, and apply it with
|
|
1499
|
+
its own editing tools — the registry replaces transcription-from-prose,
|
|
1500
|
+
not an installer. No archetype "starter-page" items this round: the
|
|
1501
|
+
issue floated one per archetype (Essay, Dossier, Notebook, ...) as an
|
|
1502
|
+
IF-cheap extra, and the honest call is SKIP — archetype pages live as
|
|
1503
|
+
demo-specific Astro files (`site/src/pages/**`), not as a reusable
|
|
1504
|
+
module with one canonical markup sample the way the four shipped items
|
|
1505
|
+
are, so extracting one would mean inventing a new "canonical" sample
|
|
1506
|
+
rather than single-sourcing an existing one.
|
|
1507
|
+
|
|
1508
|
+
**Fetching it.** The demo site serves the registry the same way it
|
|
1509
|
+
serves `tokens.json` (`site/scripts/copy-tokens-json.mjs`, extended):
|
|
1510
|
+
|
|
1511
|
+
- Index: **https://williamzujkowski.github.io/remarque/registry.json**
|
|
1512
|
+
- Per-item: **https://williamzujkowski.github.io/remarque/registry/essay.json**
|
|
1513
|
+
(and `broadsheet.json`, `forms.json`, `palette-deck.json`)
|
|
1514
|
+
- Schemas: **https://williamzujkowski.github.io/remarque/registry-item.schema.json**,
|
|
1515
|
+
**https://williamzujkowski.github.io/remarque/registry.schema.json**
|
|
1516
|
+
|
|
1517
|
+
npm package exports mirror the same paths: `remarque-tokens/registry.json`,
|
|
1518
|
+
`remarque-tokens/registry/essay` (the `./registry/*` subpath maps to
|
|
1519
|
+
`./registry/*.json`), `remarque-tokens/registry-item.schema.json`,
|
|
1520
|
+
`remarque-tokens/registry.schema.json`.
|
|
1521
|
+
|
|
1522
|
+
---
|
|
1523
|
+
|
|
1294
1524
|
## Signature Moves
|
|
1295
1525
|
|
|
1296
1526
|
These are the repeatable visual tells that make a Remarque site recognizable:
|
|
@@ -1321,6 +1551,40 @@ An implementation succeeds when:
|
|
|
1321
1551
|
|
|
1322
1552
|
---
|
|
1323
1553
|
|
|
1554
|
+
## Governance & Deprecation
|
|
1555
|
+
|
|
1556
|
+
Remarque is small enough that governance fits on a page, but it now ships as a published npm package with multiple consuming sites and a CI gate other repos call into — enough surface that the rules deserve writing down rather than living only in maintainer habit. The precedent here is deliberately modest: closer to how a single-maintainer Carbon or Polaris component documents its own contract than to either project's full RFC process.
|
|
1557
|
+
|
|
1558
|
+
### Semver contract
|
|
1559
|
+
|
|
1560
|
+
Remarque ships as a CSS token system, not an application — "breaking" means something different than it does for a library with a JS API surface:
|
|
1561
|
+
|
|
1562
|
+
- **MAJOR** — a token is **removed** or **renamed**. Anything a consumer's `var(--color-x)` or a `tokens.json` lookup resolves today must keep resolving after a minor or patch bump; only a major bump may take that away.
|
|
1563
|
+
- **MINOR** — a token's **value changes**, or a token is **added**. Value changes always carry a stated rationale in the CHANGELOG entry (a contrast fix, a gamut correction, an upstream dataset repin) — the change is minor because the *name* stays stable and every consumer reads it through `var()` indirection, but it is never silent. Additive changes (new tokens, new optional CLI flags, new subpath exports) are minor by definition — nothing existing changes shape.
|
|
1564
|
+
- **PATCH** — tool/script fixes with no token or CSS output change (a `remarque-audit` parser bug, a `tokens-json.mjs` generation fix, a fixture correction). If `tokens.json`/`tokens.d.ts`'s content is byte-identical before and after, it's a patch.
|
|
1565
|
+
|
|
1566
|
+
The project is still pre-1.0 — under strict SemVer every 0.x release is technically allowed to break anything — but every release to date has in practice held to the contract above (no token has ever been removed or renamed; see the version history in CHANGELOG.md). 1.0 is the point where that becomes a promise instead of an observed pattern.
|
|
1567
|
+
|
|
1568
|
+
### Deprecation windows
|
|
1569
|
+
|
|
1570
|
+
A token or convention slated for removal is announced in the CHANGELOG at least one MINOR release before it disappears, named explicitly with a target version — not left implicit in a commit message. The table below is the centralized registry of every open sunset commitment; a PR that adds a new one should add a row here in the same commit, not just mention it once in the CHANGELOG.
|
|
1571
|
+
|
|
1572
|
+
| Deprecated / bridged item | Introduced | Sunset target | Rationale |
|
|
1573
|
+
|---|---|---|---|
|
|
1574
|
+
| `:root.dark` compatibility selector (alongside canonical `[data-theme="dark"]`) | 0.5.0 | 1.0 | Bridges class-keyed consumer sites onto the attribute-keyed convention `remarque-audit` treats as canonical — the audit already parses both forms, so the bridge costs nothing today. Platform endgame to watch instead: `color-scheme` + `light-dark()` (see "Technology Stack"). |
|
|
1575
|
+
|
|
1576
|
+
No other open sunset commitments exist as of this writing (grepped against REMARQUE.md and CHANGELOG.md for "sunset"/"deprecated"). This table is meant to be the single place to look — but only if every future commitment is added here, not just announced in passing.
|
|
1577
|
+
|
|
1578
|
+
### Decision-log habit
|
|
1579
|
+
|
|
1580
|
+
Every design decision with more than one reasonable answer — a token value, a deviation from a cited precedent (Butterick, Carbon, USWDS), a scope change to an in-flight issue — gets a recorded vote, not just an implementation. The convention has been practiced since 0.5.0; this section states it as policy rather than leaving it implicit:
|
|
1581
|
+
|
|
1582
|
+
- The CHANGELOG entry for the release states the vote count (e.g. "ratified 3-0 by consensus panel") and, when the outcome wasn't unanimous or shipped with conditions, what those conditions were.
|
|
1583
|
+
- REMARQUE.md carries the same treatment inline wherever a deviation is asserted against upstream guidance — see "Lineage: Butterick's Five Rules" below for the fullest example: the guidance, the deviation, the argued rationale, and the ratification line together.
|
|
1584
|
+
- The habit exists so a future maintainer — human or agent — can tell the difference between "nobody thought about this" and "this was considered and decided," without reconstructing the reasoning from a diff.
|
|
1585
|
+
|
|
1586
|
+
---
|
|
1587
|
+
|
|
1324
1588
|
## What Remarque Is Named For
|
|
1325
1589
|
|
|
1326
1590
|
In fine art printmaking, a *remarque* is a small original drawing made by the artist in the margin of a print. Found only in limited proof editions, the remarque is the mark that says: this was made by a specific person, with intention, by hand.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "remarque-tokens",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=18"
|
|
6
6
|
},
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"drift": "node scripts/drift-check.mjs"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@williamzujkowski/oklch-terminal-themes": "0.
|
|
35
|
+
"@williamzujkowski/oklch-terminal-themes": "0.5.0",
|
|
36
36
|
"ajv": "^8.20.0",
|
|
37
37
|
"culori": "^4.0.2"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
|
-
"@williamzujkowski/oklch-terminal-themes": ">=0.1.0 <0.
|
|
40
|
+
"@williamzujkowski/oklch-terminal-themes": ">=0.1.0 <1.0.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependenciesMeta": {
|
|
43
43
|
"@williamzujkowski/oklch-terminal-themes": {
|
|
@@ -80,6 +80,10 @@
|
|
|
80
80
|
"./deck.js": "./deck.js",
|
|
81
81
|
"./agent-rules": "./AGENT_RULES.md",
|
|
82
82
|
"./spec": "./REMARQUE.md",
|
|
83
|
+
"./registry.json": "./registry.json",
|
|
84
|
+
"./registry-item.schema.json": "./registry-item.schema.json",
|
|
85
|
+
"./registry.schema.json": "./registry.schema.json",
|
|
86
|
+
"./registry/*": "./registry/*.json",
|
|
83
87
|
"./package.json": "./package.json"
|
|
84
88
|
},
|
|
85
89
|
"files": [
|
|
@@ -107,6 +111,12 @@
|
|
|
107
111
|
"broadsheet.css",
|
|
108
112
|
"forms.css",
|
|
109
113
|
"deck.js",
|
|
110
|
-
"scripts/lib/css-tokens.mjs"
|
|
114
|
+
"scripts/lib/css-tokens.mjs",
|
|
115
|
+
"registry.json",
|
|
116
|
+
"registry/",
|
|
117
|
+
"registry-item.schema.json",
|
|
118
|
+
"registry.schema.json",
|
|
119
|
+
"scripts/build-registry.mjs",
|
|
120
|
+
"scripts/lib/registry-extract.mjs"
|
|
111
121
|
]
|
|
112
122
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://williamzujkowski.github.io/remarque/registry-item.schema.json",
|
|
3
|
+
"name": "broadsheet",
|
|
4
|
+
"type": "remarque:contract",
|
|
5
|
+
"title": "Broadsheet Pattern",
|
|
6
|
+
"description": "The editorial Landing/archive pattern: masthead, lead article, numbered entry list, and post-header kicker. Entry numerals are generated from `data-entry-number` via `attr()`, never `counter()`, and every kicker/dateline row is true `font-variant-caps: all-small-caps`, never `text-transform: uppercase`.",
|
|
7
|
+
"version": "0.23.0",
|
|
8
|
+
"integrity": "sha256-BZea38PRdob4OfmdGN0eDw3U0adl7SEsdrjKgSewK9A=",
|
|
9
|
+
"dependencies": [
|
|
10
|
+
"remarque-tokens"
|
|
11
|
+
],
|
|
12
|
+
"cssVars": [
|
|
13
|
+
"--border-style",
|
|
14
|
+
"--border-width",
|
|
15
|
+
"--color-accent",
|
|
16
|
+
"--color-border",
|
|
17
|
+
"--color-border-bold",
|
|
18
|
+
"--color-fg",
|
|
19
|
+
"--color-fg-muted",
|
|
20
|
+
"--color-muted",
|
|
21
|
+
"--content-reading",
|
|
22
|
+
"--font-body",
|
|
23
|
+
"--font-display",
|
|
24
|
+
"--font-mono",
|
|
25
|
+
"--leading-body",
|
|
26
|
+
"--leading-display",
|
|
27
|
+
"--leading-meta",
|
|
28
|
+
"--leading-section",
|
|
29
|
+
"--motion-easing",
|
|
30
|
+
"--motion-normal",
|
|
31
|
+
"--remarque-masthead-size",
|
|
32
|
+
"--space-10",
|
|
33
|
+
"--space-2",
|
|
34
|
+
"--space-3",
|
|
35
|
+
"--space-4",
|
|
36
|
+
"--space-5",
|
|
37
|
+
"--space-6",
|
|
38
|
+
"--space-7",
|
|
39
|
+
"--space-8",
|
|
40
|
+
"--text-body",
|
|
41
|
+
"--text-body-lg",
|
|
42
|
+
"--text-display",
|
|
43
|
+
"--text-meta",
|
|
44
|
+
"--text-section",
|
|
45
|
+
"--text-title",
|
|
46
|
+
"--tracking-caps",
|
|
47
|
+
"--tracking-display",
|
|
48
|
+
"--tracking-title",
|
|
49
|
+
"--weight-display",
|
|
50
|
+
"--weight-medium",
|
|
51
|
+
"--weight-regular"
|
|
52
|
+
],
|
|
53
|
+
"docs": "https://github.com/williamzujkowski/remarque/blob/main/REMARQUE.md#broadsheet",
|
|
54
|
+
"files": [
|
|
55
|
+
{
|
|
56
|
+
"path": "broadsheet.css",
|
|
57
|
+
"content": "/*\n * Remarque — Broadsheet Pattern (masthead, lead, entry list, post kicker)\n * ────────────────────────────────────────────────────────────────────\n * Four editorial-layout primitives for Landing/archive contexts — the\n * typography-maximalist end of the system. Newspaper-broadsheet voice:\n * a nameplate, a featured lead story, a numbered contents list, and a\n * post-header kicker line, built entirely from tokens already shipped\n * in tokens-core.css / tokens-palette.css.\n *\n * PROVENANCE — graduated, not invented here. Validated downstream on\n * williamzujkowski.github.io (landing PR #213, archive/tag/post-header\n * PR #214), ratified for upstreaming in issue #36. Source: `astro-site/\n * src/components/BroadsheetEntry.astro` + `global.css`'s \"Broadsheet\n * layout\" block. As with the essay module (#52), no site-specific\n * literal was copied — every dimension below is re-derived from this\n * package's own tokens; see the inline comments for the value-by-value\n * mapping and the few places the re-expression corrects the source\n * (`text-transform: uppercase` -> `font-variant-caps: all-small-caps`,\n * per REMARQUE.md \"Small Caps\", predates that rule same as essay.css's\n * TOC summary did) or simplifies it (font-weight 300 has no Remarque\n * core token; the italic/lede treatments below lean on color + style\n * alone rather than introduce an un-tokenized weight).\n *\n * Own subpath (remarque-tokens/broadsheet, remarque-tokens/broadsheet.css)\n * — NOT included by the tokens.css aggregator: the Landing/archive\n * pattern is opt-in, most Remarque pages want none of it. Import\n * explicitly, after prose.css if both are in use:\n * @import 'remarque-tokens/broadsheet.css';\n * (string form — see AGENT_RULES.md Pitfall #6.)\n *\n * ─── MARKUP CONTRACT (summary — full version in REMARQUE.md) ──────────\n *\n * <header class=\"remarque-masthead\">\n * <p class=\"remarque-masthead-kicker\"><span>Field Notes</span></p>\n * <h1 class=\"remarque-masthead-title\">Quiet <em>Signals</em></h1>\n * <p class=\"remarque-masthead-dateline\">\n * <span class=\"remarque-rule\" aria-hidden=\"true\"></span>\n * <span>July 23, 2026</span>\n * <span class=\"remarque-rule\" aria-hidden=\"true\"></span>\n * </p>\n * </header>\n *\n * <article class=\"remarque-lead\">\n * <p class=\"remarque-lead-kicker\">\n * <span class=\"remarque-lead-section\">Essay</span>\n * <span class=\"remarque-dot\" aria-hidden=\"true\">·</span>\n * <span>8 min read</span>\n * <span class=\"remarque-dot\" aria-hidden=\"true\">·</span>\n * <time datetime=\"2026-04-08\">Apr 8, 2026</time>\n * </p>\n * <h2 class=\"remarque-lead-title\"><a href=\"/writing/x\">Title</a></h2>\n * <p class=\"remarque-lead-lede\">Lede paragraph...</p>\n * </article>\n *\n * <ul class=\"remarque-entry-list\">\n * <li class=\"remarque-entry\">\n * <span class=\"remarque-entry-numeral\" data-entry-number=\"2\"\n * aria-hidden=\"true\"></span>\n * <div class=\"remarque-entry-body\">\n * <p class=\"remarque-entry-kicker\">...</p>\n * <h3 class=\"remarque-entry-title\"><a href=\"/writing/y\">Title</a></h3>\n * <p class=\"remarque-entry-excerpt\">Excerpt...</p>\n * </div>\n * </li>\n * </ul>\n *\n * <header class=\"remarque-post-header\">\n * <p class=\"remarque-post-kicker\">\n * <span class=\"remarque-post-kicker-section\">Essay</span>\n * <span class=\"remarque-dot\" aria-hidden=\"true\">·</span>\n * <span>8 min read</span>\n * <span class=\"remarque-dot\" aria-hidden=\"true\">·</span>\n * <time datetime=\"2026-04-08\">Apr 8, 2026</time>\n * </p>\n * <h1 class=\"text-display font-display\">Post Title</h1>\n * </header>\n *\n * `<ul>` (not `<ol>`), deliberately, for the entry list: the visual\n * numeral is decorative (`aria-hidden`, generated from `data-entry-\n * number` via `attr()` — see below), and an `<ol>` would have screen\n * readers announce \"1 of 2\" alongside it, doubling up on the same\n * information in two voices. Matches the flagship's own choice.\n */\n\n/* ─── Shared meta-voice bits ─────────────────────────────────────────\n * Every kicker/dateline row below repeats these declarations rather\n * than depending on `.text-label` as a mixin (tokens-core.css) — same\n * non-mixin precedent as essay.css's TOC summary. All-small-caps, not\n * `text-transform: uppercase` (REMARQUE.md \"Small Caps\": faux uppercase\n * renders at cap-height with no optical correction; the flagship\n * predates that rule and used `text-transform: uppercase` throughout\n * this pattern). `--text-meta` (14px), not `--text-micro` (13px) — the\n * words in a kicker (\"Essay\", \"Field Notes\") are read as content, not\n * bare timestamps/fine print, the same distinction essay.css's sidenote\n * marker already draws against the 13px floor. Tabular lining figures\n * on every numeral here (dates, counts) per REMARQUE.md's Numerals\n * rule — never mixed with the oldstyle register `.remarque-entry-\n * numeral` uses below for the same reason `.text-meta` carries it.\n */\n.remarque-masthead-kicker,\n.remarque-masthead-dateline,\n.remarque-lead-kicker,\n.remarque-entry-kicker,\n.remarque-post-kicker {\n font-family: var(--font-mono);\n font-size: var(--text-meta);\n line-height: var(--leading-meta);\n font-variant-caps: all-small-caps;\n font-variant-numeric: tabular-nums lining-nums;\n letter-spacing: var(--tracking-caps);\n color: var(--color-muted);\n margin: 0;\n display: inline-flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 0.5em;\n}\n\n.remarque-dot {\n color: var(--color-border-bold);\n}\n\n.remarque-lead-section,\n.remarque-entry-section,\n.remarque-post-kicker-section {\n color: var(--color-accent);\n font-weight: var(--weight-medium);\n}\n\n/* Hairline flanking the masthead dateline — decorative only, so a\n 1px border-color rule (not -bold) is the right register; nothing\n depends on it as a boundary.\n Authored as a BORDER, not a `background` fill, on purpose (issue #93\n forced-colors audit finding): this is a bare, non-semantic `<span>`\n with no text content and no border of its own otherwise — under\n `forced-colors: active`, a plain element's `background-color` is\n generally forced to `Canvas` (the page background), which would make\n a background-fill divider disappear entirely, while `border-color` on\n a real border is forced to a visible system border color instead.\n Same 1px visual result at rest (a zero-height box with a 1px top\n border reads identically to a 1px-tall filled box); only the forced-\n colors behavior differs. */\n.remarque-rule {\n display: inline-block;\n width: var(--space-6);\n height: 0;\n border-top: var(--border-width) var(--border-style) var(--color-border-bold);\n}\n\n/* Hairline divider between broadsheet sections (masthead/lead/entry-list) */\n.remarque-broadsheet-rule {\n max-width: var(--content-reading);\n margin: var(--space-6) auto;\n border: 0;\n border-top: var(--border-width) var(--border-style) var(--color-border);\n}\n\n/* ─── 1. Masthead ─────────────────────────────────────────────────────\n * The nameplate. Centered, oversized, the one place in Remarque a\n * title outgrows `--text-display` (tokens-core.css's largest type-\n * scale rung, clamp(2.75rem, 5.5vw, 5rem)) — a Landing masthead is a\n * register above even a Landing page's own `<h1>`.\n *\n * FLUID-TYPE DECISION (issue #36's required call): the existing scale\n * does not reach the flagship's validated clamp(3.5rem, 9vw, 7.5rem).\n * Rather than assert those as bare literals (or add a new core\n * `--text-masthead` token, which the issue explicitly rules out), this\n * is a documented, file-scoped fluid exception whose min/max BOUNDS are\n * arithmetic on core spacing tokens already in tokens-core.css — not\n * new constants:\n * min = --space-7 (3rem) + --space-2 (0.5rem) = 3.5rem\n * max = --space-10 (8rem) - --space-2 (0.5rem) = 7.5rem\n * both resolve to the exact flagship values that were already\n * validated (contrast/gamut/font-floor audits + axe) downstream. The\n * 9vw fluid rate is kept as-is: it continues the same escalating-per-\n * tier vw progression tokens-core.css's own scale already uses\n * (2.25vw section -> 3.5vw title -> 5.5vw display), one step further\n * for a register one step past display. This custom property lives\n * here, not in tokens-core.css — it is NOT a new core token, it is\n * local arithmetic on existing ones, scoped to this opt-in pattern.\n */\n.remarque-masthead {\n --remarque-masthead-size: clamp(\n calc(var(--space-7) + var(--space-2)),\n 9vw,\n calc(var(--space-10) - var(--space-2))\n );\n max-width: var(--content-reading);\n margin: var(--space-8) auto var(--space-6);\n text-align: center;\n}\n\n.remarque-masthead-title {\n font-family: var(--font-display);\n font-size: var(--remarque-masthead-size);\n /* Reuses --leading-display/--tracking-display rather than the\n flagship's bespoke 0.92/-0.035em — a fully tokenized simplification\n at a scale where the difference is not perceptible at normal\n reading distance. */\n line-height: var(--leading-display);\n letter-spacing: var(--tracking-display);\n /* Same --weight-display escape hatch .text-display/.text-title use\n (tokens-core.css) — a palette can compensate thin serif hairlines\n in dark mode (tokens-palette.css already does, 400 light / 500\n dark) and the masthead picks that up for free. */\n font-weight: var(--weight-display, var(--weight-regular));\n color: var(--color-fg);\n margin: 0;\n font-feature-settings: \"liga\" 1, \"kern\" 1;\n font-optical-sizing: auto;\n font-synthesis: none;\n text-wrap: balance;\n}\n\n.remarque-masthead-title em {\n font-style: italic;\n color: var(--color-fg-muted);\n}\n\n.remarque-masthead-dateline {\n margin-block-start: var(--space-4);\n justify-content: center;\n}\n\n/* ─── 2. Lead Article ─────────────────────────────────────────────────\n * The featured story below the masthead (or standing alone atop an\n * archive). Title register: --text-display reaches close enough to\n * the flagship's clamp(2.25rem, 4.5vw, 3.75rem) (2.75-5rem vs.\n * 2.25-3.75rem) that no exception is needed here — \"express via\n * existing type tokens\" (issue #36), the masthead above is the one\n * register that genuinely doesn't fit.\n */\n.remarque-lead {\n max-width: var(--content-reading);\n margin: var(--space-7) auto var(--space-6);\n}\n\n.remarque-lead-kicker {\n margin-block-end: var(--space-3);\n}\n\n.remarque-lead-title {\n font-family: var(--font-display);\n font-size: var(--text-display);\n line-height: var(--leading-display);\n letter-spacing: var(--tracking-display);\n font-weight: var(--weight-display, var(--weight-regular));\n margin: 0 0 var(--space-4);\n text-wrap: balance;\n font-optical-sizing: auto;\n font-synthesis: none;\n}\n\n.remarque-lead-title a,\n.remarque-entry-title a {\n color: var(--color-fg);\n text-decoration: none;\n /* Underline-grow hover (issue #36 \"Findings\"): AA-compliant, and\n reduced-motion-safe for free — tokens-core.css zeroes\n --motion-normal under prefers-reduced-motion, so this needs no\n separate @media guard the way an opacity/transform animation\n would. --motion-easing/--motion-normal replace the flagship's\n hardcoded `220ms ease`. */\n background-image: linear-gradient(var(--color-fg), var(--color-fg));\n background-size: 0 1px;\n background-repeat: no-repeat;\n background-position: 0 95%;\n transition: background-size var(--motion-normal) var(--motion-easing);\n}\n\n.remarque-lead-title a:hover,\n.remarque-lead-title a:focus-visible,\n.remarque-entry-title a:hover,\n.remarque-entry-title a:focus-visible {\n background-size: 100% 1px;\n}\n\n/* Forced-colors fallback (issue #93 audit finding — affordance-loss,\n the clearest one in this file): the underline-grow hover/focus\n affordance above is painted entirely with `background-image:\n linear-gradient(...)`. CSS gradients are on the forced list — under\n `forced-colors: active` a gradient background-image is forced to\n `none`, so the ENTIRE hover/focus signal for these title links would\n otherwise disappear (the `:focus-visible` outline from tokens-core.css\n still fires for keyboard focus, but mouse hover would have no visual\n affordance at all, and even focus would lose this link-specific cue).\n `text-decoration: underline` is not on the forced list and needs no\n override to render visibly, so it stands in as the same \"this is a\n link, and it is the active one\" signal the gradient trick provides\n everywhere else. Scoped to this media query only — the gradient\n underline-grow stays the sole affordance outside forced-colors mode,\n unchanged. */\n@media (forced-colors: active) {\n .remarque-lead-title a:hover,\n .remarque-lead-title a:focus-visible,\n .remarque-entry-title a:hover,\n .remarque-entry-title a:focus-visible {\n text-decoration: underline;\n }\n}\n\n.remarque-lead-lede {\n font-family: var(--font-display);\n font-size: var(--text-body-lg);\n line-height: var(--leading-body);\n font-style: italic;\n font-weight: var(--weight-regular);\n color: var(--color-fg-muted);\n margin: 0;\n text-wrap: pretty;\n hanging-punctuation: first last;\n}\n\n/* Drop cap on italic text needs an explicit ::first-letter override\n (issue #36 \"Findings\") — it cannot just inherit italic from the\n parent's font-style the way a roman-text drop cap can, since this\n pseudo-element re-asserts every typographic property of its own.\n Same mechanics/sizing as prose.css's `.remarque-prose--dropcap`\n (3.5em / 0.85 line-height / initial-letter progressive enhancement)\n for one consistent drop-cap voice across the system, rather than a\n second bespoke number. */\n.remarque-lead-lede::first-letter {\n font-family: var(--font-display);\n font-style: italic;\n font-weight: var(--weight-display, var(--weight-regular));\n font-size: 3.5em;\n line-height: 0.85;\n float: left;\n padding: 0.08em 0.12em 0 0;\n color: var(--color-fg);\n}\n\n@supports (initial-letter: 4) or (-webkit-initial-letter: 4) {\n .remarque-lead-lede::first-letter {\n initial-letter: 3.5;\n -webkit-initial-letter: 3.5;\n float: none;\n padding: 0 0.08em 0 0;\n }\n}\n\n/* ─── 3. Numbered Entry List ──────────────────────────────────────────\n * `container-type: inline-size` — the 2-column/1-column collapse below\n * responds to how much room the list itself has, not the viewport (a\n * clean fit for a container query, matching the flagship's choice; it\n * degrades correctly if this list is ever embedded in a narrower\n * column than the page itself).\n */\n.remarque-entry-list {\n container-type: inline-size;\n max-width: var(--content-reading);\n margin: 0 auto;\n padding: 0;\n list-style: none;\n}\n\n.remarque-entry {\n display: grid;\n /* Numeral column sized in `ch` (intrinsic to the numeral glyphs\n themselves), not an asserted rem literal — the flagship's\n `minmax(3.5rem, 5rem)` re-expressed as \"just wide enough for the\n content,\" the more typographically disciplined choice per\n REMARQUE.md's Decision Protocol when a core token doesn't already\n cover a dimension. */\n grid-template-columns: minmax(2.5ch, 4ch) 1fr;\n gap: var(--space-4);\n padding: var(--space-5) 0;\n border-bottom: var(--border-width) var(--border-style) var(--color-border);\n align-items: start;\n}\n\n.remarque-entry:last-child {\n border-bottom: 0;\n}\n\n/* Oldstyle italic numerals in the outer margin, generated from\n `data-entry-number` via `attr()` — NOT CSS `counter()` (issue #36\n \"Findings\": counter-generated content does not receive the OpenType\n oldstyle-figure treatment reliably across Newsreader's weights, only\n real text/attr() content does). `attr()` reads from the element the\n pseudo-content is generated on, so `data-entry-number` lives on\n `.remarque-entry-numeral` itself, not the parent `<li>`. Both\n `font-variant-numeric: oldstyle-nums proportional-nums` AND\n `font-feature-settings: \"onum\" 1, \"pnum\" 1` are required together\n for reliable rendering across weights — either alone is\n inconsistent (issue #36 \"Findings\"). */\n.remarque-entry-numeral {\n display: block;\n}\n\n.remarque-entry-numeral::before {\n content: attr(data-entry-number);\n font-family: var(--font-display);\n font-style: italic;\n font-size: var(--text-title);\n line-height: 0.95;\n color: var(--color-border-bold);\n font-variant-numeric: oldstyle-nums proportional-nums;\n font-feature-settings: \"onum\" 1, \"pnum\" 1;\n text-align: right;\n display: block;\n padding-block-start: 0.1em;\n user-select: none;\n transition: color var(--motion-normal) var(--motion-easing);\n}\n\n.remarque-entry:hover .remarque-entry-numeral::before,\n.remarque-entry:focus-within .remarque-entry-numeral::before {\n color: var(--color-accent);\n}\n\n.remarque-entry-body {\n min-width: 0; /* allow the grid child to shrink below its content's intrinsic width */\n}\n\n.remarque-entry-kicker {\n margin-block-end: var(--space-2);\n}\n\n.remarque-entry-title {\n font-family: var(--font-display);\n font-size: var(--text-section);\n line-height: var(--leading-section);\n letter-spacing: var(--tracking-title);\n font-weight: var(--weight-medium);\n margin: 0 0 var(--space-2);\n text-wrap: balance;\n font-optical-sizing: auto;\n font-synthesis: none;\n}\n\n.remarque-entry-excerpt {\n font-family: var(--font-body);\n font-size: var(--text-body);\n line-height: var(--leading-body);\n color: var(--color-fg-muted);\n margin: 0;\n text-wrap: pretty;\n}\n\n/* Collapse to one column on a narrow container — `40rem`, exactly\n `--space-10` (8rem) x 5. Container-query conditions can't reference\n custom properties or `calc()` (unlike a plain declaration value, the\n condition itself is parsed before the cascade resolves custom\n properties, and Lightning CSS's minifier rejects a `calc()` there\n outright) so this is spelled as the literal it resolves to, with the\n token-multiple noted here rather than in the syntax — matches the\n flagship's own `640px` breakpoint exactly. */\n@container (max-width: 40rem) {\n .remarque-entry {\n grid-template-columns: 1fr;\n gap: var(--space-2);\n }\n\n .remarque-entry-numeral::before {\n text-align: left;\n font-size: var(--text-section);\n padding-block-start: 0;\n }\n}\n\n/* ─── 4. Post-Header Kicker ───────────────────────────────────────────\n * The mono \"Section · Reading time · Date\" line above an individual\n * post's title — the same kicker voice as the lead/entry rows above,\n * scaled down to a single-line header. Pair with any Remarque heading\n * class (`.text-display`, `.text-title`); this rule only styles the\n * kicker line itself, not the title beneath it.\n */\n.remarque-post-header {\n margin-block-end: var(--space-7);\n}\n\n.remarque-post-kicker {\n margin-block-end: var(--space-3);\n}\n",
|
|
58
|
+
"type": "remarque:css"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"path": "usage.html",
|
|
62
|
+
"content": "<header class=\"remarque-masthead\">\n <p class=\"remarque-masthead-kicker\"><span>Field Notes</span></p>\n <h1 class=\"remarque-masthead-title\">Quiet <em>Signals</em></h1>\n <p class=\"remarque-masthead-dateline\">\n <span class=\"remarque-rule\" aria-hidden=\"true\"></span>\n <span>July 23, 2026</span>\n <span class=\"remarque-rule\" aria-hidden=\"true\"></span>\n </p>\n</header>\n\n<article class=\"remarque-lead\">\n <p class=\"remarque-lead-kicker\">\n <span class=\"remarque-lead-section\">Essay</span>\n <span class=\"remarque-dot\" aria-hidden=\"true\">·</span>\n <span>8 min read</span>\n <span class=\"remarque-dot\" aria-hidden=\"true\">·</span>\n <time datetime=\"2026-04-08\">Apr 8, 2026</time>\n </p>\n <h2 class=\"remarque-lead-title\"><a href=\"/writing/x\">Title</a></h2>\n <p class=\"remarque-lead-lede\">Lede paragraph…</p>\n</article>\n\n<ul class=\"remarque-entry-list\">\n <li class=\"remarque-entry\">\n <span class=\"remarque-entry-numeral\" data-entry-number=\"2\" aria-hidden=\"true\"></span>\n <div class=\"remarque-entry-body\">\n <p class=\"remarque-entry-kicker\">\n <span class=\"remarque-entry-section\">Essay</span>\n <span class=\"remarque-dot\" aria-hidden=\"true\">·</span>\n <span>6 min read</span>\n <span class=\"remarque-dot\" aria-hidden=\"true\">·</span>\n <time datetime=\"2026-03-29\">Mar 29, 2026</time>\n </p>\n <h3 class=\"remarque-entry-title\"><a href=\"/writing/y\">Title</a></h3>\n <p class=\"remarque-entry-excerpt\">Excerpt…</p>\n </div>\n </li>\n</ul>\n\n<header class=\"remarque-post-header\">\n <p class=\"remarque-post-kicker\">\n <span class=\"remarque-post-kicker-section\">Essay</span>\n <span class=\"remarque-dot\" aria-hidden=\"true\">·</span>\n <span>8 min read</span>\n <span class=\"remarque-dot\" aria-hidden=\"true\">·</span>\n <time datetime=\"2026-04-08\">Apr 8, 2026</time>\n </p>\n <h1 class=\"text-display font-display\">Post Title</h1>\n</header>\n",
|
|
63
|
+
"type": "remarque:markup"
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|