remarque-tokens 0.16.0 → 0.18.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 CHANGED
@@ -62,12 +62,14 @@ These are not guidelines. Agents must follow them literally.
62
62
  - Within the centered column, use `.content-reading` for prose-width sections (46rem, auto-centered within the 72rem column)
63
63
  - Do NOT use Tailwind's `max-w-reading` or `max-w-standard` on page sections — these only set max-width without centering. Use the CSS classes `.content-reading` / `.content-standard` instead, which include `margin-inline: auto`
64
64
  - Code blocks (`<pre>`) inherit the width of their parent container — no special wrapping needed when the parent is already constrained
65
+ - Stacking order is structural, not personalization: never author a bare `z-index` number — reference `tokens-core.css`'s `--z-*` scale (`--z-base`/`--z-sticky`/`--z-dropdown`/`--z-overlay`/`--z-modal`/`--z-toast`/`--z-skip-link`). Tailwind v4 has no `--z-index-*` theme namespace, so use arbitrary values (`z-[var(--z-modal)]`) rather than bare Tailwind numbers. See REMARQUE.md "Stacking."
65
66
 
66
67
  ### Color
67
68
 
68
69
  - Accent color is used for exactly two things: inline links and one interactive element per viewport
69
70
  - Dark mode is not an inverted light mode — it must be independently tuned for readability
70
71
  - Background colors use `--color-bg`, never pure white (#fff) or pure black (#000)
72
+ - State colors (`--color-error`/`--color-success`/`--color-warning`/`--color-disabled`, plus the `-subtle` banner-background companions on the first three) are for feedback moments only — form validation, status banners, disabled controls — never for decoration. The one-accent rule above still governs everything else. See REMARQUE.md "State Colors."
71
73
 
72
74
  ### Components
73
75
 
@@ -143,6 +145,7 @@ project/
143
145
  ├── prose.css # .remarque-prose long-form styling (aggregated by tokens.css; own subpath for opt-out)
144
146
  ├── essay.css # Optional Essay module: sidenotes + sticky TOC rail (own subpath, NOT aggregated — import explicitly)
145
147
  ├── broadsheet.css # Optional Broadsheet pattern: masthead, lead, entry list, post kicker (own subpath, NOT aggregated — import explicitly)
148
+ ├── forms.css # Optional form control primitives: field/input/checkbox/radio/button, state-color wiring (own subpath, NOT aggregated — import explicitly)
146
149
  ├── print.css # Print stylesheet (own subpath, NOT aggregated — import explicitly)
147
150
  ├── theme.css # Tailwind v4 adapter (@theme inline) — import after tailwindcss + tokens
148
151
  ├── tailwind.config.js # Tailwind v3 ONLY — v4 projects use theme.css instead
@@ -205,6 +208,7 @@ Before considering any implementation complete, verify:
205
208
  - [ ] Every page maps to an archetype (Essay, Dossier, Notebook, Landing, Reference/Docs, Changelog, Gallery)
206
209
  - [ ] 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)
207
210
  - [ ] 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 `font-variant-caps: all-small-caps`, never `text-transform: uppercase`
211
+ - [ ] 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; 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
208
212
  - [ ] Mobile version is roomy — not a compressed desktop layout
209
213
  - [ ] Mobile nav links have ≥44px touch targets
210
214
  - [ ] No pure white or pure black backgrounds
@@ -212,6 +216,8 @@ Before considering any implementation complete, verify:
212
216
  - [ ] Muted text placed on `--color-surface` still meets 4.5:1 (check the surface pairing, not just bg)
213
217
  - [ ] All transitions use the motion duration tokens (reduced-motion support depends on it)
214
218
  - [ ] Skip-to-content link present and functional
219
+ - [ ] If state colors are used (error/success/warning/disabled): only for feedback moments (validation, banners, disabled controls), never decoration; every state text color and `-subtle` companion passes `npm run audit`
220
+ - [ ] No bare `z-index` numbers anywhere in the page's CSS — every stacking layer references `tokens-core.css`'s `--z-*` scale
215
221
  - [ ] OG meta tags present (og:title, og:description, og:image)
216
222
  - [ ] `<html lang="en">` attribute set
217
223
  - [ ] The site looks more like a book than a web app
package/CHANGELOG.md CHANGED
@@ -4,6 +4,128 @@ 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.18.0 — 2026-07-23
8
+
9
+ Form control primitives + reference components (closes #27, closes #30).
10
+
11
+ ### Added
12
+ - **`forms.css` — form control primitives module (issue #27).** New own-subpath
13
+ module (`remarque-tokens/forms`, `remarque-tokens/forms.css`), NOT aggregated
14
+ into `tokens.css` — matches `essay.css`/`broadsheet.css`/`print.css`'s
15
+ opt-in convention. Spec-native (built directly from the issue, not
16
+ graduated from a downstream site — no prior flagship implementation to
17
+ re-express).
18
+ - `.remarque-field` (label + control + help/error message vertical stack),
19
+ `.remarque-field-label` (meta voice, same declarations as `.text-label`),
20
+ `.remarque-field-required`, `.remarque-field-message`.
21
+ - `.remarque-input` — shared class across `<input>`/`<textarea>`/`<select>`:
22
+ body-voice text, `--color-border-bold` boundary (functional, 3:1-checked),
23
+ `--radius-sm` (tighter than the general 8px ceiling — form controls stay
24
+ more precise-reading), ≥44px tall, `::placeholder` at `--color-muted`
25
+ (a conscious AA choice, documented — placeholder is supplementary hint
26
+ copy, never the field's only label).
27
+ - `.remarque-checkbox` / `.remarque-radio` — `accent-color` only, no
28
+ `appearance: none`, no hand-drawn replacement; native controls keep every
29
+ platform accessibility behavior. Sized at `--space-5` (24px), wrapped in
30
+ a `<label>` carrying the 44px touch target.
31
+ - `.remarque-button` (+ `--primary` variant) — quiet by rule: bordered,
32
+ transparent, body-voice text by default; `--primary` is the one
33
+ sanctioned accent placement per viewport, still unfilled (accent
34
+ text/border, hover washes in `--color-accent-subtle`, never a solid
35
+ fill). Disabled state (native `:disabled`) uses `--color-disabled`.
36
+ - Validation state wiring on the 0.17.0 state-color tokens:
37
+ `.remarque-field[data-state="error"|"success"|"warning"]` recolors the
38
+ input border and message text together; always pairs with a real
39
+ `aria-invalid`/`aria-describedby` on the input (`data-state` is paint
40
+ only). `.remarque-input:user-invalid`, guarded with
41
+ `@supports selector(:user-invalid)`, is a zero-JS bonus layer.
42
+ - `.remarque-table` / `.remarque-table-wrap` — a standalone-table
43
+ re-scoping of `prose.css`'s table rules (mono `th`, 2px header rule, 1px
44
+ row rules, `.num` tabular-lining columns) for data tables that shouldn't
45
+ be wrapped in the full `.remarque-prose` container. Lives in `forms.css`
46
+ rather than a fifth subpath — AGENT_RULES.md's build order already
47
+ groups tables with buttons/cards as one supplementary-UI step.
48
+ - REMARQUE.md "Forms" section — full markup contract, state-wiring rules,
49
+ restraint rules restated (radius/touch-targets/no-fake-replacements/
50
+ quiet-buttons/placeholder-contrast), standalone-tables note, and "when
51
+ NOT to use" (Remarque is editorial-first — contact/search/newsletter
52
+ moments, not app UIs). AGENT_RULES.md's File Structure Convention and
53
+ Quality Checklist gain matching entries.
54
+ - `scripts/audit.mjs`'s `--src .` invocation covers `forms.css` for free
55
+ (font-floor + no-hardcoded-color scans — no new pairings needed, this
56
+ module introduces no new color tokens).
57
+ - **Reference components (demo site, issue #30)** — `site/src/components/`:
58
+ `Button.astro`, `Input.astro` (a `.remarque-field` wrapper — label/help/
59
+ error props, wires `for`/`id`/`aria-describedby`/`aria-invalid`),
60
+ `Table.astro` (`.remarque-table`, standalone). New demo page
61
+ `site/pages/components` exercises every input state (default/focus/error/
62
+ success/disabled), both button variants, and a sample table in both
63
+ themes — added to `site/tests/helpers.ts`'s `PAGES` for visual-regression
64
+ coverage (new baselines for this page only; no existing baseline changed)
65
+ and linked from the site nav.
66
+
67
+ ## 0.17.0 — 2026-07-23
68
+
69
+ Semantic state colors + z-index scale (closes #26, closes #29).
70
+
71
+ ### Added
72
+ - **State colors (`tokens-palette.css`, PALETTE tier)** — four semantic slots for feedback
73
+ moments (form validation, status banners, disabled controls), never decoration; the
74
+ one-accent rule still governs everything else. Hand-authored from the house ANSI hue
75
+ conventions, both themes, all three dark-mode conventions (`@media
76
+ prefers-color-scheme: dark`, `[data-theme="dark"]`, `:root.dark`):
77
+ - `--color-error` (hue 25, ANSI red) — light `oklch(0.52 0.12 25)` 5.44:1 bg / 5.28:1
78
+ surface; dark `oklch(0.62 0.11 26)` 5.07:1 bg / 4.82:1 surface.
79
+ - `--color-success` (hue 145, ANSI green) — light `oklch(0.51 0.12 145)` 5.08:1 bg /
80
+ 4.93:1 surface; dark `oklch(0.61 0.11 145)` 5.36:1 bg / 5.10:1 surface.
81
+ - `--color-warning` (hue 85, ANSI yellow) — light `oklch(0.52 0.105 85)` 5.17:1 bg /
82
+ 5.02:1 surface; dark `oklch(0.62 0.11 85)` 5.29:1 bg / 5.03:1 surface. The hard case:
83
+ yellow's low luminance-contrast means this slot solves noticeably darker in light
84
+ mode than error/success, the same shape as `--color-syntax-constant`'s solve.
85
+ - `--color-disabled` — aliased to `var(--color-muted)` in both themes: neutral
86
+ hue-80 muted family, deliberately **not** ANSI-derived.
87
+ - `-subtle` banner-background companions on the first three (`--color-error-subtle`/
88
+ `--color-success-subtle`/`--color-warning-subtle`, modeled on `--color-accent-subtle`
89
+ — near-bg lightness, state hue, low chroma) verified so `--color-fg` stays ≥4.5:1 on
90
+ them (the pairing that matters for callout/banner body text). No `-subtle` for
91
+ `--color-disabled` — a disabled control is quieter, not tinted.
92
+ - Every state text color ≥4.5:1 against `--color-bg` **and** `--color-surface`, both
93
+ themes; `scripts/audit.mjs` gains the 11 new pairings, `scripts/test-audit.mjs`'s
94
+ fixtures cover all seven tokens plus a must-fail case (`state-color-fails.css`).
95
+ - `scripts/theme.mjs` (`remarque-theme`) derives error/success/warning from a source
96
+ theme's red/green/yellow ANSI slots — keep-if-passing lightness checked against the
97
+ **stricter** of `--color-bg`/`--color-surface` at once (a new dual-target solver,
98
+ `keepOrSolveDual`/`solveDual`), not just one target like every other slot. Disabled
99
+ aliases the already-derived `--color-muted`. `-subtle` companions derive like
100
+ `--color-accent-subtle` (fixed 0.95 / bg+0.06 starting lightness — not the theme's
101
+ own solved bg lightness, which can exceed 0.95), nudged toward the extreme only if a
102
+ pathological theme doesn't already clear 4.5:1 against `--color-fg`. Self-verify
103
+ `CHECKS` mirrors `audit.mjs`'s new pairings exactly.
104
+ - `scripts/palette-golden.mjs` golden-gates all seven new tokens for free (it already
105
+ iterates whatever the bridge emits) — ΔE2000 ≤ 2.0 against `remarque-light`/
106
+ `remarque-dark`'s ANSI derivation, same identity/serialization contract as the rest
107
+ of the default palette. All new tokens land at ΔE ≤ 0.5 (see the PR description for
108
+ the full per-token table).
109
+ - REMARQUE.md "State Colors" — semantics, usage table, derivation note; Enforcement
110
+ Checklist gains the state-color pairing line.
111
+ - **Z-index scale (`tokens-core.css`, CORE tier)** — stacking is structural, not
112
+ personalization. A small ordinal scale, sparse gaps of 10: `--z-base` (0), `--z-sticky`
113
+ (10), `--z-dropdown` (20), `--z-overlay` (30), `--z-modal` (40), `--z-toast` (50),
114
+ `--z-skip-link` (60, tops everything). Wired into `essay.css`'s sticky TOC rail
115
+ (`z-index: var(--z-sticky)`) and the demo site's skip-to-content link
116
+ (`focus:z-[var(--z-skip-link)]`, replacing a hardcoded Tailwind `focus:z-50`).
117
+ `theme.css`'s header comment documents the Tailwind v4 workaround — v4 has no
118
+ `--z-index-*` theme namespace, so `z-*` utilities can't map to these directly; use
119
+ arbitrary values (`z-[var(--z-modal)]`) instead, same pattern as the motion durations.
120
+ REMARQUE.md "Stacking" — the scale, and the rule that consumer CSS must never author a
121
+ bare `z-index` number. AGENT_RULES.md's Layout/Quality Checklist sections gain matching
122
+ lines.
123
+
124
+ ### Changed
125
+ - **`scripts/tokens-json.mjs`** — `typeOf()` now types `--z-*` tokens as `number`
126
+ (unitless stacking-order integers), matching the existing `--leading-*` treatment,
127
+ instead of falling through to the generic `string` type.
128
+
7
129
  ## 0.16.0 — 2026-07-23
8
130
 
9
131
  Broadsheet pattern — masthead, lead article, numbered entry list, post-header kicker, graduated from the flagship (closes #36).
package/REMARQUE.md CHANGED
@@ -90,6 +90,23 @@ The audit remains the gate regardless of provenance — `remarque-theme` self-ve
90
90
 
91
91
  ---
92
92
 
93
+ ## State Colors
94
+
95
+ Four semantic slots — `--color-error`, `--color-success`, `--color-warning`, `--color-disabled` — for feedback moments: form validation, status banners, disabled controls. **These are for feedback, not decoration.** The one-accent rule ("Visual Rules" / "Changing the Accent Hue") still governs expressive color everywhere else — a state color says "this specific thing failed/succeeded/needs attention/can't be used right now," not "this section is important." Don't recolor headings, borders, or chrome with a state color; reserve it for the actual feedback element (an inline error message, a success toast, a disabled button's text).
96
+
97
+ Three of the four carry a `-subtle` companion — `--color-error-subtle`, `--color-success-subtle`, `--color-warning-subtle` — a faint tinted background for callout/banner-style feedback (an error banner, a success toast), sized the same way `--color-accent-subtle` is: near-`--color-bg` lightness, the state's hue, low chroma. `--color-disabled` has no `-subtle` companion — a disabled control is quieter than its enabled state, not tinted; use `--color-surface`/`--color-bg-subtle` for a disabled control's background if it needs one.
98
+
99
+ | Slot | Use for | `-subtle` companion |
100
+ |---|---|---|
101
+ | `--color-error` | Validation errors, failure states, destructive-action confirmation text | `--color-error-subtle` (banner/callout background) |
102
+ | `--color-success` | Confirmation messages, completed states | `--color-success-subtle` |
103
+ | `--color-warning` | Caution messages, states needing attention before proceeding | `--color-warning-subtle` |
104
+ | `--color-disabled` | Text/icon color for a disabled control | — (no subtle background; see above) |
105
+
106
+ **Derivation:** hand-authored in `tokens-palette.css` from the house ANSI hue conventions — error hue 25 (red), success hue 145 (green), warning hue 85 (yellow) — the same conventions `--color-syntax-string`/`--color-syntax-constant` already use for green/yellow. `scripts/remarque-theme` derives all three from a source theme's `red`/`green`/`yellow` ANSI slots (keep-if-passing lightness, checked against the **stricter** of `--color-bg` and `--color-surface` at once, not just one), exactly like every other slot in "Color Providers." `--color-disabled` is deliberately **not** ANSI-derived — it aliases the already-derived neutral `--color-muted` family, since a disabled state is a muted/quiet register, not a hue. Warning (hue 85, yellow) is the hard case in light mode — yellow has low luminance-contrast against a light background, so its solved lightness sits noticeably darker than the other two states, the same shape as `--color-syntax-constant`'s solve. Every state text color holds ≥ 4.5:1 against `--color-bg` **and** `--color-surface` in both themes; every `-subtle` background holds `--color-fg` ≥ 4.5:1 on it (the pairing that matters for a callout's body text) — both enforced by `scripts/audit.mjs` and golden-gated (ΔE2000 ≤ 2.0) against the upstream themes like the rest of the default palette.
107
+
108
+ ---
109
+
93
110
  ## Font Slots
94
111
 
95
112
  Remarque uses a three-slot font system. Each slot has a strict role, and each slot accepts a small set of approved faces — swap the face, keep the role.
@@ -365,6 +382,26 @@ Motion in Remarque is nearly invisible. The only permitted motion:
365
382
 
366
383
  ---
367
384
 
385
+ ## Stacking
386
+
387
+ Stacking order is structural, not personalization — `tokens-core.css` (CORE tier, never overridden) carries a small ordinal `--z-*` scale:
388
+
389
+ | Token | Value | Use for |
390
+ |---|---:|---|
391
+ | `--z-base` | 0 | Default stacking context — the implicit baseline |
392
+ | `--z-sticky` | 10 | Sticky headers/rails (essay.css's TOC rail) |
393
+ | `--z-dropdown` | 20 | Dropdown/select menus |
394
+ | `--z-overlay` | 30 | Overlays/backdrops |
395
+ | `--z-modal` | 40 | Modal dialogs |
396
+ | `--z-toast` | 50 | Toast/notification stacks |
397
+ | `--z-skip-link` | 60 | The skip-to-content link — tops everything; it must never be obscured when focused |
398
+
399
+ **The rule: never a bare `z-index` number in consumer CSS.** Reference one of these seven tokens instead — an unnamed `z-index: 999` (or any other asserted integer) can't be reasoned about against the rest of the page's stacking contexts, and it's the same "hardcoded value bypasses the token system" problem as a literal hex color or a bare font-size. The scale is deliberately sparse (gaps of 10) so a future layer can be inserted between two existing rungs without renumbering everything above it.
400
+
401
+ Tailwind v4 ships `z-*` as a static, non-themable utility scale — there's no `--z-index-*` entry in its default `@theme` to map these into. `theme.css` (the Tailwind v4 adapter) documents the workaround in its header comment: use arbitrary values with the tokens, `z-[var(--z-modal)]` / `focus:z-[var(--z-skip-link)]`, the same pattern already used for motion durations (`duration-[var(--motion-fast)]`).
402
+
403
+ ---
404
+
368
405
  ## USWDS Accessibility Compliance
369
406
 
370
407
  Remarque adopts typography and accessibility standards from the [US Web Design System (USWDS)](https://designsystem.digital.gov/documentation/accessibility/) and [WCAG 2.1](https://www.w3.org/WAI/WCAG21/Understanding/):
@@ -419,6 +456,8 @@ Every PR that ships Remarque pages MUST pass (`npm run audit` automates the colo
419
456
  - [ ] No hardcoded hex/rgb colors — only `var(--color-*)` tokens.
420
457
  - [ ] Body line-height ≥ 1.5 (Remarque target: 1.75).
421
458
  - [ ] Every `--color-syntax-*` slot ≥ 4.5:1 against `--color-code-bg` in both themes (see "Syntax Highlighting").
459
+ - [ ] 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.
460
+ - [ ] No bare `z-index` number in consumer CSS — reference `tokens-core.css`'s `--z-*` scale (see "Stacking").
422
461
 
423
462
  Agents reviewing PRs should reject changes that violate any line above without explicit rationale.
424
463
 
@@ -939,6 +978,149 @@ the archive itself.
939
978
 
940
979
  ---
941
980
 
981
+ ## Forms
982
+
983
+ Native form-control primitives — field wrapper, text input, checkbox,
984
+ radio, button — shipped as their own subpath, `remarque-tokens/forms`
985
+ (issue #27). Unlike the Essay Module and the Broadsheet pattern, this
986
+ module was not graduated from a downstream site implementation; it is
987
+ spec-native, built directly from the issue rather than re-expressing an
988
+ existing literal.
989
+
990
+ ### Markup contract
991
+
992
+ ```html
993
+ <div class="remarque-field">
994
+ <label class="remarque-field-label" for="email">Email</label>
995
+ <input class="remarque-input" type="email" id="email" name="email"
996
+ aria-describedby="email-help" />
997
+ <p class="remarque-field-message" id="email-help">
998
+ We'll never share your email.
999
+ </p>
1000
+ </div>
1001
+
1002
+ <div class="remarque-field" data-state="error">
1003
+ <label class="remarque-field-label" for="email2">Email</label>
1004
+ <input class="remarque-input" type="email" id="email2" name="email2"
1005
+ aria-invalid="true" aria-describedby="email2-message" required />
1006
+ <p class="remarque-field-message" id="email2-message" role="alert">
1007
+ Enter a valid email address.
1008
+ </p>
1009
+ </div>
1010
+
1011
+ <label class="remarque-checkbox">
1012
+ <input type="checkbox" name="subscribe" />
1013
+ <span>Subscribe to updates</span>
1014
+ </label>
1015
+
1016
+ <label class="remarque-radio">
1017
+ <input type="radio" name="plan" value="a" />
1018
+ <span>Plan A</span>
1019
+ </label>
1020
+
1021
+ <button class="remarque-button" type="button">Cancel</button>
1022
+ <button class="remarque-button remarque-button--primary" type="submit">Submit</button>
1023
+ ```
1024
+
1025
+ | Class | Applies to | Notes |
1026
+ |---|---|---|
1027
+ | `.remarque-field` | wrapping `<div>` | Label + control + message vertical stack. Layout only — no border/background of its own. |
1028
+ | `.remarque-field-label` | `<label>` | Meta voice (mono, small caps, `--color-muted`) — same declarations as `.text-label`, repeated per the non-mixin precedent essay.css/broadsheet.css already set. |
1029
+ | `.remarque-field-required` | `<span aria-hidden="true">` inside the label | Accent-colored asterisk. `aria-hidden` — the input's own `required` attribute is the real signal a screen reader announces. |
1030
+ | `.remarque-field-message` | `<p>` after the control | Help text by default; recolored by the parent's `data-state`. Wire `aria-describedby` on the input to this element's `id` in every case, valid or not. |
1031
+ | `.remarque-input` | `<input>` / `<textarea>` / `<select>` | Body-voice text, `--color-border-bold` boundary, `--radius-sm`, ≥44px tall. |
1032
+ | `.remarque-checkbox` / `.remarque-radio` | wrapping `<label>` | `accent-color` on the native control — no `appearance: none`, no hand-drawn replacement. The wrapping label carries the 44px touch target; the control itself stays at its natural (enlarged) size. |
1033
+ | `.remarque-button` | `<button>` | Quiet default: bordered, transparent, body-voice text. |
1034
+ | `.remarque-button--primary` | `<button>` | The one sanctioned accent placement per viewport (Visual Rules) — accent text + accent border, never a solid accent fill. Hover washes in `--color-accent-subtle`, it does not darken to a filled block. |
1035
+
1036
+ ### State wiring
1037
+
1038
+ `data-state="error"` / `"success"` / `"warning"` on `.remarque-field`
1039
+ recolors both the input's border and the message text from the 0.17.0
1040
+ state-color tokens (`--color-error`/`--color-success`/`--color-warning`).
1041
+ `data-state` is the PAINT layer only — always pair it with the real
1042
+ accessibility signal on the input itself: `aria-invalid="true"` (screen
1043
+ readers announce this; `data-state` alone is invisible to them) and
1044
+ `aria-describedby` pointing at the `.remarque-field-message` id. A
1045
+ disabled control never gets a `data-state` — it uses the native
1046
+ `:disabled`/`disabled` state and `--color-disabled`, which is a muted
1047
+ register, not one of the three feedback hues (REMARQUE.md "State Colors":
1048
+ disabled is deliberately not ANSI-derived).
1049
+
1050
+ As a zero-JS bonus layer, `.remarque-input:user-invalid` (guarded with
1051
+ `@supports selector(:user-invalid)`) picks up the same error border once
1052
+ a browser supports the pseudo-class and the user has actually interacted
1053
+ with an invalid field — it never replaces authoring `data-state="error"`
1054
+ explicitly, since `:user-invalid` alone has no matching text-message
1055
+ recolor and no `aria-invalid` wiring of its own.
1056
+
1057
+ ### Restraint rules restated
1058
+
1059
+ - **Border radius**: form controls cap at `--radius-sm` (4px) — a
1060
+ tighter ceiling than AGENT_RULES.md's general `--radius-md` (8px)
1061
+ maximum for other chrome. A smaller, more precise-reading radius suits
1062
+ a control the user directly manipulates.
1063
+ - **Touch targets**: every interactive control — input, textarea,
1064
+ select, button, and the `<label>` wrapping a checkbox/radio — is
1065
+ ≥44×44px (WCAG 2.5.5 AAA, USWDS floor; see "Touch Targets" above and
1066
+ `.nav-link`'s identical convention in the demo site's `globals.css`).
1067
+ - **No fake replacements**: checkboxes and radios stay native elements
1068
+ recolored with `accent-color` only. No `appearance: none` plus a
1069
+ hand-drawn box/dot — that would trade away native keyboard behavior,
1070
+ forced-colors-mode support, and screen-reader state announcements for
1071
+ a purely cosmetic win this system doesn't need.
1072
+ - **Buttons stay quiet**: text-only or bordered, never filled/solid by
1073
+ default — the primary variant included. Accent appears as text/border
1074
+ color, never as a background fill (Visual Rules' one-accent rule and
1075
+ Components' "never filled/solid as default" both still govern the
1076
+ emphasized variant).
1077
+ - **Placeholder contrast**: `--color-muted` (4.5:1 on `--color-bg`/
1078
+ `--color-surface`), a conscious choice — placeholder text is
1079
+ supplementary hint copy, never the field's only label, so AA is the
1080
+ correct tier rather than `--color-fg-muted`'s AAA.
1081
+
1082
+ ### Standalone tables
1083
+
1084
+ `forms.css` also ships `.remarque-table` / `.remarque-table-wrap`
1085
+ (issue #30) — the same visual language as `.remarque-prose table`
1086
+ (mono `th` voice, 2px header rule, 1px row rules, `.num` for tabular-
1087
+ lining right-aligned numeric columns), re-scoped under a top-level
1088
+ class so a standalone data grid doesn't have to wrap itself in the full
1089
+ `.remarque-prose` container (which also applies oldstyle proportional
1090
+ numerals and prose spacing rhythm a data table doesn't want):
1091
+
1092
+ ```html
1093
+ <div class="remarque-table-wrap">
1094
+ <table class="remarque-table">
1095
+ <caption>Example</caption>
1096
+ <thead>
1097
+ <tr><th>Name</th><th class="num">Count</th></tr>
1098
+ </thead>
1099
+ <tbody>
1100
+ <tr><td>Rows</td><td class="num">42</td></tr>
1101
+ </tbody>
1102
+ </table>
1103
+ </div>
1104
+ ```
1105
+
1106
+ Lives in `forms.css` rather than its own subpath — AGENT_RULES.md's
1107
+ build order already groups tables with buttons/cards/code blocks as one
1108
+ "supplementary UI" step, and this table variant is a small re-scoping,
1109
+ not enough surface area to justify a fourth optional module.
1110
+
1111
+ ### When NOT to use
1112
+
1113
+ Remarque is editorial-first. This module exists for the handful of
1114
+ form moments a written-word site actually needs — a contact form, a
1115
+ site search box, a newsletter signup — not for building application
1116
+ UIs. If a page needs more than a few fields, conditional multi-step
1117
+ logic, inline data tables of editable rows, or anything that starts to
1118
+ resemble a settings/dashboard screen, that is out of scope for
1119
+ Remarque's forms primitives; reach for a dedicated UI/form library
1120
+ instead and keep Remarque to the page's editorial chrome around it.
1121
+
1122
+ ---
1123
+
942
1124
  ## Signature Moves
943
1125
 
944
1126
  These are the repeatable visual tells that make a Remarque site recognizable:
package/essay.css CHANGED
@@ -421,6 +421,9 @@
421
421
  align-self: start;
422
422
  justify-self: start;
423
423
  position: sticky;
424
+ /* --z-sticky (tokens-core.css, issue #29) — never a bare z-index
425
+ number in consumer CSS. */
426
+ z-index: var(--z-sticky);
424
427
  top: var(--space-6);
425
428
  width: 100%;
426
429
  max-height: calc(100vh - var(--space-8));