remarque-tokens 0.29.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 +2 -1
- package/CHANGELOG.md +35 -0
- package/README.md +5 -4
- package/REMARQUE.md +97 -3
- package/forms.css +70 -19
- package/package.json +1 -1
- package/prose.css +65 -9
- package/registry/breadcrumb.json +1 -1
- package/registry/broadsheet.json +1 -1
- package/registry/essay.json +1 -1
- package/registry/forms.json +3 -3
- package/registry/pager.json +1 -1
- 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 +1 -1
- package/registry/tags.json +1 -1
- package/registry.json +10 -10
- package/scripts/audit.mjs +30 -2
- package/scripts/build-registry.mjs +49 -3
- package/tokens.d.ts +1 -1
- package/tokens.json +1 -1
package/AGENT_RULES.md
CHANGED
|
@@ -223,7 +223,7 @@ Before considering any implementation complete, verify:
|
|
|
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; disabled controls use `--color-disabled`, never a state color
|
|
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
|
|
227
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
|
|
228
228
|
- [ ] Mobile version is roomy — not a compressed desktop layout
|
|
229
229
|
- [ ] Mobile nav links have ≥44px touch targets (meets WCAG 2.5.8 AA; exceeds it, and meets 2.5.5 AAA)
|
|
@@ -272,6 +272,7 @@ npx remarque-audit --palette <file> --src <dir> --json
|
|
|
272
272
|
required: number; // minimum ratio (e.g. 4.5, 7.0, 3.0)
|
|
273
273
|
actual: number | null; // computed ratio rounded to 2dp, or null if fg/bg was unresolvable
|
|
274
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
|
|
275
276
|
error?: string; // present only when actual is null (unresolved var()/missing token)
|
|
276
277
|
}>;
|
|
277
278
|
gamut: Array<{
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,41 @@ 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
|
+
|
|
7
42
|
## 0.29.0 — 2026-10-24
|
|
8
43
|
|
|
9
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.
|
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
|
|
|
@@ -125,13 +126,13 @@ Gzip (level 9) / brotli (max) bytes per shipped stylesheet, enforced by `scripts
|
|
|
125
126
|
|
|
126
127
|
| File | gzip | brotli |
|
|
127
128
|
|------|-----:|-------:|
|
|
128
|
-
| `tokens.css` (effective: core + palette + prose) |
|
|
129
|
+
| `tokens.css` (effective: core + palette + prose) | 16,628 | 13,973 |
|
|
129
130
|
| `tokens-core.css` | 4,790 | 3,995 |
|
|
130
131
|
| `tokens-palette.css` | 7,036 | 5,848 |
|
|
131
|
-
| `prose.css` |
|
|
132
|
+
| `prose.css` | 5,792 | 4,852 |
|
|
132
133
|
| `essay.css` | 7,914 | 6,680 |
|
|
133
134
|
| `broadsheet.css` | 7,463 | 6,241 |
|
|
134
|
-
| `forms.css` |
|
|
135
|
+
| `forms.css` | 8,412 | 6,987 |
|
|
135
136
|
| `chrome.css` | 3,296 | 2,772 |
|
|
136
137
|
| `print.css` | 2,542 | 2,095 |
|
|
137
138
|
| `theme.css` | 1,744 | 1,452 |
|
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
|
|
@@ -1719,9 +1780,42 @@ already uses) with negative fixtures proving the schemas actually reject
|
|
|
1719
1780
|
malformed input, and an independent recomputation of every `integrity`
|
|
1720
1781
|
hash proving it wasn't just carried over from a stale build.
|
|
1721
1782
|
|
|
1722
|
-
**
|
|
1723
|
-
|
|
1724
|
-
|
|
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
|
|
1725
1819
|
is fetch the JSON, read `usage.html`/the CSS `content`, and apply it with
|
|
1726
1820
|
its own editing tools — the registry replaces transcription-from-prose,
|
|
1727
1821
|
not an installer. No archetype "starter-page" items this round: the
|
package/forms.css
CHANGED
|
@@ -171,8 +171,30 @@ textarea.remarque-input {
|
|
|
171
171
|
hand-drawn replacement icon, exactly the "fake replacement" this module
|
|
172
172
|
avoids) or accepting a single browser's rendering as the reference,
|
|
173
173
|
neither of which is worth it for one triangle. */
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
/* Hover washes only where hover exists: on touch, a tap leaves :hover
|
|
175
|
+
stuck on the last control until the next tap elsewhere (#129). */
|
|
176
|
+
@media (hover: hover) {
|
|
177
|
+
.remarque-input:hover {
|
|
178
|
+
border-color: var(--color-fg-muted);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* Read-only: visibly not editable, still fully legible and focusable
|
|
183
|
+
(the value can be selected and copied). A quiet fill, no new token.
|
|
184
|
+
[readonly], not :read-only, which also matches checkboxes, selects
|
|
185
|
+
and buttons. */
|
|
186
|
+
.remarque-input[readonly] {
|
|
187
|
+
background-color: var(--color-bg-subtle);
|
|
188
|
+
cursor: default;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/* Opt-in auto-growing textarea: field-sizing (Baseline 2026-06: Chrome
|
|
192
|
+
123, Firefox 152, Safari 26.2) replaces the JS resize snippet. Keeps
|
|
193
|
+
the textarea minimum below and caps the growth; older engines keep a
|
|
194
|
+
fixed, scrollable textarea. */
|
|
195
|
+
.remarque-input--autosize {
|
|
196
|
+
field-sizing: content;
|
|
197
|
+
max-height: 24rem;
|
|
176
198
|
}
|
|
177
199
|
|
|
178
200
|
/* Placeholder: --color-muted, the same 4.5:1-on-bg/-surface token used
|
|
@@ -285,8 +307,17 @@ textarea.remarque-input {
|
|
|
285
307
|
background-color var(--motion-fast) var(--motion-easing);
|
|
286
308
|
}
|
|
287
309
|
|
|
288
|
-
|
|
289
|
-
|
|
310
|
+
@media (hover: hover) {
|
|
311
|
+
.remarque-button:hover:not(:disabled) {
|
|
312
|
+
border-color: var(--color-fg-muted);
|
|
313
|
+
color: var(--color-fg);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* Pressed: the border goes to full fg for the duration of the press, on
|
|
318
|
+
every input type (touch has no hover to precede it). */
|
|
319
|
+
.remarque-button:active:not(:disabled) {
|
|
320
|
+
border-color: var(--color-fg);
|
|
290
321
|
color: var(--color-fg);
|
|
291
322
|
}
|
|
292
323
|
|
|
@@ -295,7 +326,15 @@ textarea.remarque-input {
|
|
|
295
326
|
border-color: var(--color-accent);
|
|
296
327
|
}
|
|
297
328
|
|
|
298
|
-
|
|
329
|
+
@media (hover: hover) {
|
|
330
|
+
.remarque-button--primary:hover:not(:disabled) {
|
|
331
|
+
color: var(--color-accent-hover);
|
|
332
|
+
border-color: var(--color-accent-hover);
|
|
333
|
+
background-color: var(--color-accent-subtle);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.remarque-button--primary:active:not(:disabled) {
|
|
299
338
|
color: var(--color-accent-hover);
|
|
300
339
|
border-color: var(--color-accent-hover);
|
|
301
340
|
background-color: var(--color-accent-subtle);
|
|
@@ -410,6 +449,30 @@ textarea.remarque-input {
|
|
|
410
449
|
}
|
|
411
450
|
}
|
|
412
451
|
|
|
452
|
+
/* ─── Non-color state cue, every mode (issue #128; WCAG 1.4.1) ──────────
|
|
453
|
+
* A glyph before each state message, so error, warning and success are
|
|
454
|
+
* told apart by shape, not only by color. Until 0.30 the glyphs existed
|
|
455
|
+
* only under forced colors (below); under deuteranopia simulation the
|
|
456
|
+
* default error and success colors are only ΔE2000 2.7 (light) and 1.7
|
|
457
|
+
* (dark) apart, so ordinary rendering needed them too. Empty alt text:
|
|
458
|
+
* the message text and aria-invalid carry the meaning for screen
|
|
459
|
+
* readers, and the glyph would only add noise. The message must still
|
|
460
|
+
* say what's wrong in words (REMARQUE.md "Forms"). */
|
|
461
|
+
.remarque-field[data-state="error"] .remarque-field-message::before {
|
|
462
|
+
content: "\2715\0020"; /* ✕ */
|
|
463
|
+
content: "\2715\0020" / "";
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.remarque-field[data-state="warning"] .remarque-field-message::before {
|
|
467
|
+
content: "\26A0\0020"; /* ⚠ */
|
|
468
|
+
content: "\26A0\0020" / "";
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.remarque-field[data-state="success"] .remarque-field-message::before {
|
|
472
|
+
content: "\2713\0020"; /* ✓ */
|
|
473
|
+
content: "\2713\0020" / "";
|
|
474
|
+
}
|
|
475
|
+
|
|
413
476
|
/* ─── Forced-colors reinforcement (issue #93) ────────────────────────────
|
|
414
477
|
* The validation-state wiring above is the textbook color-only-affordance
|
|
415
478
|
* case the issue names explicitly: error/success/warning are distinguished
|
|
@@ -423,9 +486,8 @@ textarea.remarque-input {
|
|
|
423
486
|
* Fix is geometry, not color, since color is exactly what forced-colors
|
|
424
487
|
* discards: each state gets a distinct `border-style`/`border-width`
|
|
425
488
|
* combination on the control itself (a shape that reads under ANY forced
|
|
426
|
-
* system border color),
|
|
427
|
-
*
|
|
428
|
-
* alone) is unchanged everywhere else. The glyph is supplementary, not
|
|
489
|
+
* system border color), on top of the glyph prefixed to the message line
|
|
490
|
+
* in every mode (above; forced-colors-only until 0.30). The glyph is supplementary, not
|
|
429
491
|
* the primary signal (a screen reader user already gets `aria-invalid` +
|
|
430
492
|
* the message text; a sighted forced-colors user gets the border shape);
|
|
431
493
|
* it is not marked `aria-hidden` because CSS generated content cannot
|
|
@@ -450,17 +512,6 @@ textarea.remarque-input {
|
|
|
450
512
|
border-width: 2px;
|
|
451
513
|
}
|
|
452
514
|
|
|
453
|
-
.remarque-field[data-state="error"] .remarque-field-message::before {
|
|
454
|
-
content: "\2715\0020"; /* ✕ */
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
.remarque-field[data-state="warning"] .remarque-field-message::before {
|
|
458
|
-
content: "\26A0\0020"; /* ⚠ */
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
.remarque-field[data-state="success"] .remarque-field-message::before {
|
|
462
|
-
content: "\2713\0020"; /* ✓ */
|
|
463
|
-
}
|
|
464
515
|
}
|
|
465
516
|
|
|
466
517
|
/* ─── Standalone data tables (issue #30) ──────────────────────────────────
|
package/package.json
CHANGED
package/prose.css
CHANGED
|
@@ -148,6 +148,28 @@
|
|
|
148
148
|
overflow-x: auto;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
.remarque-prose pre[data-title]::before {
|
|
152
|
+
/* Code-block title (#129): a mono filename line at the top of the
|
|
153
|
+
block, in the no-chrome voice: a hairline under it, no filled tab.
|
|
154
|
+
Set data-title="src/config.ts" on the <pre> (Shiki and
|
|
155
|
+
rehype-pretty-code both can). The negative margins reach the block's
|
|
156
|
+
edges through its padding. */
|
|
157
|
+
content: attr(data-title);
|
|
158
|
+
display: block;
|
|
159
|
+
margin: calc(-1 * var(--space-5)) calc(-1 * var(--space-5)) var(--space-4);
|
|
160
|
+
padding: var(--space-2) var(--space-5);
|
|
161
|
+
border-block-end: var(--border-width) var(--border-style) var(--color-border);
|
|
162
|
+
letter-spacing: var(--tracking-meta);
|
|
163
|
+
color: var(--color-muted);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* Line highlighting is the highlighter's markup; this is only the color
|
|
167
|
+
hook: mark a line with data-highlighted-line (rehype-pretty-code's
|
|
168
|
+
attribute) and it gets the quiet accent wash prose `mark` uses. */
|
|
169
|
+
.remarque-prose pre [data-highlighted-line] {
|
|
170
|
+
background-color: var(--color-accent-subtle);
|
|
171
|
+
}
|
|
172
|
+
|
|
151
173
|
.remarque-prose pre code {
|
|
152
174
|
background: none;
|
|
153
175
|
padding: 0;
|
|
@@ -168,6 +190,12 @@
|
|
|
168
190
|
border-radius: var(--radius-sm);
|
|
169
191
|
}
|
|
170
192
|
|
|
193
|
+
.remarque-prose figure {
|
|
194
|
+
/* The UA indents figures by 40px on both sides; a figure belongs to the
|
|
195
|
+
column. Its block rhythm comes from the `> * + *` rule above. */
|
|
196
|
+
margin-inline: 0;
|
|
197
|
+
}
|
|
198
|
+
|
|
171
199
|
.remarque-prose figcaption {
|
|
172
200
|
font-family: var(--font-mono);
|
|
173
201
|
font-size: var(--text-meta);
|
|
@@ -179,19 +207,24 @@
|
|
|
179
207
|
|
|
180
208
|
/* ─── Headings, continued (h4–h6) ───────────────────────── */
|
|
181
209
|
/*
|
|
182
|
-
* h2/h3 are defined above. h4–h6 complete the vocabulary
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
* used everywhere else for the
|
|
187
|
-
*
|
|
188
|
-
*
|
|
210
|
+
* h2/h3 are defined above. h4–h6 complete the vocabulary by alternating
|
|
211
|
+
* the font slots, so each level differs from its neighbour in face or
|
|
212
|
+
* style, not only in size: h3 is the body face at 19px semibold, h4 the
|
|
213
|
+
* display face's roman at 17px medium, h5 the display face's italic,
|
|
214
|
+
* h6 the mono "technical label" voice used everywhere else for the
|
|
215
|
+
* deepest level of metadata.
|
|
216
|
+
*
|
|
217
|
+
* h4 moved from the body face in 0.30 (#131, ratified in
|
|
218
|
+
* vote-1792892296073-vxi53q7): as Inter 17/600 under Inter 19/600 it
|
|
219
|
+
* differed from h3 by 2px and read as the same level. A face change
|
|
220
|
+
* survives print, prefers-contrast and forced colors, where a color
|
|
221
|
+
* difference would collapse. Sizes are unchanged.
|
|
189
222
|
*/
|
|
190
223
|
|
|
191
224
|
.remarque-prose h4 {
|
|
192
|
-
font-family: var(--font-
|
|
225
|
+
font-family: var(--font-display);
|
|
193
226
|
font-size: var(--text-body);
|
|
194
|
-
font-weight: var(--weight-
|
|
227
|
+
font-weight: var(--weight-medium);
|
|
195
228
|
line-height: var(--leading-title);
|
|
196
229
|
margin-top: var(--space-5);
|
|
197
230
|
margin-bottom: var(--space-2);
|
|
@@ -493,6 +526,22 @@
|
|
|
493
526
|
* it intentionally falls through to the serif fallback stack.
|
|
494
527
|
*/
|
|
495
528
|
|
|
529
|
+
/* ─── External-link marks (opt-in, #129) ──────────────────────────────
|
|
530
|
+
* .remarque-prose--external-marks adds a small ↗ after links marked
|
|
531
|
+
* rel="external". Screen only: print.css prints every link's URL after
|
|
532
|
+
* it with ::after, and a marker on the same pseudo-element would replace
|
|
533
|
+
* that (the bug the flagship hit hand-rolling this). The arrow is
|
|
534
|
+
* decoration with empty alt text; rel carries the meaning. */
|
|
535
|
+
@media screen {
|
|
536
|
+
.remarque-prose--external-marks a[rel~="external"]::after {
|
|
537
|
+
content: "\00a0\2197";
|
|
538
|
+
content: "\00a0\2197" / "";
|
|
539
|
+
display: inline-block;
|
|
540
|
+
color: var(--color-muted);
|
|
541
|
+
text-decoration: none;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
496
545
|
/* ─── Script-aware italics (issue #123) ───────────────────────────────
|
|
497
546
|
* Arabic, Persian, Urdu, Hebrew and CJK have no italic tradition, and
|
|
498
547
|
* browsers fake one by slanting the glyphs. The decorative italics
|
|
@@ -508,6 +557,13 @@
|
|
|
508
557
|
font-style: normal;
|
|
509
558
|
}
|
|
510
559
|
|
|
560
|
+
/* Upright h5 would now match h4 (both the display face's roman, and in
|
|
561
|
+
these scripts both fall back to the same face), so h5 steps down in
|
|
562
|
+
color instead (#131). */
|
|
563
|
+
:is(:lang(ar), :lang(fa), :lang(ur), :lang(he), :lang(zh), :lang(ja), :lang(ko)):is(.remarque-prose h5) {
|
|
564
|
+
color: var(--color-fg-muted);
|
|
565
|
+
}
|
|
566
|
+
|
|
511
567
|
:is(:lang(ar), :lang(fa), :lang(ur), :lang(he), :lang(zh), :lang(ja), :lang(ko)):is(.remarque-prose em) {
|
|
512
568
|
font-style: normal;
|
|
513
569
|
font-weight: var(--weight-semibold);
|
package/registry/breadcrumb.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"type": "remarque:contract",
|
|
5
5
|
"title": "Breadcrumb (chrome.css)",
|
|
6
6
|
"description": "The Reference/Docs breadcrumb kicker: `nav[aria-label=\"Breadcrumb\"] > ol`, the current page last as `<span aria-current=\"page\">` (never a link), separators generated by CSS with empty alt text and never typed into the DOM.",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.30.0",
|
|
8
8
|
"integrity": "sha256-V+FqUVopXBpnu9jfd8frbZJcs3NWmjv8KBuU/Uknaco=",
|
|
9
9
|
"dependencies": [
|
|
10
10
|
"remarque-tokens"
|
package/registry/broadsheet.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"type": "remarque:contract",
|
|
5
5
|
"title": "Broadsheet Pattern",
|
|
6
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 the label voice: tracked capitals, because the default mono face has no small caps (`font-variant-caps: all-small-caps` only on a face with `smcp` + `c2sc`; remarque-audit checks).",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.30.0",
|
|
8
8
|
"integrity": "sha256-0gdOLXhIStXi2nrbSvWuyaOvCfjAdTdJAeZkprafekU=",
|
|
9
9
|
"dependencies": [
|
|
10
10
|
"remarque-tokens"
|
package/registry/essay.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"type": "remarque:contract",
|
|
5
5
|
"title": "Essay Module",
|
|
6
6
|
"description": "Sidenotes + sticky TOC rail for long-form Essay pages: margin notes relocated into real DOM order, right after the paragraph that cites them, numbered by CSS counter and labeled `aria-label=\"Note N\"` in the same order the counter advances — the mechanism that prevents the sidenote aria-label/DOM-order transcription bug found in the flagship migration (#89, williamzujkowski.github.io#380).",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.30.0",
|
|
8
8
|
"integrity": "sha256-HwTP/hiT/oid/YHBlzSECSvG5wSg9ICYVfns7luc6SA=",
|
|
9
9
|
"dependencies": [
|
|
10
10
|
"remarque-tokens"
|