Specimens
Every token, type step, motif, data pattern and state in the system, in the theme you are looking at. Every ratio on this page was measured by the page, from the computed value of the token beside it, at the moment you loaded it — nothing here is quoted. Flip the theme and every figure re-measures.
Ground and ink
Three grounds. --pitch is the same colour in both
themes, which is the point of it: the dark data panel does not change
when the page does. A token's ratio is quoted against the worst
ground it can legally sit on.
--paperpage ground
--surfacecards, panels
--pitchdark panel · both themes
--inkprimary text
--ink-bodybody copy
--text-helpevery caption, label, tick
--hairline1px dividers · decorative
--text-help is the most consequential token in
the system, because it carries most of the small text in the
product. The tempting value is a 45% ink; it measures 2.98:1 and
fails AA outright. Where you want quieter than body, use it at a
smaller size — never at a lower alpha.
Identity — who
Two hues, a near-black, a dashed rule and a hatch — not a colour per entity. Seven mutually distinguishable hues do not exist in a palette this restrained, and faking it produces a chart that lies to about one reader in twelve. Many entities on one chart are separated by naming, never by adding a third hue.
--who-methe manager reading
--who-enginethe engine's team
--who-rivalother managers — drawn, labelled, not hued
--who-fielda baseline, never a series — always text-labelled
--who-elitea benchmark cohort — a pattern, not a hue
The second hue is violet, and the obvious choice is wrong. A dark blue against this green measures in the 12–21 band — under the floor — because under protanopia and deuteranopia a desaturated dark green and a dark blue both collapse toward the same blue-grey. And the rival neutral is nearly black on purpose: at a mid-grey it measures around 18–22 against the brand green, because a mid-grey and a mid-green share a lightness, and separation has to come from lightness once hue is spent.
Valence — good, bad, level
Position carries valence; hue only reinforces it. This is forced, not preferred. The best red/green pair available measures 18.3 light and 23.8 dark, both under the floor, because every red/green pair collapses under deuteranopia. So a signed number always shows its sign, and a bar always grows from a visible zero line.
--valence-goodgains, positive margins
--valence-badlosses, negative margins
--valence-flatexactly zero — a result, not an absence
| Salah | +4.2 | |
| Haaland | −2.7 | |
| Saka | 0.0 |
State — which kind of absence
Never a valence, never an identity. Three kinds of nothing plus one demand. A blank is drawn as a blank, never a zero — this is the product's honesty made visible, and the three patterns are at three different angles so they are told apart from each other rather than merely from a flat fill.
Recorded, not yet settled. Normal.
Not recorded. Unrecoverable. 135°.
The source would not serve it. 90° — a different failure, and the reader is owed the difference.
Identity, not state — 45°, and the reason the two above are not.
Deadline in 2 hours and no advice recorded. Action required now — full width, because a demand that fits in a cell reads as a status.
Accent, and where it may not go
On --pitch, which is the only ground it may appear on.
It may not appear on a light ground at any size, for any reason. Not as a 10px label, not as a 1px rule. The number is not a close call — it is a quarter of the floor.
Measured against the literal light paper, not against
--paper: that token is near-black in the dark theme,
where the same accent reads 9.48:1 and the rule does not bite.
The constraint is on light grounds, not on a token that holds one
half the time.
--accent-ui is chrome — focus rings, the active tab,
a pressed state, a slider. It resolves to the same hue as
--who-me today and it is still not the same token,
because collapsing them is exactly how one token ends up meaning
"selected", "recommended", "focused" and "captain" at once.
The fixture ramp
Seven steps, the only diverging scale in the product, used only for fixture difficulty and never borrowed. A fixture's kindness is a magnitude with a sign, not an identity — which is why it must never reuse an identity slot. Each step ships a paired ink; lightness is monotonic outward from the midpoint in both arms and both themes, which is what keeps it readable when the hue is not.
| Team | GW6 | GW7 | GW8 | GW9 | GW10 |
|---|---|---|---|---|---|
| Arsenal | BUR (H) | nEW (A) | SHU (H) | mci (A) | BHA (H) |
| Spurs | LUT (H) | liv (A) | FUL (H) | bur (A) | CHE (H) |
Type
Display in Archivo Expanded, body in Archivo, and
every label, stat, verdict, timestamp and button in JetBrains
Mono — which is the single decision that makes the whole
thing read as an instrument rather than a blog. Archivo and Archivo
Expanded are one file: the same variable font at
font-stretch: 125%.
Two display ceilings, deliberately. The public site goes to 64px, where a page has room and one headline is the whole screen. Inside the app the ceiling is 32px, used once per screen — a 38px name is unusable at fifteen rows and eight columns, which is what the app actually renders.
1,118,402
91,077
743
11
1,118,402
91,077
743
11
Jargon is never the label. "Expected points over the
next six gameweeks", not pred_horizon. The short form
can live in a tooltip; it may not be the thing on screen.
Space and density
Spacing is a scale and a token, never a hand-written
margin. A stylesheet with eleven distinct margin values has
no system. Four steps carry the app, four more carry the public site,
and layout does the spacing — flex or grid with
gap, never per-element margins that collapse or double.
Every app screen has a budget: one viewport-height in its
default state, on desktop and at 375px. Everything beyond
goes behind a segment, a <details> or a counted
disclosure. The measured defect today is length — the main decision
screen runs 7.9 viewport-heights on a phone for a job that is one
transfer and one captain pick.
Geometry and motif
Square corners everywhere. No border radius, no shadows — depth comes from ground changes and hairlines. The football is in the materials, never in the illustration: no player photography, no crowd shots, no stadium hero, no whistle icons, no cartoon boots. At most one texture per screen, and never behind a table or a chart.
Pointy-top, height = width × 1.1547 or it stops
being regular. Never put a border on a clipped
hexagon — clip-path cuts it on every
diagonal and it renders as two vertical bars.
Three tapered blades and a hexagon replacing a period — large, tiny, medium, left to right, hexagon overlapping their roots. Once per viewport at most, and only at 26px and above; below that it is noise.
From a ball's panels. For light sections that are mostly type, and never behind data. You should notice it on the second look, not the first — if it competes with this sentence it is too strong.
Two out-of-phase layers, squares kept square and orthogonal.
A 45° version reads as a fence, not a net. The sway
runs at 16s and 22s — the only continuous animation in the
product, long enough that it is never noticed directly,
and it stops dead under prefers-reduced-motion.
A 3px unevenly-stopped rule, shown here on both grounds. The uneven alpha is the point — worn paint, not a border. A clean 3px line is just a thick border and reads as a mistake.
§5 describes it as a white gradient, which is right on the
dark panel and invisible on paper — the public site's footer rule
vanished completely. So the token carries only the wear and the
element inks it through a mask from
currentColor. Same worn paint, either ground.
The full-bleed break on the public site, and the answer to "where does the hero photograph go" — nowhere. A dark ground, repeating-gradient stripes and an inset touchline rectangle. Convincing at any width, costs a few lines, and has no licence attached to it.
Data display
Mono headers at 10px. Rows 32px dense. Hairline between rows, none within. No zebra striping — it is noise at this density and the hairline already does the work. Numerals tabular and right-aligned, text left. A sortable header says so.
| Player | Next 6 | Price | Owned | Last |
|---|---|---|---|---|
| M. Salah | 34.1 | £12.9 | 48.2% | 13 |
| E. Haaland | 31.8 | £14.1 | 61.7% | 9 |
| B. Saka | ~27.4 | £10.0 | 34.9% | 6 |
| C. Palmer | ~24.9 | £10.7 | 29.1% | — |
FPL stated it, it cannot change. Plain.
FPL stated it, it can still change. Dotted underline, muted.
This app computed it. Always ~-prefixed, and never at the same weight as a confirmed one.
Em dash. Never 0.
One y-axis, ever — two measures of different scale are two charts on a shared x. Drawn at measured pixel width so type never scales with the container. Paths break at missing values so a gap is a gap, never an interpolation. One crosshair reading out every series, not per-point tooltips. Reference lines are dashed and always carry a text label.
| Manager | Salah | Haaland | Saka | Palmer | Isak |
|---|---|---|---|---|---|
| You | ● | Ⓒ | ● | · | ○ |
| Engine | Ⓒ | ● | ● | ● | · |
| Fieldwork FC | ● | ● | · | ○ | ● |
● present · ○ secondary ·
Ⓒ marked · · absent.
It spends nothing from the hue budget and survives every
dichromacy absolutely, which a five-by-five grid of tinted
cells does not.
| Player | Form | Best | Worst | Mean |
|---|---|---|---|---|
| M. Salah | 13 | 2 | 6.4 | |
| E. Haaland | 16 | 1 | 5.1 |
Prefer three columns of best / worst / mean to the sparkline where the exact numbers matter. The sparkline shows a shape; the columns answer the question.
Expected points over the next six gameweeks 34.1
| Minutes expectation | +18.4 |
| Attacking returns | +12.7 |
| Fixture adjustment | +4.1 |
| Rotation risk | −1.1 |
| Total | 34.1 |
A breakdown that approximately sums is worse than
none. This is the product's core promise made literal, and
it is also the best onboarding available — nobody reads a
methodology page, but everyone opens a row. A row that expands on
click must expand on Enter, which <details> gives
for free and a div with an onClick does not.
6 others considered
A section labelled 6 others is one the reader can skip without opening; a section labelled 0 needs no thought at all.
Motion
Almost none, and all of it ambient. The netting sway above is the
only continuous animation. Buttons darken on hover by roughly
calc(l − .06); secondary fills to ink-6.
No lift, no shadow, no transform.
Data never animates on load — a chart that draws
itself is a chart you cannot read for 400ms, and this product is read
in a hurry on a Friday evening.
prefers-reduced-motion: reduce is honoured by pausing
the netting and removing every transition — not by shortening them. A
setting that leaves a 16s loop running has been acknowledged, not
honoured.
Accessibility, as a floor
AA everywhere: 4.5:1 for text, 3:1 for graphical elements and UI boundaries. Nothing carries meaning in colour alone — every state also has a word, a glyph or a position, which is why the three absences above are three angles of hatch and not three tints. Everything interactive is keyboard-operable with a visible focus ring (tab through this page), and a row that expands on click expands on Enter.
Both themes are designed, not inverted. The full
light palette is on bare :root; only what changes is
redefined under prefers-color-scheme: dark and again
under [data-theme], so the control at the top of this
page beats the OS in both directions. Verify with
uv run python scripts/check_palette.py, which re-measures
every figure in tokens.css and fails if a comment and a
value have drifted apart.