Specimen · ouroboros-design
Design System
Every value on this page is a live token. Nothing here is a screenshot or a hand-copied hex. Switch the theme and the page restates itself, because it is built out of the thing it documents.
Type
--fs-1 … --fs-11Eleven steps, ascending. Steps 1 to 6 are the text range and
hold fixed rem values. Steps 7 to 11 are the display range and are fluid: each
one is a clamp() with a viewport term, so a headline shrinks on a
phone without a media query.
Institutional betrayal
Institutional betrayal
Institutional betrayal
Institutional betrayal
Institutional betrayal
Institutional betrayal
A record that holds
Ouroboros
The text range is not the modular scale.
--fs-1 through --fs-6 run 0.8, 0.9, 1, 1.2, 1.4, 1.6rem.
That is a hand-tuned ramp, not 1.25 compounded, and pretending otherwise
would be a nicer story than a true one.
The ratio steps live separately as --f-1 through
--f-5, unitless, for anything that genuinely needs to compute
off the scale. The masthead spine above is one such thing. Its tile widths
are the ratios, so the spine measures itself.
Leading
--lh-1 … --lh-4Leading runs backwards against size. Small text needs air
between lines to stay trackable. A 64px headline at 1.75 reads as two
unrelated lines instead of one sentence, so display type takes
--lh-1 and body copy takes --lh-3.
Survivors of institutional betrayal rarely arrive needing to be convinced that something went wrong. They arrive needing a record.
They arrive needing a record that holds up later, when the people who caused the harm are the ones writing the summary.
A record that holds up later.
A record that holds
Spacing
--space-1 … --space-13Thirteen steps, derived from an audit of the 719 spacing literals that preceded them rather than from a generic 4px grid. The ten values the system already leaned on are all present at their exact former size, which is why most of the migration moved nothing at all.
Two values sit outside the ramp on purpose.
--space-card is 1.4rem and --space-band-lg is 3.5rem.
Both are component metrics, and snapping either one would have moved real
chrome to satisfy a number.
Sixteen more call sites were left as raw literals. Nine are sub-pixel chip pads at 0.05 to 0.15rem, where the nearest step is a 67% change. Seven are outliers between 4.5 and 10rem. The migration rule was no more than 0.25rem and no more than 25% of movement, and those sixteen failed it. Leaving them visible is more honest than resizing them quietly.
Surface
--surface-1 … --surface-5Ascending means further forward, in both themes. On a dark ground a drop shadow has almost nothing left to darken, so a raised panel reads as raised because it is brighter than what it sits on. The light ramp keeps the same order rather than inverting it, so hover-to-brighter still reads as lift and not as sink.
#0c101a · #E7EAF0
#1a1d26 · #EFF1F5
#22262f · #F5F7FA
#252831 · #FCFDFE
#2b2f39 · #FFFFFF
Ink
--ink-1 … --ink-4Four steps, ascending by emphasis. Brightness accelerates as it climbs: the gap from meta to body is small, and the gap from body to display is large. Faint text sits far from its neighbours so it never competes; display text crowds toward the extreme so it always wins.
#999999 · #535971
#B0AAA0 · #3D4353
#E8E4DC · #34394A
#FFFFFF · #1C2029
Contrast
WCAG 2.1, every pairingMeasured, not assumed. Every ink and every accent against
every surface, computed with the sRGB relative-luminance formula. The same
numbers run as a build gate: npm run check:contrast reads the
compiled CSS and fails the build if any token drops below its declared floor.
| on → | surface-1 | surface-2 | surface-3 | surface-4 | surface-5 |
|---|---|---|---|---|---|
| ink-1 | 6.67 | 5.91 | 5.32 | 5.17 | 4.70 |
| ink-2 | 8.24 | 7.30 | 6.57 | 6.38 | 5.80 |
| ink-3 | 14.99 | 13.27 | 11.95 | 11.61 | 10.56 |
| ink-4 | 19.01 | 16.83 | 15.15 | 14.72 | 13.39 |
| mark | 7.39 | 6.54 | 5.89 | 5.72 | 5.20 |
| sage | 6.43 | 5.69 | 5.12 | 4.98 | 4.53 |
| teal | 6.38 | 5.65 | 5.08 | 4.94 | 4.49 ✗ |
| amethyst | 6.06 | 5.36 | 4.83 | 4.69 | 4.27 ✗ |
| steel | 6.06 | 5.36 | 4.83 | 4.69 | 4.26 ✗ |
The one place the system runs out of room.
--steel is the binding token. Holding it at 4.5:1 caps the
surface luminance at 0.02432, and --surface-4 already sits at
0.02133. That leaves about one perceptual step, and
--surface-5 spends it.
So --surface-5 carries ink and never accent text. The
alternative was lightening steel, amethyst and teal, which is a brand
change rather than a CSS change. The restriction is written into the token
source, the contrast script and the README, so the next person to reach for
it finds the reason before they find the bug.
Accent
categorical, not a rampFive accents, each bound to a meaning. Gold is the house colour and the only accent allowed to decorate. The other four carry category: announcement type, dashboard audience, service family. Call-site counts are in the captions, and the distribution is the point.
#b39f7b · #7A6028
#A284CA · #6B4F94
#6DA187 · #38624C
#5DA19C · #295F5A
#7F94A6 · #4A5A68
--accent-1 through --accent-5 exist, and you almost certainly want the semantic name. They are aliases, added so the numbered vocabulary is complete. They are not a lightness ramp and the numbers imply an interchangeability this palette does not have.
Reach for --accent-N only when you genuinely mean the nth
swatch in the palette, which in practice means a page like this one.
Everywhere else, name the meaning.
Elevation
--elev-0 … --elev-5Shadow carries elevation and lightness carries it back.
--shadow is itself a theme token, so every level re-resolves per
palette: a near-black at low alpha on dark, a cool grey at 18% on light. Pair
each level with the matching surface step and the two channels agree.
none
0 1px 3px
0 2px 8px
0 2px 12px
0 4px 16px
0 24px 60px
Radius
px, not remA corner radius is a fixed optical detail, not rhythm, so it does not scale with the root font the way spacing does. The low end is spaced one pixel at a time because 2px, 3px and 4px account for 54 of the 65 radius call sites in the system.
2px
3px
4px
8px
20px
50%
Line
--line-1 … --line-4Four hairlines, ascending by assertiveness. They are opaque
steps rather than white at low alpha, so a border keeps the same value
whatever surface it lands on. In dark theme --line-1 is the same
value as --surface-3, which is what makes it read as a seam
rather than a frame.
#22262f · #E1E4EC
#2b3038 · #D4D8E2
#30343e · #C7CBD6
#3d424e · #B3B8C6
Measure
--measure: 68chSet in ch rather than rem, so the column tracks
the font actually rendering rather than a guess about it. Sixty-eight
characters is roughly the span the eye tracks before it starts losing the
beginning of the next line.
This column is exactly one measure wide. Drag the window narrower and it shrinks with you. Drag it wider and it refuses to follow. A text column that grows to fill a 27-inch display is not generous, it is unreadable, and no amount of leading rescues it. That matters more here than on most sites: a good share of the people reading this are doing it at 200% zoom, on a phone, or under stress, and often all three.