Strata

Style guide

Archival print. The tokens, what they measure, and where the two modes deliberately disagree.

Warm paper rather than white, ink-brown rather than black, and a single seal-red accent used sparingly enough that it still carries meaning when it appears. Rules provide structure. Section labels name what a region is. Hashes are set as artifacts, because that is what they are.

This page renders in whichever mode your system asks for. Switch your OS appearance and everything below re-measures.


Icons

Phosphor Light, MIT licensed, drawn at a weight that sits with Fraunces and Newsreader rather than against them. Inline SVG, never an icon font — a font maps glyphs onto Private Use Area codepoints, which some screen readers announce as garbage and which vanish entirely for anyone overriding fonts for legibility.

The names are our meanings, not the library’s: manuscript, not diamond. Changing library is one file, and no call site moves. Every icon in the app is aria-hidden and sits beside a real text label; an icon that ever appears alone needs a label of its own.

select check-square
folder folder-open
align-left text-align-left
align-center text-align-center
align-right text-align-right
align-justify text-align-justify
manuscript diamond
material cards-three
checkpoint seal-check
heading text-h
heading-one text-h-one
heading-two text-h-two
heading-three text-h-three
paragraph paragraph
outline list-dashes
notes note
comment chat-teardrop-text
bullet-list list-bullets
numbered-list list-numbers
rename pencil-simple
remove x
cut scissors
copy copy
paste clipboard-text
bold text-b
italic text-italic
typeface text-aa
image image
page-break arrow-line-down
find magnifying-glass
view eye
verify check-circle
compare git-diff
reorder arrows-down-up
jump arrow-elbow-down-right
split arrows-out-simple
question question
history clock-counter-clockwise
typography text-aa
accessibility person-arms-spread
collapse caret-right
expand caret-left
library books
add-files folder-plus
new-tab plus
save floppy-disk
close x
next caret-down
previous caret-up
replace arrows-clockwise

Palette

--seal and --brand start at the same value and are not the same thing. --seal is set in type — labels, rules, the current nav item — so it must clear 4.5:1 against the paper. --brand is the logotype: the band in the icon, the dot after the wordmark. A logotype is exempt from contrast minimums, so it is free to be whatever the mark wants. Conflated, they fight — tune the brand and label text silently loses contrast.

Measured from the stylesheet as this page loads, so it cannot drift from what the site actually uses. The values follow the theme you are reading in — this palette has a light and a dark set, and the ratios below are against the paper of whichever one you have.

Defaults must pass before any setting exists. A preferences pane is for going further than the baseline, never for repairing one. Three tokens originally failed here — supplementary text at 3.38:1, the pending state at 4.18:1, and functional borders at 1.39:1 — and none of it was visible to anyone who could read comfortably.

Two border tokens, on purpose

--rule is ornament: hairlines between panes, under table rows. It sits below 3:1 deliberately, because WCAG 1.4.11 governs components required to understand content, not decoration. --edge is functional — buttons, inputs, active states — and meets 3:1. Collapsing them into one token either breaks compliance or flattens the design.

Light and dark disagree deliberately

Paper Ink on warm stock. Contrast can run high — 15.6:1 for body text — because dark marks on a light field do not bloom.
Ink Light on dark halates: strokes appear to spread, and thin serifs suffer most. So dark mode runs slightly softer, never pure white on pure black.

What changes, and why

PropertyLightDark
Body contrast15.6:111.0:1 — one step softer by default
Text colournear-black brownwarm bone, never #fff
Accentsaturated seal redlighter, desaturated — saturated reds vibrate on dark
Elevationborderslighter surfaces (--paper-deep rises)
Grainreads as paperreads as sensor noise — candidate for reduction
The reason this is not a preference

Dark mode is an accessibility requirement for some people and an accessibility problem for others. Light sensitivity — migraine, concussion, some autistic sensory profiles — can make a bright field genuinely painful. Astigmatism makes light-on-dark blur, because halation smears strokes that were already smearing.

So neither can be the assumed default, both have to be good, and the real answer is that mode belongs in the settings pane beside the contrast controls rather than behind a single toggle.

The evidence is not symmetric, though. Positive polarity measurably wins for reading — smaller pupil size, better proofreading, and reading-speed penalties reported as high as 26% for light-on-dark. Astigmatism affects 30–50% of adults, and a dark field dilates the pupil in exactly the way that exposes corneal aberration. On OLED phones there is a second mechanism entirely: dark mode encourages lower brightness, and PWM dimming near 240Hz worsens below roughly a quarter brightness.

Hence dark mode here runs one contrast step softer by default. Maximum contrast is the halation condition, so the highest ratio was the wrong place to start.

Scale

h1 — Fraunces 500, opsz 144, tight Pale Fire
h2 — Fraunces 500, opsz 60 A record of composition
body — Newsreader, 1.06rem/1.68 She had never been this far south…and she already hated it.
section label — 0.66rem, 0.11em tracking, seal Outline
mono — IBM Plex Mono, hashes and commands 0450d4dfcf8916ef34088def552f4974

Section labels

Small-caps, letterspaced, seal-red, sitting above a region to say what it is. They carry the print language into application chrome and they are the pattern to reuse — a second heading treatment for panes would dilute both.

Motion

One quiet staggered reveal on load, then stillness. Printed things do not fidget, and a writing surface that animates is a writing surface that interrupts. prefers-reduced-motion is honoured, and the settings pane will let it be forced independently of the OS.

Focus

A 2px seal outline with 2px offset on every interactive element. This was entirely absent in the first build — keyboard-only users had no indication of position anywhere in the interface, which no amount of visual polish compensates for. The editing surface is the one exception: there the caret is the indicator.