remarque-tokens 0.28.0 → 0.30.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 +4 -2
- package/CHANGELOG.md +51 -0
- package/README.md +10 -7
- package/REMARQUE.md +189 -3
- package/chrome.css +364 -0
- package/forms.css +70 -19
- package/package.json +4 -1
- package/print.css +3 -0
- package/prose.css +65 -9
- package/registry/breadcrumb.json +53 -0
- package/registry/broadsheet.json +1 -1
- package/registry/essay.json +1 -1
- package/registry/forms.json +3 -3
- package/registry/pager.json +53 -0
- package/registry/palette-deck.json +1 -1
- package/registry/shadcn/breadcrumb.json +58 -0
- package/registry/shadcn/broadsheet.json +72 -0
- package/registry/shadcn/essay.json +60 -0
- package/registry/shadcn/forms.json +66 -0
- package/registry/shadcn/pager.json +58 -0
- package/registry/shadcn/palette-deck.json +25 -0
- package/registry/shadcn/registry.json +431 -0
- package/registry/shadcn/skip-link.json +58 -0
- package/registry/shadcn/tags.json +58 -0
- package/registry/skip-link.json +53 -0
- package/registry/tags.json +53 -0
- package/registry.json +42 -6
- package/scripts/audit.mjs +31 -3
- package/scripts/build-registry.mjs +81 -3
- package/tokens.d.ts +1 -1
- package/tokens.json +1 -1
package/AGENT_RULES.md
CHANGED
|
@@ -8,7 +8,7 @@ Read `REMARQUE.md` first for the full system specification. This file tells you
|
|
|
8
8
|
|
|
9
9
|
## Prefer the Registry Over Transcribing Prose
|
|
10
10
|
|
|
11
|
-
For the Essay Module, the Broadsheet pattern, and
|
|
11
|
+
For the Essay Module, the Broadsheet pattern, Forms and the Chrome Module (`remarque-tokens/essay`, `remarque-tokens/broadsheet`, `remarque-tokens/forms`, `remarque-tokens/chrome`), 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
12
|
|
|
13
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
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.
|
|
@@ -222,7 +222,8 @@ Before considering any implementation complete, verify:
|
|
|
222
222
|
- [ ] Every page maps to an archetype (Essay, Dossier, Notebook, Landing, Reference/Docs, Changelog, Gallery)
|
|
223
223
|
- [ ] If the Essay uses sidenotes/a TOC rail (`remarque-tokens/essay`): `.remarque-sidenote-ref`/`.remarque-sidenote` alternate in strict DOM order, the rail never intrudes into `.remarque-prose`'s own measure, and the page renders correctly with `essay.css`'s `@media` block deleted (the narrow-viewport/no-JS fallback)
|
|
224
224
|
- [ ] If a Landing/archive page uses the Broadsheet pattern (`remarque-tokens/broadsheet`): entry numerals are generated from `data-entry-number` via `attr()` (not `counter()`), the entry list stays a `<ul>` (not `<ol>` — the numeral is `aria-hidden` and decorative), and every kicker/dateline row uses the label voice: tracked capitals (`text-transform: uppercase` + `--tracking-caps`), because the default mono face has no small caps; `font-variant-caps: all-small-caps` only on a label face that has `smcp` + `c2sc` (REMARQUE.md "Small Caps"; `remarque-audit` fails synthesized small caps)
|
|
225
|
-
- [ ]
|
|
225
|
+
- [ ] Site chrome uses `remarque-tokens/chrome` instead of hand-rolled copies: `.remarque-skip-link` is the first focusable element and targets `<main id="main-content" tabindex="-1">`; a breadcrumb's current page is `<span aria-current="page">`, never a link, with no separator typed into the DOM; prev/next links carry `rel="prev"`/`rel="next"` and an endpoint with nowhere to go is omitted (or plain text in numbered pagination), never a disabled-looking link; tags are plain mono links with `rel="tag"`, no border, fill or radius (REMARQUE.md "Chrome Module")
|
|
226
|
+
- [ ] If a page uses form controls (`remarque-tokens/forms`): checkboxes/radios are styled with `accent-color` only — never `appearance: none` plus a hand-drawn replacement; every control (input, button, checkbox/radio label) is ≥44×44px; every input collecting known personal data has a valid `autocomplete` token and paste is never blocked (WCAG 3.3.7/3.3.8); validation state lives on `.remarque-field[data-state]` AND a real `aria-invalid`/`aria-describedby` pair on the input, not `data-state` alone; every state message says what's wrong in words (the shipped glyph has empty alt text and is the non-color cue, not the message); a pending submit disables the button, changes its label and announces through `role="status"`, never a spinner or `aria-busy` on the button; disabled controls use `--color-disabled`, never a state color
|
|
226
227
|
- [ ] If a page uses the Palette Deck (`remarque-tokens/deck`): `data-palette` is set/cleared on the same root element as `data-theme` (they compose, never conflict); the FOUC-safe restore is a synchronous classic `<script>` physically in `<head>` (never `type="module"`, which defers); the switcher control itself is a native, keyboard-operable element (e.g. `.remarque-input` `<select>`) at ≥44×44px, not a hand-rolled widget; every registered palette was generated via `remarque-theme --scope`, never hand-authored
|
|
227
228
|
- [ ] Mobile version is roomy — not a compressed desktop layout
|
|
228
229
|
- [ ] Mobile nav links have ≥44px touch targets (meets WCAG 2.5.8 AA; exceeds it, and meets 2.5.5 AAA)
|
|
@@ -271,6 +272,7 @@ npx remarque-audit --palette <file> --src <dir> --json
|
|
|
271
272
|
required: number; // minimum ratio (e.g. 4.5, 7.0, 3.0)
|
|
272
273
|
actual: number | null; // computed ratio rounded to 2dp, or null if fg/bg was unresolvable
|
|
273
274
|
ok: boolean;
|
|
275
|
+
apcaLc?: number; // APCA Lc (SAPC 0.0.98G), 1dp, REPORT-ONLY: never affects ok/passed; + = dark text on light, − = light on dark. Added in 0.30.0
|
|
274
276
|
error?: string; // present only when actual is null (unresolved var()/missing token)
|
|
275
277
|
}>;
|
|
276
278
|
gamut: Array<{
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,57 @@ 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.30.0 — 2026-10-24
|
|
8
|
+
|
|
9
|
+
The demand-gated tail of the 2026-10 roadmap (#133), worked through item by item. The identity questions went to nexus panels first. **Consumer-visible changes:**
|
|
10
|
+
- prose `h4` is set in the display face (#131);
|
|
11
|
+
- form state messages carry a glyph in every mode (#128);
|
|
12
|
+
- hover washes appear only where hover exists, and buttons get a pressed state (#129);
|
|
13
|
+
- the spec's lede samples are in full foreground (#131).
|
|
14
|
+
|
|
15
|
+
Additive: code-block titles, figure indent, opt-in external-link marks, readonly and auto-growing fields (#129), the shadcn-installable registry and `llms-full.txt` (#130), and an advisory APCA column in `remarque-audit` (#128). **Default palette values are unchanged:** the panel held them behind the demand gate 7–0 and documented opt-in recipes instead (#128). No token was added, removed or renamed.
|
|
16
|
+
|
|
17
|
+
### Changed — the heading ladder and the lede (#131; consumer-visible)
|
|
18
|
+
|
|
19
|
+
- **`.remarque-prose h4` is set in the display face** (Newsreader roman, 17px, weight 500) instead of Inter 17/600. Next to Inter 19/600 for h3, it differed by 2px and read as the same level. It now alternates face with its neighbours (h3 sans, h4 serif roman, h5 serif italic, h6 mono). That survives print, `prefers-contrast` and forced colors, where a color-only difference would collapse. Sizes are unchanged. Ratified 5–2 in `vote-1792892296073-vxi53q7` (option A over a color-only change). Per the panel: under the `:lang()` script guards, where h5 sets upright and would match h4, h5 steps down to `--color-fg-muted`.
|
|
20
|
+
- **New REMARQUE.md "Type Scale" section.** The text rungs are floors, not a modular ratio; the display rungs are fluid and open from about 1.15–1.47 steps on phones to about 1.5 on desktops (measured at 375, 1280 and 1920px). It adds the heading-ladder table and the rule that rung values only change by vote.
|
|
21
|
+
- **The lede is in `--color-fg`.** The spec's samples and the demo set the 19px standfirst in `fg-muted`, which read quieter than the body below it. `.text-body-lg` itself sets no color, so no token changes.
|
|
22
|
+
- Demo: byline tags render as a `.remarque-tags` list instead of a run-together phrase. New `site/tests/type-scale.spec.ts`.
|
|
23
|
+
|
|
24
|
+
### Added — prose and forms details (#129) and state cues (#128)
|
|
25
|
+
|
|
26
|
+
- **Forms: a non-color state cue in every mode (#128, WCAG 1.4.1).** The ✕/⚠/✓ glyphs before state messages, which until now appeared only under forced colors, render everywhere, with empty alt text. Under simulated deuteranopia the default error and success colors are only ΔE2000 2.7 (light) and 1.7 (dark) apart. **Consumer-visible:** state messages gain a leading glyph.
|
|
27
|
+
- **Forms: hover only where hover exists.** Input and button hover washes moved inside `@media (hover: hover)`, so a tap no longer leaves a control looking hovered. Buttons gain an `:active` pressed state (border to `--color-fg`).
|
|
28
|
+
- **Forms: `readonly` inputs** get a quiet `--color-bg-subtle` fill. **`.remarque-input--autosize`** opts a textarea into `field-sizing: content`, from the 6rem minimum to a 24rem cap. The text-only pending-submit pattern is documented.
|
|
29
|
+
- **Prose:**
|
|
30
|
+
- `pre[data-title]` renders a mono filename line (the flagship hand-rolls a filled tab for this);
|
|
31
|
+
- `[data-highlighted-line]` inside a `pre` gets the `--color-accent-subtle` wash;
|
|
32
|
+
- `figure` loses the browser's 40px side indent;
|
|
33
|
+
- the opt-in `.remarque-prose--external-marks` adds a screen-only ↗ after `rel="external"` links, without clobbering `print.css`'s printed URLs.
|
|
34
|
+
- **Default palette values are unchanged (#128, ratified 7–0 in `vote-1792892374374-55pw50q`).** REMARQUE.md "Dataviz Tokens" now documents opt-in recipes instead: a verified viz lightness ladder (worst-pair ΔE2000 under color-vision simulation rises from 3.5 to 11.7 in light and from 2.7 to 13.6 in dark, every slot ≥ 3.4:1 on bg and surface) and a dark `--color-muted` lift (5.77:1, APCA Lc −40.3). It also records the revisit triggers: a consumer request, or the coherence warnings being promoted to failures. The default change would then go through an upstream release first, never a golden-gate exception.
|
|
35
|
+
- **`remarque-audit` prints an advisory APCA Lc beside every contrast ratio** (new `apcaLc` field in each `contrast` record; SAPC 0.0.98G constants, checked against the reference values). It's report-only: WCAG 2.x stays the gate, because the WCAG 3 working draft still leaves its contrast algorithm undetermined. It shows what the ratio hides: default dark `--color-muted` passes at 4.92:1 but measures Lc −34.5 against light's 70.0.
|
|
36
|
+
|
|
37
|
+
### Added — distribution (#130)
|
|
38
|
+
|
|
39
|
+
- shadcn-installable registry, additive: `registry/shadcn/<name>.json` plus an index `registry/shadcn/registry.json`, generated from the same item definitions and freshness-checked by `build-registry.mjs --check`. `type: "registry:item"`, `registry:file` entries with `src/styles/remarque/*` and `src/components/remarque/*` targets, `remarque-tokens` under `dependencies`, Remarque-only fields under `meta`. Validated in `test-registry.mjs` against the vendored official shadcn schemas (`scripts/vendor/`). Existing `registry/*.json` and the `remarque:contract` type are unchanged. Published beside them on the demo site.
|
|
40
|
+
- `llms-full.txt` on the demo site (AGENT_RULES.md plus a token table with light and dark values), generated at site build time and linked from `llms.txt`.
|
|
41
|
+
|
|
42
|
+
## 0.29.0 — 2026-10-24
|
|
43
|
+
|
|
44
|
+
The "chrome" minor of the 2026-10 roadmap (#133, ratified 7–0 in `vote-1792819887868-esau595`): a new `remarque-tokens/chrome` module and four registry items. Additive: nothing existing changes for consumers who don't import it.
|
|
45
|
+
|
|
46
|
+
### Added — Chrome Module (#127; additive)
|
|
47
|
+
|
|
48
|
+
- **New subpath `remarque-tokens/chrome` (`chrome.css`):** the site-chrome and wayfinding pieces the spec required and never shipped CSS for, so every consumer hand-rolled them. The skip link had four implementations across the flagship, tsundoku, the starter and this demo, and one tsundoku pagination kept its "disabled" endpoints as focusable links. Not aggregated into `tokens.css`.
|
|
49
|
+
- **Skip link** (`.remarque-skip-link`, plus `.remarque-sr-only`): first focusable element, targets `<main id="main-content" tabindex="-1">`, hidden by clip-path until focused (never an off-screen offset, which breaks in RTL), then a filled fg-on-bg block on `--z-skip-link`, 44px tall, bordered under forced colors.
|
|
50
|
+
- **Breadcrumb** (`nav.remarque-breadcrumb > ol`): mono `--text-meta`, the current page `<span aria-current="page">`, separators generated with empty alt text so screen readers don't read them.
|
|
51
|
+
- **Pager** (`.remarque-pager`): prev/next with `rel`, a label line over the target's title, arrows that flip in RTL, an optional series row with a `<details>` part list. Plus numbered `.remarque-pagination`. An endpoint with nowhere to go is omitted, or plain text in pagination.
|
|
52
|
+
- **Tags** (`.remarque-tags`): plain mono links, `rel="tag"`, no border, fill or radius. An outlined chip is a documented deviation.
|
|
53
|
+
- The module sets no outer margins on the lists consumers position, because it's unlayered and would beat Tailwind utilities (Pitfall #7).
|
|
54
|
+
- **Four new registry items** (`skip-link`, `breadcrumb`, `pager`, `tags`), each with a REMARQUE.md "Chrome Module" markup contract that `scripts/test-registry.mjs` asserts: the skip-link target, a current page that isn't a link, no DOM separators, `rel` on pager and tag links, no disabled-looking links, and no border, fill or radius on tags.
|
|
55
|
+
- `print.css` hides the skip link, pager and pagination. AGENT_RULES gains a chrome checklist line. `remarque-audit`'s font-feature check and the budget/closure test cover the new module.
|
|
56
|
+
- Demo: the layout's Tailwind skip-link chain, which set `focus:outline-none`, is replaced by `.remarque-skip-link`; essays end with the pager; the dossier has a breadcrumb; `/components` gains a Chrome section. New `site/tests/chrome.spec.ts` covers the keyboard skip flow, separators and arrows out of the accessibility tree, pager sides with an omitted endpoint, tag styling, and the 44px pagination targets.
|
|
57
|
+
|
|
7
58
|
## 0.28.0 — 2026-10-24
|
|
8
59
|
|
|
9
60
|
The "truth" minor of the 2026-10 roadmap (#133): it makes the spec's claims true on the default stack. It was researched with Open Design and nexus-agents, ratified 7–0 (`vote-1792819887868-esau595`), and landed as one PR per issue, each with its own regression test proven red on 0.27.0. **Consumer-visible changes:**
|
package/README.md
CHANGED
|
@@ -76,7 +76,8 @@ Packaging for agent tooling:
|
|
|
76
76
|
|
|
77
77
|
All three ship in the npm tarball under `skills/` (see "Installing the skills" below) as well as living at `.claude/skills/` in this repo.
|
|
78
78
|
- **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.
|
|
79
|
-
- **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."
|
|
79
|
+
- **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." shadcn users: `npx shadcn@latest add https://williamzujkowski.github.io/remarque/registry/shadcn/essay.json`, or register `"@remarque": "https://williamzujkowski.github.io/remarque/registry/shadcn/{name}.json"` under `registries` in `components.json` and run `npx shadcn@latest add @remarque/essay`.
|
|
80
|
+
- **`llms-full.txt`:** **https://williamzujkowski.github.io/remarque/llms-full.txt** — AGENT_RULES.md plus a compact token table (name, tier, light, dark) in one fetch, generated at site build time.
|
|
80
81
|
|
|
81
82
|
### Installing the skills
|
|
82
83
|
|
|
@@ -107,14 +108,15 @@ Output" shape changes) to pick up the current playbook.
|
|
|
107
108
|
| `tokens-core.css` | Core tier — type scale, spacing, widths, radius, motion, prose styling. Never overridden |
|
|
108
109
|
| `tokens-palette.css` | Palette tier — font slots, colors, accent, reading measure. The sanctioned personalization surface |
|
|
109
110
|
| `prose.css` | `.remarque-prose` long-form styling — own subpath so sites with their own prose system can skip it |
|
|
111
|
+
| `chrome.css` | Site chrome and wayfinding (`remarque-tokens/chrome`, 0.29): skip link + `.remarque-sr-only`, breadcrumb, pager/pagination, tags — see REMARQUE.md "Chrome Module" |
|
|
110
112
|
| `scripts/audit.mjs` | `npm run audit` — enforces the spec's contrast/gamut/font-floor/no-hardcoded-color checklist (`--json` for structured output) |
|
|
111
113
|
| `scripts/drift-check.mjs` | `npx remarque-drift` — token drift check for consumers (`--json` for structured output) |
|
|
112
114
|
| `tokens.json` + `tokens.d.ts` | Generated machine-readable token inventory + TypeScript types (`scripts/tokens-json.mjs`) |
|
|
113
115
|
| `tokens.schema.json` | Generated JSON Schema (draft 2020-12) for `tokens.json`, published alongside it |
|
|
114
|
-
| `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" |
|
|
116
|
+
| `registry.json` + `registry/` | Generated markup-contract registry index + per-item files for essay/broadsheet/forms/skip-link/breadcrumb/pager/tags/palette-deck (`scripts/build-registry.mjs`) — see REMARQUE.md "The Registry" |
|
|
115
117
|
| `registry-item.schema.json` + `registry.schema.json` | Generated JSON Schemas (draft 2020-12) for the per-item and index registry files |
|
|
116
118
|
| `skills/remarque/SKILL.md` + `skills/remarque-adopt/SKILL.md` | Generated packaging copies of the two Claude Code skills (`scripts/build-skills.mjs`) — canonical source is `.claude/skills/*/SKILL.md`; see "Installing the skills" above |
|
|
117
|
-
| `fonts.css` + `fonts/` | Self-hosted @font-face declarations and woff2 files (no CDN requests) |
|
|
119
|
+
| `fonts.css` + `fonts/` | Self-hosted @font-face declarations and woff2 files (no CDN requests); `fonts/features.json` lists each face's OpenType features for `remarque-audit`'s font-feature check |
|
|
118
120
|
| `tailwind.config.js` | Tailwind CSS **v3** configuration (v4 projects use an `@theme` block instead) |
|
|
119
121
|
| `package.json` | npm package manifest for `remarque-tokens` |
|
|
120
122
|
|
|
@@ -124,14 +126,15 @@ Gzip (level 9) / brotli (max) bytes per shipped stylesheet, enforced by `scripts
|
|
|
124
126
|
|
|
125
127
|
| File | gzip | brotli |
|
|
126
128
|
|------|-----:|-------:|
|
|
127
|
-
| `tokens.css` (effective: core + palette + prose) |
|
|
129
|
+
| `tokens.css` (effective: core + palette + prose) | 16,628 | 13,973 |
|
|
128
130
|
| `tokens-core.css` | 4,790 | 3,995 |
|
|
129
131
|
| `tokens-palette.css` | 7,036 | 5,848 |
|
|
130
|
-
| `prose.css` |
|
|
132
|
+
| `prose.css` | 5,792 | 4,852 |
|
|
131
133
|
| `essay.css` | 7,914 | 6,680 |
|
|
132
134
|
| `broadsheet.css` | 7,463 | 6,241 |
|
|
133
|
-
| `forms.css` |
|
|
134
|
-
| `
|
|
135
|
+
| `forms.css` | 8,412 | 6,987 |
|
|
136
|
+
| `chrome.css` | 3,296 | 2,772 |
|
|
137
|
+
| `print.css` | 2,542 | 2,095 |
|
|
135
138
|
| `theme.css` | 1,744 | 1,452 |
|
|
136
139
|
| `fonts.css` | 612 | 484 |
|
|
137
140
|
|
package/REMARQUE.md
CHANGED
|
@@ -187,6 +187,30 @@ This is why the measure is palette-tier: it is a property of the font choice, no
|
|
|
187
187
|
|
|
188
188
|
---
|
|
189
189
|
|
|
190
|
+
## Type Scale
|
|
191
|
+
|
|
192
|
+
Seven rungs, in two kinds (0.30, #131). The **text rungs** are fixed: 13, 14, 17 and 19px (`--text-micro`, `--text-meta`, `--text-body`, `--text-body-lg`). Their steps (1.08, 1.21, 1.12) aren't a modular ratio; each rung is a floor. 13 and 14 are the USWDS minimums for fine print and small text, 17 is Remarque's body minimum, and 19 is the lede. The **display rungs** are fluid: `--text-section`, `--text-title` and `--text-display` scale with the viewport between fixed bounds. So the scale changes *shape* with the viewport, not only size, and that's deliberate. On a phone the headings sit close to the text (steps 1.16, 1.36, 1.47) so a title doesn't eat the screen; on a desktop they open out to about a 1.5 ratio:
|
|
193
|
+
|
|
194
|
+
| Viewport | section | title | display | steps from the 19px lede up |
|
|
195
|
+
|---|---|---|---|---|
|
|
196
|
+
| 375px | 22px | 30px | 44px | 1.16 · 1.36 · 1.47 |
|
|
197
|
+
| 1280px | 28.8px | 44.8px | 70.4px | 1.52 · 1.56 · 1.57 |
|
|
198
|
+
| 1920px | 32px | 48px | 80px | 1.68 · 1.50 · 1.67 |
|
|
199
|
+
|
|
200
|
+
There's no rung between 19 and the section heading (1.52 at 1280px); headings are meant to be a clear jump from text, not a gradient. Any change to a rung's *value* is a core-tier change and goes to a vote first.
|
|
201
|
+
|
|
202
|
+
**Heading ladder in prose.** Adjacent levels differ in face or style, not only in size, so each still reads as its own level in print and under forced colors:
|
|
203
|
+
|
|
204
|
+
| Level | Face | Size / weight |
|
|
205
|
+
|---|---|---|
|
|
206
|
+
| `h2` | display (Newsreader) roman | `--text-section` / medium |
|
|
207
|
+
| `h3` | body (Inter) | 19px / semibold |
|
|
208
|
+
| `h4` | display roman (0.30; was Inter 17/600, which read as h3) | 17px / medium |
|
|
209
|
+
| `h5` | display italic | 17px / medium |
|
|
210
|
+
| `h6` | mono | 14px |
|
|
211
|
+
|
|
212
|
+
**The lede is in `--color-fg`.** `.text-body-lg` sets no color. Give a standfirst full foreground; its size and leading already set it apart, and in `--color-fg-muted` it read *quieter* than the body below it, which inverts the hierarchy. (The demo and this spec's samples used `fg-muted` until 0.30.)
|
|
213
|
+
|
|
190
214
|
## Editorial Microtypography
|
|
191
215
|
|
|
192
216
|
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.
|
|
@@ -218,6 +242,14 @@ Labels that want a caps-like treatment use **real capitals with added tracking**
|
|
|
218
242
|
- **Enforced.** `remarque-audit` fails any rule that asks a face for a feature it lacks (small caps, oldstyle figures, `font-feature-settings` tags), checked against `fonts/features.json`. That's the OpenType feature list of every shipped face, generated with fontTools by `scripts/font-features.py`. A rule that is deliberately face-dependent says so with a `remarque-feature: conditional` comment and is reported, not failed. Consumers add their own faces with `--font-features <manifest.json>` (same shape). Defaults count: a face whose default figures are lining satisfies `lining-nums` without `lnum`, and a tabular-by-default face satisfies `tabular-nums` without `tnum`.
|
|
219
243
|
- Uppercase for *running text* (a shouted sentence, a heading set in caps) is still out: the rule is about labels.
|
|
220
244
|
|
|
245
|
+
### Code Blocks, Figures and Links
|
|
246
|
+
|
|
247
|
+
- **Code-block titles.** `data-title="src/config.ts"` on a `<pre>` inside `.remarque-prose` renders a mono filename line across the top of the block, with a hairline under it and no filled tab (0.30, #129). Shiki and rehype-pretty-code can both emit the attribute.
|
|
248
|
+
- **Line highlighting** belongs to your highlighter's markup. Remarque supplies only the color: an element marked `data-highlighted-line` (rehype-pretty-code's attribute) gets the `--color-accent-subtle` wash that `mark` uses.
|
|
249
|
+
- **Figures** sit in the column: `.remarque-prose figure` cancels the browser's 40px side indent, and its vertical rhythm comes from the prose flow like any other block.
|
|
250
|
+
- **External-link marks (opt-in).** `.remarque-prose--external-marks` adds a small ↗ after links marked `rel="external"`, on screen only. `print.css` prints each link's URL with `::after`, and a screen marker on the same pseudo-element would replace it. The arrow has empty alt text; `rel` carries the meaning.
|
|
251
|
+
- Code blocks never wrap; they scroll in their own box (see "Wrapping & Optical Defaults").
|
|
252
|
+
|
|
221
253
|
### Drop Caps (opt-in)
|
|
222
254
|
|
|
223
255
|
`.remarque-prose--dropcap`, applied alongside `.remarque-prose`, floats a large italic display-face initial letter on the first paragraph only — the way a printed essay opens its first page. Rules:
|
|
@@ -352,6 +384,27 @@ Sequential and diverging ramps are **not** shipped as tokens — a fixed `--colo
|
|
|
352
384
|
Each entry carries `oklch` (structured `{l, c, h}`) and `oklchCss` (a ready-to-use `oklch(...)` string) — no parsing required.
|
|
353
385
|
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.
|
|
354
386
|
|
|
387
|
+
### Color-vision robustness: opt-in recipes (0.30, #128)
|
|
388
|
+
|
|
389
|
+
Since 0.28, `remarque-audit` warns when the categorical slots are hard to tell apart under simulated color-vision deficiency, and the default palette triggers it. The six slots span only 0.042 L (light) and 0.029 L (dark), so hue is their only channel, and the worst pair under simulation is ΔE2000 3.5 (light, deuteranopia) and 2.7 (dark, protanopia); below about 5, marks blur together. **The defaults stay as they are** (ratified 7–0, `vote-1792892374374-55pw50q`): a value change would recolor every consumer's charts, no consumer has asked, and the defaults are pinned to the upstream `oklch-terminal-themes` palettes by the golden gate. The "never color alone" rule above (labels, markers, dash patterns) is what keeps a default-palette chart readable.
|
|
390
|
+
|
|
391
|
+
If your charts lean on color, override the slots with this **lightness ladder**: same hues and chroma, lightness alternated so that neighbouring slots differ in both hue and lightness. Every slot clears 3.4:1 on `--color-bg` and `--color-surface`. It is a palette-tier override, so `remarque-drift` reports it as INFO, and it's verified by the same coherence math the audit uses:
|
|
392
|
+
|
|
393
|
+
| Slot | Light | Dark |
|
|
394
|
+
|---|---|---|
|
|
395
|
+
| `--color-viz-1` | `oklch(0.46 0.121 250.5)` | `oklch(0.72 0.129 249.5)` |
|
|
396
|
+
| `--color-viz-2` | `oklch(0.61 0.111 85.5)` | `oklch(0.88 0.13 85.3)` |
|
|
397
|
+
| `--color-viz-3` | `oklch(0.52 0.12 24.6)` | `oklch(0.56 0.129 310.2)` |
|
|
398
|
+
| `--color-viz-4` | `oklch(0.40 0.12 144.8)` | `oklch(0.72 0.129 25.4)` |
|
|
399
|
+
| `--color-viz-5` | `oklch(0.61 0.121 309.6)` | `oklch(0.56 0.129 144.9)` |
|
|
400
|
+
| `--color-viz-6` | `oklch(0.55 0.09 195.8)` | `oklch(0.88 0.119 194.8)` |
|
|
401
|
+
|
|
402
|
+
The worst pair under normal, protan, deutan and tritan simulation rises to **ΔE2000 11.7 (light) and 13.6 (dark)**. The lightness alternates rather than ramps, so the series don't read as an ordered scale; keep it that way if you reorder them. The trade-off is that the lightest slots read as brighter marks than the rest. Run against the audit, the recipe clears every `viz-cvd-*` warning; `accent-vs-viz-1` still warns, because viz-1 keeps the accent's hue (rotate it if links sit next to your charts).
|
|
403
|
+
|
|
404
|
+
**Dark tertiary text.** Dark `--color-muted` (L 0.60) passes WCAG 2.x at 4.92:1 on `--color-bg`, but the advisory APCA column measures it at Lc −34.5, about half its light twin's 70.0. `--color-muted: oklch(0.64 0.01 80)` in your dark block gives 5.77:1 on bg, 5.49:1 on surface, and Lc −40.3; it moves muted closer to `--color-fg-muted` (0.70), so check the audit's text-tier warnings after.
|
|
405
|
+
|
|
406
|
+
**When the defaults change.** Revisit when a consumer asks, or when the coherence warnings are promoted to failures (the default palette would then fail its own audit). The change then goes through an upstream `oklch-terminal-themes` release first and a pin bump here, so the golden gate stays exact; never as a per-token exception in the golden gate.
|
|
407
|
+
|
|
355
408
|
---
|
|
356
409
|
|
|
357
410
|
## Syntax Highlighting
|
|
@@ -1261,6 +1314,14 @@ disabled control never gets a `data-state` — it uses the native
|
|
|
1261
1314
|
register, not one of the three feedback hues (REMARQUE.md "State Colors":
|
|
1262
1315
|
disabled is deliberately not ANSI-derived).
|
|
1263
1316
|
|
|
1317
|
+
**State is never color alone (0.30, #128; WCAG 1.4.1).** Each state message carries a glyph in every mode: ✕ error, ⚠ warning, ✓ success. Under simulated deuteranopia the default error and success colors are only ΔE2000 2.7 (light) and 1.7 (dark) apart, so the glyph is what tells them apart. It has empty alt text, so the message has to say what's wrong in words ("Enter an email address", not "Invalid"), and `aria-invalid` still carries the state for screen readers. Before 0.30 the glyphs appeared only under forced colors.
|
|
1318
|
+
|
|
1319
|
+
**Read-only, pending and auto-growing fields (0.30, #129).**
|
|
1320
|
+
- `readonly` inputs get a quiet `--color-bg-subtle` fill: visibly not editable, still legible, focusable and copyable. The selector is `[readonly]`, not `:read-only`, which also matches checkboxes, selects and buttons.
|
|
1321
|
+
- **Pending submit is text, not a spinner.** Disable the button, change its label ("Send" → "Sending…"), and announce completion or failure through a polite live region (`<p role="status">`). Don't put `aria-busy` on the button (it's for regions being updated, and AT support on controls is poor), and never add a spinner (AGENT_RULES bans decorative motion).
|
|
1322
|
+
- `.remarque-input--autosize` on a `<textarea>` grows with its content (`field-sizing: content`, Baseline 2026-06), from the 6rem minimum up to a 24rem cap. Older engines keep the fixed, scrollable textarea.
|
|
1323
|
+
- **Hover only where hover exists.** Button and input hover washes sit inside `@media (hover: hover)`, so a tap doesn't leave a control looking hovered. `:active` gives buttons a pressed state (border to `--color-fg`) on every input type.
|
|
1324
|
+
|
|
1264
1325
|
As a zero-JS bonus layer, `.remarque-input:user-invalid` (guarded with
|
|
1265
1326
|
`@supports selector(:user-invalid)`) picks up the same error border once
|
|
1266
1327
|
a browser supports the pseudo-class and the user has actually interacted
|
|
@@ -1351,6 +1412,98 @@ instead and keep Remarque to the page's editorial chrome around it.
|
|
|
1351
1412
|
|
|
1352
1413
|
---
|
|
1353
1414
|
|
|
1415
|
+
## Chrome Module
|
|
1416
|
+
|
|
1417
|
+
`remarque-tokens/chrome` (`chrome.css`, 0.29, #127): the site-chrome and wayfinding pieces this spec requires and never shipped CSS for, so every consumer hand-rolled its own. The skip link alone had four implementations across the flagship, tsundoku, the starter and this demo. One `left: -9999px` broke under RTL, and a tsundoku pagination kept its "disabled" endpoints as focusable links with `pointer-events: none`. The module isn't aggregated into `tokens.css`; import it after the tokens. Everything in it speaks in the mono meta register, and every link meets the 44px floor on coarse pointers. Each piece is a registry item whose markup contract `scripts/test-registry.mjs` asserts.
|
|
1418
|
+
|
|
1419
|
+
### Skip link
|
|
1420
|
+
|
|
1421
|
+
The first focusable element in `<body>`, targeting `<main id="main-content" tabindex="-1">`. It's visually hidden (clip-path, never an off-screen offset) until focused, then shows as a filled `--color-fg` block at the top inline-start corner on `--z-skip-link`. The `tabindex="-1"` lets focus land on `<main>`; `chrome.css` drops the outline for that programmatic focus. `.remarque-sr-only` is the same visually-hidden pattern for any text that's for screen readers only. `print.css` hides the skip link.
|
|
1422
|
+
|
|
1423
|
+
<!-- registry-usage:skip-link — scripts/build-registry.mjs (issue #127)
|
|
1424
|
+
extracts the fenced block immediately below into
|
|
1425
|
+
registry/skip-link.json's usage.html verbatim. -->
|
|
1426
|
+
```html
|
|
1427
|
+
<body>
|
|
1428
|
+
<a class="remarque-skip-link" href="#main-content">Skip to content</a>
|
|
1429
|
+
<header>…site navigation…</header>
|
|
1430
|
+
<main id="main-content" tabindex="-1">…</main>
|
|
1431
|
+
</body>
|
|
1432
|
+
```
|
|
1433
|
+
|
|
1434
|
+
### Breadcrumb
|
|
1435
|
+
|
|
1436
|
+
The Reference/Docs "breadcrumb kicker": `nav[aria-label="Breadcrumb"] > ol`. The last item is the current page: `aria-current="page"` on a `<span>`, **not a link**. The separator is generated by CSS with empty alt text, so screen readers don't read "slash", and is never typed into the DOM.
|
|
1437
|
+
|
|
1438
|
+
<!-- registry-usage:breadcrumb — scripts/build-registry.mjs (issue #127)
|
|
1439
|
+
extracts the fenced block immediately below into
|
|
1440
|
+
registry/breadcrumb.json's usage.html verbatim. -->
|
|
1441
|
+
```html
|
|
1442
|
+
<nav class="remarque-breadcrumb" aria-label="Breadcrumb">
|
|
1443
|
+
<ol>
|
|
1444
|
+
<li><a href="/">Home</a></li>
|
|
1445
|
+
<li><a href="/reference/">Reference</a></li>
|
|
1446
|
+
<li><span aria-current="page">Configuration</span></li>
|
|
1447
|
+
</ol>
|
|
1448
|
+
</nav>
|
|
1449
|
+
```
|
|
1450
|
+
|
|
1451
|
+
### Pager
|
|
1452
|
+
|
|
1453
|
+
Prev/next between adjacent pages: the Essay and Reference/Docs footers, and Plate figure navigation. Links carry `rel="prev"` and `rel="next"`; a label line ("Previous", "Next", in the label voice) sits over the target's title. **An endpoint with nothing beyond it is omitted, never rendered as a disabled-looking link.** `.remarque-pager-next` stays on the inline-end side even when it's alone. An optional `.remarque-pager-series` row gives "Part N of M" and a `<details>` list of every part, with the current one `aria-current="page"` and not a link. There's no progress bar; the text carries it. Archives use `.remarque-pagination`: numbered pages in `nav[aria-label="Pagination"] > ol`, where the current page is `aria-current="page"` text, and prev/next at the ends are links when there's somewhere to go and plain text when there isn't.
|
|
1454
|
+
|
|
1455
|
+
<!-- registry-usage:pager — scripts/build-registry.mjs (issue #127)
|
|
1456
|
+
extracts the fenced block immediately below into
|
|
1457
|
+
registry/pager.json's usage.html verbatim. -->
|
|
1458
|
+
```html
|
|
1459
|
+
<nav class="remarque-pager" aria-label="More writing">
|
|
1460
|
+
<div class="remarque-pager-series">
|
|
1461
|
+
<details>
|
|
1462
|
+
<summary>Part 2 of 3 · Quiet Interfaces</summary>
|
|
1463
|
+
<ol>
|
|
1464
|
+
<li><a href="/writing/quiet-1/">Against Decoration</a></li>
|
|
1465
|
+
<li><span aria-current="page">Typography as Interface</span></li>
|
|
1466
|
+
<li><a href="/writing/quiet-3/">The Reading Column</a></li>
|
|
1467
|
+
</ol>
|
|
1468
|
+
</details>
|
|
1469
|
+
</div>
|
|
1470
|
+
<a class="remarque-pager-prev" href="/writing/quiet-1/" rel="prev">
|
|
1471
|
+
<span class="remarque-pager-label">Previous</span>
|
|
1472
|
+
<span class="remarque-pager-title">Against Decoration</span>
|
|
1473
|
+
</a>
|
|
1474
|
+
<a class="remarque-pager-next" href="/writing/quiet-3/" rel="next">
|
|
1475
|
+
<span class="remarque-pager-label">Next</span>
|
|
1476
|
+
<span class="remarque-pager-title">The Reading Column</span>
|
|
1477
|
+
</a>
|
|
1478
|
+
</nav>
|
|
1479
|
+
|
|
1480
|
+
<nav class="remarque-pagination" aria-label="Pagination">
|
|
1481
|
+
<ol>
|
|
1482
|
+
<li><span>Previous</span></li>
|
|
1483
|
+
<li><span aria-current="page">1</span></li>
|
|
1484
|
+
<li><a href="/writing/page/2/">2</a></li>
|
|
1485
|
+
<li><a href="/writing/page/3/">3</a></li>
|
|
1486
|
+
<li><a href="/writing/page/2/" rel="next">Next</a></li>
|
|
1487
|
+
</ol>
|
|
1488
|
+
</nav>
|
|
1489
|
+
```
|
|
1490
|
+
|
|
1491
|
+
### Tags
|
|
1492
|
+
|
|
1493
|
+
"Tags or categories (mono text, not pill badges)": a wrapping row of plain mono links in a `<ul>` with an accessible name, each with `rel="tag"`. **No border, fill or radius.** An outlined or filled chip is a documented deviation (`DESIGN-DEVIATIONS.md`), not personalization, the same rule as semver pills in the Changelog archetype.
|
|
1494
|
+
|
|
1495
|
+
<!-- registry-usage:tags — scripts/build-registry.mjs (issue #127)
|
|
1496
|
+
extracts the fenced block immediately below into
|
|
1497
|
+
registry/tags.json's usage.html verbatim. -->
|
|
1498
|
+
```html
|
|
1499
|
+
<ul class="remarque-tags" aria-label="Tags">
|
|
1500
|
+
<li><a href="/tags/typography/" rel="tag">typography</a></li>
|
|
1501
|
+
<li><a href="/tags/design-systems/" rel="tag">design-systems</a></li>
|
|
1502
|
+
</ul>
|
|
1503
|
+
```
|
|
1504
|
+
|
|
1505
|
+
---
|
|
1506
|
+
|
|
1354
1507
|
## Palette Deck
|
|
1355
1508
|
|
|
1356
1509
|
A tiny runtime (`remarque-tokens/deck`, `deck.js`) for switching between
|
|
@@ -1627,9 +1780,42 @@ already uses) with negative fixtures proving the schemas actually reject
|
|
|
1627
1780
|
malformed input, and an independent recomputation of every `integrity`
|
|
1628
1781
|
hash proving it wasn't just carried over from a stale build.
|
|
1629
1782
|
|
|
1630
|
-
**
|
|
1631
|
-
|
|
1632
|
-
|
|
1783
|
+
**Installing with the shadcn CLI (#130).** The `remarque:contract` items
|
|
1784
|
+
above fail shadcn's `type` enum, so a parallel, schema-valid copy is
|
|
1785
|
+
generated from the same definitions into `registry/shadcn/` (an index
|
|
1786
|
+
`registry.json` plus one `<name>.json` per item, freshness-checked by
|
|
1787
|
+
`build-registry.mjs --check`). Each is `type: "registry:item"` (the
|
|
1788
|
+
schema's catch-all for a bundle that is neither a component nor a theme),
|
|
1789
|
+
with `files[]` entries of `type: "registry:file"` and a `target`
|
|
1790
|
+
(`src/styles/remarque/<name>.css`, `src/components/remarque/<name>.usage.html`;
|
|
1791
|
+
the palette-deck item is markup-only). `remarque-tokens` is listed under
|
|
1792
|
+
`dependencies` (npm packages, which the CLI installs), and Remarque-only
|
|
1793
|
+
fields (`version`, `integrity`, `docs` URL, `cssVars` names) sit under
|
|
1794
|
+
`meta`. Verified against `shadcn@latest` (needs a `components.json`):
|
|
1795
|
+
|
|
1796
|
+
```sh
|
|
1797
|
+
npx shadcn@latest add https://williamzujkowski.github.io/remarque/registry/shadcn/essay.json
|
|
1798
|
+
```
|
|
1799
|
+
|
|
1800
|
+
or, with `"registries": { "@remarque": "https://williamzujkowski.github.io/remarque/registry/shadcn/{name}.json" }`
|
|
1801
|
+
in `components.json`:
|
|
1802
|
+
|
|
1803
|
+
```sh
|
|
1804
|
+
npx shadcn@latest add @remarque/essay
|
|
1805
|
+
```
|
|
1806
|
+
|
|
1807
|
+
The CLI copies files only; import the stylesheet after `remarque-tokens`
|
|
1808
|
+
yourself. The chrome items (`skip-link`, `breadcrumb`, `pager`, `tags`)
|
|
1809
|
+
share one `chrome.css`, so adding several writes the same file. The
|
|
1810
|
+
validating schemas are vendored from shadcn in `scripts/vendor/` and
|
|
1811
|
+
enforced in `scripts/test-registry.mjs`. The package export
|
|
1812
|
+
`remarque-tokens/registry/shadcn/essay` resolves via the existing
|
|
1813
|
+
`./registry/*` wildcard.
|
|
1814
|
+
|
|
1815
|
+
**What this deliberately is NOT.** No Remarque-specific installer — there
|
|
1816
|
+
is no `npx remarque-registry add essay` command (the shadcn CLI above is
|
|
1817
|
+
the optional install path for the Tailwind/Astro subset; the
|
|
1818
|
+
`remarque:contract` items carry no `files[].target`). An agent's expected workflow
|
|
1633
1819
|
is fetch the JSON, read `usage.html`/the CSS `content`, and apply it with
|
|
1634
1820
|
its own editing tools — the registry replaces transcription-from-prose,
|
|
1635
1821
|
not an installer. No archetype "starter-page" items this round: the
|