remarque-tokens 0.13.0 → 0.15.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
@@ -141,6 +141,8 @@ project/
141
141
  ├── tokens-core.css # CORE tier: type scale, spacing, widths, radius, motion, prose machinery. NEVER override
142
142
  ├── tokens-palette.css # PALETTE tier: font slots, colors, accent, reading measure. Override freely, then run the audit
143
143
  ├── prose.css # .remarque-prose long-form styling (aggregated by tokens.css; own subpath for opt-out)
144
+ ├── essay.css # Optional Essay module: sidenotes + sticky TOC rail (own subpath, NOT aggregated — import explicitly)
145
+ ├── print.css # Print stylesheet (own subpath, NOT aggregated — import explicitly)
144
146
  ├── theme.css # Tailwind v4 adapter (@theme inline) — import after tailwindcss + tokens
145
147
  ├── tailwind.config.js # Tailwind v3 ONLY — v4 projects use theme.css instead
146
148
  ├── public/
@@ -200,6 +202,7 @@ Before considering any implementation complete, verify:
200
202
  - [ ] No scroll-triggered or entrance animations exist
201
203
  - [ ] Every image has a 1px border and mono caption
202
204
  - [ ] Every page maps to an archetype (Essay, Dossier, Notebook, Landing, Reference/Docs, Changelog, Gallery)
205
+ - [ ] 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)
203
206
  - [ ] Mobile version is roomy — not a compressed desktop layout
204
207
  - [ ] Mobile nav links have ≥44px touch targets
205
208
  - [ ] No pure white or pure black backgrounds
package/CHANGELOG.md CHANGED
@@ -4,6 +4,69 @@ 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.15.0 — 2026-07-23
8
+
9
+ Essay module — sidenotes + sticky TOC rail, graduated from the flagship (closes #52).
10
+
11
+ ### Added
12
+ - **`essay.css`** (new subpath: `remarque-tokens/essay`, `remarque-tokens/essay.css`) —
13
+ optional Essay-archetype module, not aggregated into `tokens.css` or `prose.css`. Adds:
14
+ - `.remarque-sidenote-ref` / `.remarque-sidenote` — footnotes as in-flow margin notes.
15
+ Small-print inline block below `80rem` (`--color-border-bold`, the note's only visual
16
+ separator at that width); floats into the left gutter above it via a Tufte-style
17
+ negative-margin technique (`clear: left` stacks consecutive notes, no JS). Numbered
18
+ with CSS counters (`counter-reset` on `.remarque-prose`) rather than an authored number
19
+ — this package ships no build step, unlike the flagship's rehype transform. A repeat
20
+ citation of an already-noted footnote uses `.remarque-sidenote-ref--repeat` (styled
21
+ identically, doesn't advance the shared counter).
22
+ - `.remarque-footnotes` — a same-voice fallback for consumers without a DOM-reordering
23
+ build step: styles GFM's default end-of-document footnotes section instead of relocating
24
+ notes inline.
25
+ - `.remarque-toc-rail` — `position: sticky` right rail at `>= 80rem`, plain inline
26
+ collapsible `<details>` below it, same markup at every width (mono meta voice, small
27
+ caps rather than `text-transform: uppercase` — the flagship predates that rule).
28
+ - `.remarque-essay` — the opt-in 3-column grid shell (gutter / reading column / TOC
29
+ rail) that places the above two. Center track is `minmax(0, var(--content-reading))`,
30
+ identical to plain `.content-reading` centering; the grid never sets `max-width` or
31
+ `margin-inline` on its children, so the reading-column measure is unaffected with or
32
+ without this module.
33
+ - Single breakpoint, `>= 80rem` (1280px) — re-derived from `--content-standard` /
34
+ `--content-reading` / the core spacing scale rather than asserting the flagship's
35
+ literal rem measurements. Deleting the `@media` block leaves the correct
36
+ narrow-viewport/no-JS presentation, not a degraded one.
37
+ - `grid-row: 1 / span 999` on the rail, not `1 / -1` — ships with the flagship's
38
+ same-day grid-inflation fix already applied (`-1` resolves to row 1 with no explicit
39
+ `grid-template-rows`, inflating it to the rail's own height).
40
+ - **`print.css`** — forces `.remarque-sidenote` back to inline mode and `.remarque-essay`
41
+ back to block flow under `@media print`, making good on this file's existing "when a
42
+ sidenotes module lands" comment. (`.remarque-toc-rail` was already caught by the
43
+ existing `nav` / `[class*="toc-"]` print-hiding rules — no change needed there.)
44
+ - **REMARQUE.md "Essay Module"** — markup contract, when-to-use guidance, the
45
+ breakpoint-behavior table, and the grid-inflation provenance note.
46
+ - **AGENT_RULES.md** — File Structure Convention lists `essay.css` (and, newly, `print.css`,
47
+ which the convention had never mentioned); Quality Checklist gains an Essay-module line.
48
+ - **Demo site (`site/`)** — `writing/typography-as-interface` (the Essay archetype's VR
49
+ reference page) now exercises both sidenotes and the TOC rail. VR baselines updated:
50
+ `essay-{light,dark}-{desktop,mobile}.png` (4 files). New computed-style assertion:
51
+ the TOC rail's bounding box never overlaps `.remarque-prose`'s reading column at the
52
+ `1280px` desktop viewport.
53
+
54
+ ### Documentation
55
+ - **README.md "Graduation"** — adds this release to the "Historical examples" list as
56
+ the worked example of the "optional module" destination.
57
+
58
+
59
+
60
+ Triage bundle — prose/`.content-reading` pairing documented, `--motion-easing` bridged into the Tailwind v4 utility layer, USWDS/WCAG source citations added to the accessibility section (closes #23, closes #31, closes #32).
61
+
62
+ ### Added
63
+ - **`ease-remarque` Tailwind v4 utility** — `theme.css`'s `@theme inline` block now maps `--ease-remarque: var(--motion-easing)`, mirroring the `transitionTimingFunction.remarque` mapping the v3 `tailwind.config.js` already carried. Utility-class users of `duration-[var(--motion-fast)]` previously fell back to Tailwind's default `ease` timing function instead of the token (#31).
64
+ - **Demo site** — every `duration-[var(--motion-fast)]` call site (22, across `site/src/pages/*`, `layouts/BaseLayout.astro`, `components/Meta.astro`) now pairs with `ease-remarque` so the rendered CSS carries `var(--motion-easing)` instead of Tailwind's default.
65
+
66
+ ### Documentation
67
+ - **REMARQUE.md "Editorial Microtypography"** — states that `.remarque-prose` is typography-only (font, line height, numeral register, optical wrapping) and does not center the column; pair it with `.content-reading`, with a minimal code sample (#23).
68
+ - **REMARQUE.md "USWDS Accessibility Compliance"** — every cited WCAG 2.1 success criterion (1.4.3 Contrast Minimum, 1.4.11 Non-text Contrast, 2.5.5 Target Size, 2.3.3 Animation from Interactions, the last in the adjacent "Motion Rules" section) and USWDS claim (16px font-size floor, the overall accessibility approach) now links to its source (`w3.org/WAI/WCAG21/Understanding/*`, `designsystem.digital.gov`) (#32).
69
+
7
70
  ## 0.13.0 — 2026-07-23
8
71
 
9
72
  Syntax-highlighting palette — 9 `--color-syntax-*` slots, ANSI-derived, golden-gated (closes #53; consensus-ratified 3-0 with a binding taxonomy design record, 2026-07-23).
package/README.md CHANGED
@@ -119,6 +119,41 @@ See the [live demo](https://williamzujkowski.github.io/remarque/) for the origin
119
119
  | Border radius | 0.5rem max | Restrained. No "friendly SaaS" over-rounding |
120
120
  | Motion | 120ms/180ms | Hover and theme transitions only. No scroll-triggered, entrance, or staggered animations |
121
121
 
122
+ ## Graduation — when a site invention becomes system vocabulary
123
+
124
+ Consumer sites invent things (the flagship's sidenotes and theme deck, tsundoku's gallery
125
+ grid). Some of those inventions belong upstream; most are the site's own remarque in the
126
+ margin and should stay there. A local invention is upstreamable when **all four** hold:
127
+
128
+ 1. **Wanted by 2+ sites** (or one site plus a ratified case that it generalizes).
129
+ 2. **Consumes only tokens** — no hardcoded colors, sizes, or faces.
130
+ 3. **Passes the audit tooling** (`remarque-audit`, and `remarque-drift` reports it as
131
+ palette-tier INFO, not a core FAIL).
132
+ 4. **Violates no Never-list entry** in the spec — or arrives together with an explicit
133
+ spec amendment arguing the change.
134
+
135
+ **Three destinations.** Core spec (rare — core is identity, the bar is a fork-level
136
+ argument); optional module (sidenotes, TOC rail, palette deck); or *documented site-local
137
+ pattern* — explicitly not shared. The zine layer that inspired this checklist is the
138
+ exemplar of the third: sanctioned personality that would be diluted by generalizing it.
139
+
140
+ **Process (single-maintainer scale, no ceremony):** open an issue with before/after
141
+ screenshots and the token-purity check; record the decision in the changelog. Historical
142
+ examples: measure compensation and the Gallery archetype graduated; the essay
143
+ sidenotes/sticky-TOC-rail pair graduated as an optional module
144
+ ([#52](https://github.com/williamzujkowski/remarque/issues/52)); the theme deck was
145
+ re-scoped on the way up ([#56](https://github.com/williamzujkowski/remarque/issues/56));
146
+ the zine layer deliberately stayed local.
147
+
148
+ **Standing rulings** (ratified 3-0, 2026-07-23) on patterns the reviews kept re-flagging:
149
+
150
+ | Pattern | Ruling |
151
+ |---------|--------|
152
+ | Share links in post footers | Site-local, never a system module. If you build one: URL-encode every interpolated value and reject non-`https` schemes — title/frontmatter injection into share-intent URLs is the classic mistake. |
153
+ | Cross-hue hover accents | Not sanctioned anywhere. The hover move is a lightness/chroma shift within the accent hue (`--color-accent-hover` models it). A cross-hue hover is a documented deviation, not personalization. |
154
+ | Ambient/decorative animation | Site-local only, never in system modules — and the `prefers-reduced-motion` guard is mandatory, not advisory. A future audit scan may flag unguarded infinite animations (as a heuristic check, not a soundness proof). |
155
+ | Landing-page entry counts | Permitted as meta-voice data in the Landing archetype **only when computed from real inventory at build time**. A hardcoded count is a violation — counts drift (see the upstream count-drift bug class this system just fixed in its own theme provider). |
156
+
122
157
  ## Named For
123
158
 
124
159
  In fine art printmaking, a *remarque* is a small original drawing made by the artist in the margin of a proof print. Found only in limited editions, it is the mark that says: this was made by someone specific, with intention.
package/REMARQUE.md CHANGED
@@ -124,6 +124,14 @@ This is why the measure is palette-tier: it is a property of the font choice, no
124
124
 
125
125
  The book-craft layer: numerals, small caps, drop caps, pull quotes, and optical wrapping. These are the details that separate typeset prose from a web page that merely uses nice fonts.
126
126
 
127
+ `.remarque-prose` supplies typography only — font, line height, numeral register, optical wrapping — and stops short of centering the column, so pair it with `.content-reading` wherever the reading column needs to sit centered on the page:
128
+
129
+ ```html
130
+ <div class="remarque-prose content-reading">
131
+ <!-- article body -->
132
+ </div>
133
+ ```
134
+
127
135
  ### Numerals
128
136
 
129
137
  Two numeral registers, chosen by context:
@@ -353,19 +361,19 @@ Motion in Remarque is nearly invisible. The only permitted motion:
353
361
  - Parallax effects
354
362
  - Any animation on non-interactive elements
355
363
 
356
- **Reduced motion:** all motion must respect `prefers-reduced-motion` (WCAG 2.3.3). Because every sanctioned transition uses the two duration tokens, tokens.css zeroes `--motion-fast`/`--motion-normal` under `prefers-reduced-motion: reduce` — motion authored with the tokens is automatically safe. Never hardcode durations; any future opt-in animation must live inside a `(prefers-reduced-motion: no-preference)` query.
364
+ **Reduced motion:** all motion must respect `prefers-reduced-motion` ([WCAG 2.3.3](https://www.w3.org/WAI/WCAG21/Understanding/animation-from-interactions.html)). Because every sanctioned transition uses the two duration tokens, tokens.css zeroes `--motion-fast`/`--motion-normal` under `prefers-reduced-motion: reduce` — motion authored with the tokens is automatically safe. Never hardcode durations; any future opt-in animation must live inside a `(prefers-reduced-motion: no-preference)` query.
357
365
 
358
366
  ---
359
367
 
360
368
  ## USWDS Accessibility Compliance
361
369
 
362
- Remarque adopts typography and accessibility standards from the US Web Design System (USWDS) and WCAG 2.1:
370
+ Remarque adopts typography and accessibility standards from the [US Web Design System (USWDS)](https://designsystem.digital.gov/documentation/accessibility/) and [WCAG 2.1](https://www.w3.org/WAI/WCAG21/Understanding/):
363
371
 
364
372
  ### Font Size Floors
365
373
 
366
374
  | Token | Value | USWDS Rationale |
367
375
  |-------|-------|-----------------|
368
- | `--text-body` | 17px (1.0625rem) | Exceeds USWDS 16px minimum. Non-negotiable floor. |
376
+ | `--text-body` | 17px (1.0625rem) | Exceeds [USWDS 16px minimum](https://designsystem.digital.gov/components/typography/). Non-negotiable floor. |
369
377
  | `--text-meta` | 14px (0.875rem) | USWDS minimum for small text. Used sparingly for metadata, labels, captions. |
370
378
  | `--text-micro` | 13px (0.8125rem) | USWDS floor for timestamps and fine print only. Never for body content. |
371
379
 
@@ -373,13 +381,13 @@ Remarque adopts typography and accessibility standards from the US Web Design Sy
373
381
 
374
382
  | Pairing | Requirement | Notes |
375
383
  |---------|-------------|-------|
376
- | Normal text on background | 4.5:1 minimum | All text below 24px regular / 18.5px bold. Applies on `--color-surface` too, not just `--color-bg` |
377
- | Large text on background | 3:1 minimum | Display and title text (24px+) |
378
- | Functional non-text elements | 3:1 minimum | WCAG 1.4.11 — use `--color-border-bold` (meets 3:1). `--color-border` is decorative-only by design and exempt; never the sole boundary of an interactive element |
384
+ | Normal text on background | 4.5:1 minimum | [WCAG 1.4.3](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html) — all text below 24px regular / 18.5px bold. Applies on `--color-surface` too, not just `--color-bg` |
385
+ | Large text on background | 3:1 minimum | [WCAG 1.4.3](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html) — display and title text (24px+) |
386
+ | Functional non-text elements | 3:1 minimum | [WCAG 1.4.11](https://www.w3.org/WAI/WCAG21/Understanding/non-text-contrast.html) — use `--color-border-bold` (meets 3:1). `--color-border` is decorative-only by design and exempt; never the sole boundary of an interactive element |
379
387
 
380
388
  ### Touch Targets
381
389
 
382
- All interactive elements (links, buttons, inputs) must have a minimum touch target of **44x44px** (WCAG 2.5.5 AAA). This applies to nav links, theme toggles, footer links, chips, and any clickable element.
390
+ All interactive elements (links, buttons, inputs) must have a minimum touch target of **44x44px** ([WCAG 2.5.5](https://www.w3.org/WAI/WCAG21/Understanding/target-size.html) AAA). This applies to nav links, theme toggles, footer links, chips, and any clickable element.
383
391
 
384
392
  ### Line Height
385
393
 
@@ -488,7 +496,8 @@ Long-form writing. The core content type.
488
496
  - Low-noise footer navigation (previous/next)
489
497
 
490
498
  **Optionally includes:**
491
- - Side table of contents on desktop (outside reading column)
499
+ - Side table of contents on desktop (outside reading column) — see "Essay Module" below for the sanctioned sticky-rail implementation
500
+ - Sidenotes/footnotes as in-flow margin notes — same module, same rail-vs-gutter layout
492
501
 
493
502
  **Never includes:**
494
503
  - Sidebar content within the reading column
@@ -618,6 +627,153 @@ Cover-grid and catalog pages — a page whose primary content unit is a grid of
618
627
 
619
628
  ---
620
629
 
630
+ ## Essay Module
631
+
632
+ An optional pair of Essay-archetype features — margin notes and a sticky
633
+ table of contents — shipped as their own subpath, `remarque-tokens/essay`
634
+ (`essay.css`), rather than aggregated into `tokens.css` or `prose.css`.
635
+ Not every Essay wants either; both are opt-in.
636
+
637
+ **Provenance.** Graduated from williamzujkowski.github.io's
638
+ `rehype-sidenotes.mjs` + `global.css` sidenote/sticky-TOC implementation,
639
+ ratified for upstreaming in [issue #52](https://github.com/williamzujkowski/remarque/issues/52)
640
+ per the "Graduation" checklist in the package README — a footnote
641
+ vocabulary Remarque otherwise entirely lacked (this spec, until now,
642
+ permitted a side TOC at line ~499 without specifying anything about it).
643
+ No site-specific value was copied; every dimension below is re-derived
644
+ from tokens this package already ships (see `essay.css`'s header comment
645
+ for the value-by-value mapping).
646
+
647
+ ### Markup contract
648
+
649
+ ```html
650
+ <article class="remarque-essay">
651
+ <header class="content-reading">
652
+ <h1 class="text-display font-display">Essay Title</h1>
653
+ </header>
654
+
655
+ <nav class="remarque-toc-rail" aria-label="Table of contents">
656
+ <details open>
657
+ <summary>On this page</summary>
658
+ <ul>
659
+ <li><a href="#section-one">Section One</a></li>
660
+ <li><a href="#section-two">Section Two</a></li>
661
+ </ul>
662
+ </details>
663
+ </nav>
664
+
665
+ <div class="remarque-prose content-reading">
666
+ <h2 id="section-one">Section One</h2>
667
+ <p>
668
+ A claim worth a citation<a href="#note-1" id="note-1-ref"
669
+ class="remarque-sidenote-ref" aria-describedby="note-1"></a>.
670
+ </p>
671
+ <aside class="remarque-sidenote" id="note-1" role="note">
672
+ The note text, in real DOM order right after the paragraph that
673
+ cites it — not collected into an end-of-document section.
674
+ </aside>
675
+ </div>
676
+
677
+ <footer class="content-reading">...</footer>
678
+ </article>
679
+ ```
680
+
681
+ Requirements the CSS depends on:
682
+
683
+ - `.remarque-sidenote-ref` and its matching `.remarque-sidenote` MUST
684
+ alternate in strict DOM order (ref, then note, then the next ref, then
685
+ its note...) — the module numbers them with CSS counters
686
+ (`counter-reset` on `.remarque-prose`), not an authored number, so an
687
+ out-of-order pair mislabels every note after it.
688
+ - A footnote cited more than once: only the FIRST reference gets a
689
+ `.remarque-sidenote` after it; later citations of the same note use
690
+ `.remarque-sidenote-ref--repeat` (styled identically, does not advance
691
+ the counter) so the shared numbering doesn't skip.
692
+ - `role="note"` on every `.remarque-sidenote` — `<aside>`'s implicit role
693
+ is `complementary`, a landmark; an essay with several notes would
694
+ otherwise scatter that many unlabeled landmark regions through the
695
+ page. `aria-describedby` on the reference (pointing at the note's `id`)
696
+ is optional but recommended.
697
+ - `.remarque-toc-rail` wraps a plain `<details open>` — the SAME markup
698
+ at every viewport width, no JS breakpoint listener. `open` by default
699
+ is deliberate (see "Breakpoint behavior" below).
700
+
701
+ **No build step required, and none shipped.** This module doesn't ship or
702
+ depend on a rehype/remark transform — the contract above is satisfied by
703
+ hand-authored HTML exactly as well as by a build-time relocation step. If
704
+ your pipeline doesn't relocate GFM footnote definitions into DOM order
705
+ (most don't, out of the box), style the default end-of-document
706
+ `<section data-footnotes>` output with `.remarque-footnotes` instead —
707
+ same mono/meta voice, without the DOM-reordering accessibility upgrade:
708
+
709
+ ```html
710
+ <section class="remarque-footnotes">
711
+ <h2>Notes</h2>
712
+ <ol>
713
+ <li id="note-1">The note text. <a href="#note-1-ref">&#8617;</a></li>
714
+ </ol>
715
+ </section>
716
+ ```
717
+
718
+ Use `.remarque-sidenote` whenever you can relocate notes into reading
719
+ order — that relocation is the actual accessibility win this module
720
+ exists for. `.remarque-footnotes` is a same-voice fallback, not a lesser
721
+ tier of support, for the common case where you can't (yet).
722
+
723
+ ### When to use
724
+
725
+ Essay archetype only (sidenotes/a TOC rail are meaningless outside
726
+ long-form prose). Reach for sidenotes on essays with real citations,
727
+ asides, or sourcing that would otherwise interrupt the sentence; reach
728
+ for the TOC rail on any essay long enough that "where am I" becomes a
729
+ real question — roughly the same length threshold Reference/Docs pages
730
+ already assume. Both are independent opt-ins: an essay can use one, the
731
+ other, both, or neither without violating the archetype.
732
+
733
+ ### Breakpoint behavior
734
+
735
+ Gated on a single `>= 80rem` (1280px) `@media` query — no other
736
+ breakpoint, no JS:
737
+
738
+ | Viewport | Sidenotes | TOC |
739
+ |---|---|---|
740
+ | `< 80rem` (narrow) | Small-print block, in-flow, right after the citing paragraph — bordered with `--color-border-bold` (a 3:1 functional border, since it's the note's only visual separator at this width) | Inline collapsible `<details>` in the reading column, native disclosure triangle |
741
+ | `>= 80rem` (wide) | Floats into the LEFT margin gutter via a Tufte-style negative-margin technique, `clear: left` stacks consecutive notes, border relaxes to decorative `--color-border` (whitespace is now the real separator) | `position: sticky` in the RIGHT rail, `.remarque-essay` becomes a 3-column grid (gutter / reading column / rail) |
742
+
743
+ The rail and margin notes always live OUTSIDE `--content-reading` — that
744
+ is the entire point of the module. The center grid track is
745
+ `minmax(0, var(--content-reading))`, identical to the plain
746
+ `max-width: content-reading; margin-inline: auto` centering used
747
+ everywhere else in Remarque; a TOC rail or a floated sidenote can widen
748
+ the page shell, never the reading column itself. `.remarque-essay`'s grid
749
+ placement rule only ever sets `grid-column` on its children — it never
750
+ touches `max-width`/`margin-inline`, so the reading-column assertion
751
+ this spec's Enforcement Checklist already covers holds unmodified with
752
+ or without this module.
753
+
754
+ Delete `essay.css`'s single `@media (min-width: 80rem)` block and the
755
+ page still renders correctly — that IS the narrow-viewport presentation,
756
+ not a degraded one. No JavaScript is required at either width. (The
757
+ flagship implementation runs a small script to force `<details>` open on
758
+ every breakpoint crossing; this module holds to "no JS required" and
759
+ instead specifies `open` by default in the markup — a reader who
760
+ manually collapses the TOC below the breakpoint and then widens their
761
+ window keeps it collapsed until clicked again, a fully static cost this
762
+ module accepts in exchange for not requiring a script tag.)
763
+
764
+ ### Provenance note: the grid-inflation lesson
765
+
766
+ `.remarque-essay`'s TOC rail spans `grid-row: 1 / span 999`, not
767
+ `1 / -1`. With no explicit `grid-template-rows` (as here), `-1` resolves
768
+ to "the last explicit row line" — which, with zero explicit rows, is
769
+ row 1. The flagship implementation shipped exactly that bug: the rail's
770
+ own height inflated row 1, pushing the whole essay down and leaving a
771
+ visible gap between the header and the first paragraph on wide
772
+ viewports, fixed same-day. This module ships with the fix already
773
+ applied so the next consumer doesn't rediscover it.
774
+
775
+ ---
776
+
621
777
  ## Signature Moves
622
778
 
623
779
  These are the repeatable visual tells that make a Remarque site recognizable:
package/essay.css ADDED
@@ -0,0 +1,429 @@
1
+ /*
2
+ * Remarque — Essay Module (sidenotes + sticky TOC rail)
3
+ * ───────────────────────────────────────────────────────
4
+ * Optional Essay-archetype module: footnotes become in-flow margin notes
5
+ * (Tufte/gwern-style), and an "on this page" table of contents pins to a
6
+ * sticky rail alongside the reading column. Both are progressive
7
+ * enhancements over a plain Essay page — CSS-only, no JavaScript required
8
+ * at either breakpoint.
9
+ *
10
+ * PROVENANCE — graduated, not invented here. This module re-expresses, in
11
+ * Remarque's own token vocabulary, a design first built and shipped on
12
+ * williamzujkowski.github.io (`astro-site/src/lib/rehype-sidenotes.mjs` +
13
+ * `global.css`'s "Sidenotes"/"Sticky TOC side rail" blocks). Ratified for
14
+ * upstreaming in issue #52. No site-specific value (the flagship's literal
15
+ * `11.5rem`/`13rem`/`14rem`/`1280px` measurements) was copied — every
16
+ * dimension below is re-derived from tokens already in tokens-core.css /
17
+ * tokens-palette.css. See REMARQUE.md's "Essay Module" section for the
18
+ * full markup contract, and this file's inline comments for exactly which
19
+ * site value maps to which token and why a few mechanics changed shape
20
+ * (CSS counters instead of a build-time numbering step; `<details open>`
21
+ * by default instead of a JS breakpoint listener) on the way upstream.
22
+ *
23
+ * Own subpath (remarque-tokens/essay, remarque-tokens/essay.css) — NOT
24
+ * included by the tokens.css aggregator, and not required by prose.css:
25
+ * plenty of Essay pages want neither sidenotes nor a TOC rail. Import
26
+ * explicitly, after prose.css:
27
+ * @import 'remarque-tokens/prose.css';
28
+ * @import 'remarque-tokens/essay.css';
29
+ * (string form — see AGENT_RULES.md Pitfall #6.)
30
+ *
31
+ * ─── MARKUP CONTRACT (summary — full version in REMARQUE.md) ──────────
32
+ *
33
+ * <article class="remarque-essay">
34
+ * <header class="content-reading">...</header>
35
+ *
36
+ * <nav class="remarque-toc-rail" aria-label="Table of contents">
37
+ * <details open>
38
+ * <summary>On this page</summary>
39
+ * <ul>
40
+ * <li><a href="#section-one">Section One</a></li>
41
+ * </ul>
42
+ * </details>
43
+ * </nav>
44
+ *
45
+ * <div class="remarque-prose content-reading">
46
+ * <p>
47
+ * A claim worth a citation<a href="#note-1" id="note-1-ref"
48
+ * class="remarque-sidenote-ref" aria-describedby="note-1"></a>.
49
+ * </p>
50
+ * <aside class="remarque-sidenote" id="note-1" role="note">
51
+ * The note text, in real DOM order right after the paragraph that
52
+ * cites it — not collected into an end-of-document section.
53
+ * </aside>
54
+ * </div>
55
+ *
56
+ * <footer class="content-reading">...</footer>
57
+ * </article>
58
+ *
59
+ * DOM order is the accessibility requirement, not a presentational nicety:
60
+ * a screen reader hits a sidenote's content exactly where its reference
61
+ * occurs. `.remarque-sidenote-ref` and its matching `.remarque-sidenote`
62
+ * MUST alternate in strict DOM order (ref, then note, then the next ref,
63
+ * then its note...) — the counter mechanics below depend on it. `role=
64
+ * "note"` keeps a page with several sidenotes from scattering that many
65
+ * unlabeled `complementary` landmarks through the document (`<aside>`'s
66
+ * implicit role) — the same fix the flagship's rehype plugin documents.
67
+ *
68
+ * This module is deliberately silent on HOW that markup gets produced. A
69
+ * build-time transform (the flagship's rehype-sidenotes.mjs is one worked
70
+ * example, not a dependency of this package) or hand-authored HTML both
71
+ * satisfy the contract equally — see REMARQUE.md for the no-build-step
72
+ * fallback (`.remarque-footnotes`) if you don't have one.
73
+ *
74
+ * ─── BREAKPOINT ─────────────────────────────────────────────────────────
75
+ * Gated on >= 80rem (1280px, at the platform default 16px root) viewport
76
+ * width — the same threshold the flagship ratified, re-derived here from
77
+ * this package's own tokens instead of asserted as a bare magic number:
78
+ * `--content-standard` (72rem) is the widest a Remarque page's content
79
+ * ever grows, and the rail/gutter tracks below need `--space-11`+
80
+ * `--space-12` (10-12rem) and `--space-10` (8rem) of clearance on top of
81
+ * `--content-reading` (46rem default) plus two `--space-6` gaps — call it
82
+ * roughly 70-76rem of content width before both tracks have their
83
+ * documented minimum room, plus headroom for a scrollbar and window
84
+ * chrome. 80rem clears that with margin to spare across every approved
85
+ * body-face/measure pairing (see REMARQUE.md's Measure Compensation
86
+ * table) without needing a `--breakpoint-*` token this package doesn't
87
+ * otherwise have. Below it: sidenotes render as small-print inline blocks
88
+ * exactly where the plain document flow already places them, and the TOC
89
+ * is a collapsible `<details>` in the reading column — this is also the
90
+ * ONLY rendering a consumer who skips this module's `@media` block ever
91
+ * sees, so nothing here is required for the page to work.
92
+ */
93
+
94
+ /* ─── Sidenotes — default / narrow-viewport mode ────────────────────────
95
+ * `.remarque-sidenote` renders as a bordered small-print block right in
96
+ * the text flow by default. This IS the narrow-viewport fallback the
97
+ * spec calls for — zero extra markup, zero JS: the note just isn't
98
+ * floated into a margin yet.
99
+ *
100
+ * Numbering is generated, not authored: `counter-reset` on
101
+ * `.remarque-prose`, `counter-increment` on `.remarque-sidenote-ref` only,
102
+ * and `.remarque-sidenote` simply READING that same counter (no increment
103
+ * of its own — see the comment on that rule) keep the visible number in
104
+ * sync across the reference and its note with no number typed into
105
+ * either element — the flagship's rehype plugin injects a literal
106
+ * `<span class="sidenote-number">1</span>`/text node at build time; this
107
+ * package ships no build step, so CSS counters do the same job for a
108
+ * pure-CSS module. A repeat citation of an already-noted footnote should
109
+ * use `.remarque-sidenote-ref--repeat` (no `.remarque-sidenote` follows
110
+ * it) so the shared counter isn't advanced for a note that was already
111
+ * printed — mirrors the flagship's "only the first reference gets a
112
+ * sidenote" rule.
113
+ */
114
+ .remarque-prose {
115
+ counter-reset: remarque-sidenote;
116
+ }
117
+
118
+ .remarque-sidenote-ref {
119
+ counter-increment: remarque-sidenote;
120
+ font-family: var(--font-mono);
121
+ font-size: var(--text-meta);
122
+ /* USWDS floor rationale: --text-micro is reserved for timestamps/fine
123
+ print only (tokens-core.css, REMARQUE.md "Font Size Floors") — the
124
+ flagship's own marker used that size, but this module holds to
125
+ Remarque's stricter 14px floor for anything read as content. */
126
+ line-height: 0;
127
+ vertical-align: super;
128
+ color: var(--color-accent);
129
+ text-decoration: none;
130
+ }
131
+
132
+ .remarque-sidenote-ref::after {
133
+ content: counter(remarque-sidenote);
134
+ }
135
+
136
+ .remarque-sidenote-ref:hover {
137
+ color: var(--color-accent-hover);
138
+ text-decoration: underline;
139
+ }
140
+
141
+ .remarque-sidenote-ref--repeat {
142
+ counter-increment: none;
143
+ }
144
+
145
+ .remarque-sidenote {
146
+ /* No counter-increment here: `.remarque-sidenote-ref` already advanced
147
+ `remarque-sidenote` immediately before this element in DOM order —
148
+ incrementing again would double-count every pair (ref shows N, the
149
+ note that follows it would show N+1 instead of N). This element only
150
+ READS the counter its ref just set. */
151
+ display: block;
152
+ margin-block: var(--space-3);
153
+ padding-inline-start: var(--space-3);
154
+ /* border-bold: this is the note's ONLY visual separator from running
155
+ prose at narrow widths, so it must clear the 3:1 functional-border
156
+ minimum (WCAG 1.4.11) — same reasoning as .remarque-prose blockquote. */
157
+ border-inline-start: var(--border-width) var(--border-style) var(--color-border-bold);
158
+ font-family: var(--font-mono);
159
+ font-size: var(--text-meta);
160
+ line-height: var(--leading-meta);
161
+ color: var(--color-fg-muted);
162
+ }
163
+
164
+ /* The generated number precedes whatever content the note holds. If that
165
+ content is a single run of inline text (no wrapping <p>), the number
166
+ sits inline with the opening words — the flagship's "1 Note text..."
167
+ pairing. A `<p>`-wrapped note (multi-paragraph notes need it) instead
168
+ reads as a small label above the paragraph — a pure-CSS module can't
169
+ reach inside an arbitrary child the way a build-time transform can, so
170
+ this is the honest degradation: prefer bare text for single-line notes. */
171
+ .remarque-sidenote::before {
172
+ content: counter(remarque-sidenote) "\2002";
173
+ color: var(--color-accent);
174
+ font-weight: var(--weight-semibold);
175
+ }
176
+
177
+ .remarque-sidenote :where(p) {
178
+ margin: 0;
179
+ }
180
+
181
+ .remarque-sidenote :where(p) + :where(p) {
182
+ margin-top: var(--space-2);
183
+ }
184
+
185
+ /* ─── Sidenotes — wide-viewport mode: float into the left margin ────────
186
+ * Classic Tufte CSS technique: `.remarque-sidenote` isn't a grid child
187
+ * itself (it's nested inside `.remarque-prose`, which IS grid-column 2 —
188
+ * see `.remarque-essay` below) so grid placement doesn't apply to it
189
+ * directly. Instead it floats left with a negative inline-start margin
190
+ * exactly wide enough to clear its own box plus one column-gap unit,
191
+ * landing in the grid's column-1 gutter. `clear: left` stacks consecutive
192
+ * notes without any JS-computed offset.
193
+ *
194
+ * Width (`--space-10`, 8rem) and pull-out distance (that width plus
195
+ * `--space-6`, the same gap the grid below uses between tracks) are both
196
+ * drawn from the core spacing scale rather than asserting a bespoke
197
+ * sidebar-width value — this is the token-vocabulary re-expression of the
198
+ * flagship's hand-measured `11.5rem`/`13rem` pair.
199
+ */
200
+ @media (min-width: 80rem) {
201
+ .remarque-sidenote {
202
+ float: left;
203
+ clear: left;
204
+ width: var(--space-10);
205
+ margin-inline-start: calc(-1 * (var(--space-10) + var(--space-6)));
206
+ margin-block: var(--space-1) var(--space-4);
207
+ padding-inline-start: 0;
208
+ padding-inline-end: var(--space-2);
209
+ border-inline-start: none;
210
+ /* Decorative-only at this width (the gutter's own whitespace is the
211
+ real separator now) — `--color-border`, not `-bold`, matching the
212
+ flagship's own choice to relax the border once it's no longer the
213
+ sole boundary cue. */
214
+ border-inline-end: var(--border-width) var(--border-style) var(--color-border);
215
+ text-align: start;
216
+ }
217
+ }
218
+
219
+ /* ─── Footnotes fallback (no relocation build step) ─────────────────────
220
+ * `.remarque-footnotes` styles a traditional end-of-document footnotes
221
+ * section — e.g. GFM/mdast-util-to-hast's default
222
+ * `<section data-footnotes><h2>Footnotes</h2><ol>...</ol></section>`
223
+ * output — in the same meta voice as `.remarque-sidenote`, for a consumer
224
+ * who hasn't built (or doesn't want) a DOM-reordering transform. It is a
225
+ * same-voice fallback, not a lesser feature: use `.remarque-sidenote`
226
+ * whenever you can relocate notes into DOM reading order (the real a11y
227
+ * upgrade this module exists for); reach for `.remarque-footnotes` only
228
+ * when that isn't available yet.
229
+ *
230
+ * <section class="remarque-footnotes">
231
+ * <h2>Notes</h2>
232
+ * <ol>
233
+ * <li id="note-1">The note text. <a href="#note-1-ref">&#8617;</a></li>
234
+ * </ol>
235
+ * </section>
236
+ */
237
+ .remarque-footnotes {
238
+ margin-top: var(--space-7);
239
+ padding-top: var(--space-3);
240
+ border-top: var(--border-width) var(--border-style) var(--color-border);
241
+ font-family: var(--font-mono);
242
+ font-size: var(--text-meta);
243
+ line-height: var(--leading-meta);
244
+ color: var(--color-fg-muted);
245
+ }
246
+
247
+ .remarque-footnotes h2 {
248
+ font-family: var(--font-mono);
249
+ font-size: var(--text-meta);
250
+ letter-spacing: var(--tracking-caps);
251
+ font-variant-caps: all-small-caps;
252
+ color: var(--color-muted);
253
+ margin: 0 0 var(--space-3);
254
+ }
255
+
256
+ .remarque-footnotes ol {
257
+ padding-inline-start: var(--space-5);
258
+ margin: 0;
259
+ }
260
+
261
+ .remarque-footnotes li + li {
262
+ margin-top: var(--space-2);
263
+ }
264
+
265
+ .remarque-footnotes a {
266
+ color: var(--color-accent);
267
+ text-decoration: none;
268
+ }
269
+
270
+ .remarque-footnotes a:hover {
271
+ color: var(--color-accent-hover);
272
+ text-decoration: underline;
273
+ }
274
+
275
+ /* ─── Sticky TOC rail ────────────────────────────────────────────────────
276
+ * `.remarque-toc-rail` wraps a plain collapsible `<details>` — the SAME
277
+ * markup at every viewport, no JS breakpoint listener. Author it
278
+ * `<details open>` by default: below the rail breakpoint this is an
279
+ * inline collapsible block a reader can close to save space (native
280
+ * disclosure behavior, unchanged); at/above it, it reads as an
281
+ * already-expanded sticky rail. The flagship instead runs a small inline
282
+ * script that force-opens the native `open` attribute on every breakpoint
283
+ * crossing — this module holds to "no JS required," so it authors the
284
+ * open state up front instead of syncing it at runtime. The one
285
+ * degradation this trades away: a reader who manually collapses the TOC
286
+ * below the breakpoint and then widens their window keeps it collapsed
287
+ * until they click it again — a fully static, CSS-only cost worth paying
288
+ * for not requiring a script tag.
289
+ *
290
+ * Marker: reuses the same quiet mono disclosure triangle as
291
+ * `.remarque-prose details summary::before` (prose.css) rather than
292
+ * asking consumers to embed an inline SVG chevron, as the flagship's
293
+ * TableOfContents.astro does.
294
+ */
295
+ .remarque-toc-rail {
296
+ margin-block: var(--space-5);
297
+ }
298
+
299
+ .remarque-toc-rail details {
300
+ border: none;
301
+ padding: 0;
302
+ }
303
+
304
+ .remarque-toc-rail summary {
305
+ cursor: pointer;
306
+ list-style: none;
307
+ display: flex;
308
+ align-items: center;
309
+ justify-content: space-between;
310
+ gap: var(--space-2);
311
+ padding-block: var(--space-2);
312
+ border-bottom: var(--border-width) var(--border-style) var(--color-border);
313
+ /* Mono meta voice — same declarations as .text-label (tokens-core.css),
314
+ repeated rather than shared because .text-label isn't a mixin: true
315
+ small caps, never text-transform: uppercase (REMARQUE.md "Small
316
+ Caps"). The flagship predates that rule and used `text-transform:
317
+ uppercase`; this is the one place the re-expression corrects the
318
+ source rather than just relocating it. */
319
+ font-family: var(--font-mono);
320
+ font-size: var(--text-meta);
321
+ font-variant-caps: all-small-caps;
322
+ letter-spacing: var(--tracking-caps);
323
+ color: var(--color-muted);
324
+ transition: color var(--motion-fast) var(--motion-easing);
325
+ }
326
+
327
+ .remarque-toc-rail summary::-webkit-details-marker {
328
+ display: none;
329
+ }
330
+
331
+ .remarque-toc-rail summary::before {
332
+ content: "\25B8";
333
+ display: inline-block;
334
+ color: var(--color-muted);
335
+ transition: transform var(--motion-fast) var(--motion-easing);
336
+ }
337
+
338
+ .remarque-toc-rail details[open] summary::before {
339
+ transform: rotate(90deg);
340
+ }
341
+
342
+ .remarque-toc-rail summary:hover {
343
+ color: var(--color-fg);
344
+ }
345
+
346
+ .remarque-toc-rail ul {
347
+ list-style: none;
348
+ padding: 0;
349
+ margin: var(--space-3) 0 0;
350
+ }
351
+
352
+ .remarque-toc-rail li + li {
353
+ margin-top: var(--space-1);
354
+ }
355
+
356
+ .remarque-toc-rail a {
357
+ font-family: var(--font-mono);
358
+ font-size: var(--text-meta);
359
+ color: var(--color-fg-muted);
360
+ text-decoration: none;
361
+ transition: color var(--motion-fast) var(--motion-easing);
362
+ }
363
+
364
+ .remarque-toc-rail a:hover {
365
+ color: var(--color-fg);
366
+ }
367
+
368
+ /* ─── Essay grid shell — wide-viewport rail layout ───────────────────────
369
+ * `.remarque-essay` is the opt-in grid container an Essay page's <article>
370
+ * adds to place a sticky TOC rail beside the reading column, mirroring
371
+ * this module's sidenote gutter on the OTHER side (right rail = TOC, left
372
+ * gutter = sidenotes — never the same side, so the two mechanisms never
373
+ * fight over space). Below the breakpoint it is a no-op: every child
374
+ * keeps whatever block-flow centering it already had from `.content-
375
+ * reading` / `.remarque-prose` — this rule only exists inside the
376
+ * `@media` block.
377
+ *
378
+ * Track widths: the center track is `minmax(0, var(--content-reading))` —
379
+ * IDENTICAL to the plain `max-width: content-reading; margin-inline:
380
+ * auto` centering used everywhere else (same visual column, just a grid
381
+ * track instead of block flow), so nothing here changes the reading
382
+ * measure the "prose measure" computed-style check already covers. The
383
+ * right track (`minmax(--space-11, --space-12)`, 10-12rem) is this
384
+ * module's re-expression of the flagship's bespoke `minmax(12rem, 14rem)`
385
+ * rail — same shape (a comfortable range, not a fixed width), core
386
+ * spacing tokens instead of asserted rem literals.
387
+ *
388
+ * Children are placed by exclusion (`:not(.remarque-toc-rail)` -> column
389
+ * 2) rather than an explicit tag list, deliberately: nothing about this
390
+ * rule touches `max-width` or `margin-inline` on any child, so each
391
+ * element's own `.content-reading` / `.remarque-prose` centering keeps
392
+ * working exactly as it does outside a grid — the grid track is already
393
+ * sized to match it.
394
+ */
395
+ @media (min-width: 80rem) {
396
+ .remarque-essay {
397
+ display: grid;
398
+ grid-template-columns:
399
+ minmax(0, 1fr)
400
+ minmax(0, var(--content-reading))
401
+ minmax(var(--space-11), var(--space-12));
402
+ column-gap: var(--space-6);
403
+ }
404
+
405
+ .remarque-essay > :not(.remarque-toc-rail) {
406
+ grid-column: 2;
407
+ }
408
+
409
+ .remarque-essay > .remarque-toc-rail {
410
+ grid-column: 3;
411
+ /* `1 / -1` resolves to row 1 ONLY when the grid has no explicit
412
+ `grid-template-rows` (as here) — `-1` means "the last explicit
413
+ row line," which with zero explicit rows IS row 1. That inflates
414
+ row 1 to the rail's own height instead of spanning the real
415
+ content rows, shoving everything after it down a gap the height
416
+ of the TOC. (Exactly the bug the flagship shipped and fixed,
417
+ 2026-07-20 — captured here so this module doesn't reintroduce
418
+ it.) A generous explicit span covers any Essay's block count;
419
+ empty implicit rows stay 0-height. */
420
+ grid-row: 1 / span 999;
421
+ align-self: start;
422
+ justify-self: start;
423
+ position: sticky;
424
+ top: var(--space-6);
425
+ width: 100%;
426
+ max-height: calc(100vh - var(--space-8));
427
+ overflow-y: auto;
428
+ }
429
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remarque-tokens",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "engines": {
5
5
  "node": ">=18"
6
6
  },
@@ -68,6 +68,8 @@
68
68
  "./prose.css": "./prose.css",
69
69
  "./print": "./print.css",
70
70
  "./print.css": "./print.css",
71
+ "./essay": "./essay.css",
72
+ "./essay.css": "./essay.css",
71
73
  "./agent-rules": "./AGENT_RULES.md",
72
74
  "./spec": "./REMARQUE.md",
73
75
  "./package.json": "./package.json"
@@ -92,6 +94,7 @@
92
94
  "scripts/tokens-json.mjs",
93
95
  "prose.css",
94
96
  "print.css",
97
+ "essay.css",
95
98
  "scripts/lib/css-tokens.mjs"
96
99
  ]
97
100
  }
package/print.css CHANGED
@@ -18,10 +18,11 @@
18
18
  * markup — a site with different class names should add its own
19
19
  * @media print rule for anything this file doesn't catch.
20
20
  *
21
- * Sidenotes: this package does not yet ship a sidenotes module. When
22
- * one lands, its narrow-viewport "inline" mode is exactly the right
23
- * print behaviorprint should never attempt the wide-margin
24
- * side-rail layout a sidenotes module might use on desktop screens.
21
+ * Sidenotes/TOC rail (remarque-tokens/essay.css, issue #52): the "Force
22
+ * the narrow-viewport mode" rule below is this file making good on the
23
+ * promise abovethe essay module's own default (non-`@media`) CSS
24
+ * already IS the narrow-viewport inline mode, so this rule only needs to
25
+ * cancel the `>= 80rem` float, not author a print-specific layout.
25
26
  */
26
27
 
27
28
  @page {
@@ -84,17 +85,43 @@
84
85
  display: none !important;
85
86
  }
86
87
 
88
+ /* ─── Sidenotes: force the narrow-viewport (inline) mode ───
89
+ * essay.css's `>= 80rem` float/negative-margin rule has no viewport to
90
+ * key off of on paper, and some print engines DO evaluate desktop-width
91
+ * `@media (min-width)` queries against the page box. Cancel it
92
+ * explicitly rather than relying on that not happening — a floated
93
+ * margin note next to a 2cm-margin page is exactly the layout the
94
+ * comment at the top of this file promises never to attempt. */
95
+ .remarque-sidenote {
96
+ float: none !important;
97
+ width: auto !important;
98
+ margin-inline-start: 0 !important;
99
+ margin-block: var(--space-3) !important;
100
+ padding-inline-start: var(--space-3) !important;
101
+ padding-inline-end: 0 !important;
102
+ border-inline-start: var(--border-width) var(--border-style) var(--color-border-bold) !important;
103
+ border-inline-end: none !important;
104
+ }
105
+
87
106
  /* ─── Full-width reading column ────────────────────────────
88
107
  * The page itself is already narrow at 2cm margins — don't also
89
- * constrain content to the on-screen reading measure. */
108
+ * constrain content to the on-screen reading measure. Also collapses
109
+ * `.remarque-essay` back to plain block flow — same defensive reasoning
110
+ * as the sidenote float-cancel above, for the grid rather than the
111
+ * float half of essay.css's wide-viewport rule. */
90
112
  .content-reading,
91
113
  .content-standard,
92
114
  .content-wide,
115
+ .remarque-essay,
93
116
  main {
94
117
  max-width: 100% !important;
95
118
  margin-inline: 0 !important;
96
119
  }
97
120
 
121
+ .remarque-essay {
122
+ display: block !important;
123
+ }
124
+
98
125
  body {
99
126
  font-size: 11pt;
100
127
  line-height: 1.5;
package/theme.css CHANGED
@@ -24,6 +24,9 @@
24
24
  * is never overridden (mt-12 stays 3rem; use mt-remarque-9 for 6rem).
25
25
  * Motion utilities: use arbitrary values with the tokens —
26
26
  * duration-[var(--motion-fast)] — so reduced-motion zeroing applies.
27
+ * Pair every duration-[var(--motion-*)] utility with ease-remarque
28
+ * (mapped below from --motion-easing) — without it Tailwind falls back
29
+ * to its default `ease` timing function instead of the token (issue #31).
27
30
  */
28
31
 
29
32
  @theme inline {
@@ -74,4 +77,8 @@
74
77
  /* Radius (rounded-sm / rounded-md — the system's ceiling) */
75
78
  --radius-sm: var(--radius-sm);
76
79
  --radius-md: var(--radius-md);
80
+
81
+ /* Easing (ease-remarque) — pair with duration-[var(--motion-*)] so
82
+ transitions use the token instead of Tailwind's default `ease` */
83
+ --ease-remarque: var(--motion-easing);
77
84
  }
package/tokens.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Remarque design tokens — GENERATED from tokens.json by
3
- * scripts/tokens-json.mjs (v0.13.0). Do not edit — the CSS
3
+ * scripts/tokens-json.mjs (v0.15.0). Do not edit — the CSS
4
4
  * (tokens-core.css + tokens-palette.css) is the source of truth;
5
5
  * tokens.json is the intermediate machine-readable form this file is
6
6
  * generated from. Regenerate with: node scripts/tokens-json.mjs
package/tokens.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$description": "Remarque design tokens — GENERATED from tokens-core.css + tokens-palette.css by scripts/tokens-json.mjs. Do not edit; the CSS is the source of truth.",
3
3
  "$extensions": {
4
4
  "remarque": {
5
- "version": "0.13.0",
5
+ "version": "0.15.0",
6
6
  "tiers": {
7
7
  "core": "immutable identity — overriding forks the system",
8
8
  "palette": "sanctioned personalization surface — override freely, then run remarque-audit"