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 +3 -0
- package/CHANGELOG.md +63 -0
- package/README.md +35 -0
- package/REMARQUE.md +164 -8
- package/essay.css +429 -0
- package/package.json +4 -1
- package/print.css +32 -5
- package/theme.css +7 -0
- package/tokens.d.ts +1 -1
- package/tokens.json +1 -1
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 |
|
|
377
|
-
| Large text on background | 3:1 minimum |
|
|
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">↩</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">↩</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.
|
|
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
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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 above — the 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.
|
|
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.
|
|
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"
|