design system · css · published here

The Rule

A rule is three things at once: the instrument you measure with, the line a printer sets between columns, and the principle you are held to. One foundation, two faces. This page is set in it.

what it is

One foundation, two faces

The foundation is shared so the properties read as one hand; the faces are inverted so they are never mistaken for each other. Workshop — this site — is a cold accent on a warm ground: a tool laid on paper. Observatory — caelum.agency — is a warm signal on a cold ground: an instrument lit at night. Neither is another dark starfield, because that is The Hidden Door's and it should stay theirs.

groundWorkshop #EDEFEC cool paper · Observatory #0D1218 blue-black
accentWorkshop #26685F verdigris · Observatory #E0A458 instrument amber
secondWorkshop #964E29 oxide · Observatory #6FA8C7 cold blue — each face's second colour is the other's temperature
displayInstrument Serif
bodyNewsreader, optical sizing on
dataSpline Sans Mono, tabular numerals wherever a figure appears
structurehairline rules, no rounded corners, no shadows, no cards
motionone 8px rise on entry; none if you asked for reduced motion
how to use it

Three files, in order

<link rel="stylesheet" href="https://caelum.codes/rule/tokens.css">
<link rel="stylesheet" href="https://caelum.codes/rule/workshop.css">   <!-- or observatory.css -->
<link rel="stylesheet" href="https://caelum.codes/rule/base.css">
  • tokens.css — type, scale, measure, structure, and the colour slots. The reasoning is in the comments.
  • workshop.css — the light face. Fills every slot.
  • observatory.css — the dark face. Fills every slot.
  • base.css — the dozen components: wrap, headings, label, links, masthead, nav, index, facts, code, aside, section, colophon. Reads slots only.

Swapping a face is swapping one line. Nothing in base.css knows which face it is wearing.

what breaks

The numbers were wrong once already

The first palette for this face shipped with an accent at 4.23:1 on the ground and a label grey at 2.76:1 — both under the 4.5 floor for body text, both written down as passing. The fix is procedural, not cosmetic: the repo carries a contrast gate, tests/contrast.py, that fails the build when any text pair is under 4.8 (the house margin over the 4.5 floor, because a value that grazes the line drifts under it through rounding or a colour profile). It checks its own instrument first — black on white must return exactly 21:1 — before it trusts any other reading.

Hairline rules are decorative and exempt from WCAG 1.4.11; they are tuned for visibility, not compliance, and the gate reports them as information only.

Light on the Workshop is an open question. Everything else in the estate is dark. It is the strongest differentiator the face has and the biggest departure from the house, and it is token-driven so that ruling costs one file.