/ / design
Web Quality Index design system.
A clinical, restrained, monochrome reference for a public-utility data product. Derived from Tranco, Internet.nl, and Have I Been Pwned. The page you are on is the manual: every token enumerated, every primitive shown in use.
Built on tokens.css + typography.css + global.css.
Color tokens
Single source of truth. Anything that needs a color reads from here. Hex values shown for review only — never hard-code them.
Surfaces
Foreground
Rules
Live accent
The only non-monochrome color outside the verdict scale. Reserved for live-data signals: the pulsing dot in the method banner, the "live" badge, and selection highlights.
Verdict tones
Three semantic tones, each as a fg + bg pair. Desaturated on purpose — the data has to feel measured, not alarming. Used by GradeCell, GradePill, VerdictPill, score-heat cells, and DeltaCell.
Typography
Two variable fonts. Newsreader (display serif) carries h1 and the homepage hero. Inter Variable carries body and h2/h3 sans. System monospace stack carries every numeric, table header, and metadata pill.
The web is critical infrastructure.
Same rules for every site. Nobody — including us — can move a score without changing the methodology in public, with a version bump and a logged reason.
WQI v0.5.0 · 86 live / 86 total factors · scanned 2026-04-27
Type scale
- The quick brown fox
- The quick brown fox
- The quick brown fox
- The quick brown fox
- The quick brown fox
- The quick brown fox
- The quick brown fox
- The quick brown fox
Grade primitives
Four small Astro components live in src/components/primitives/. Each renders a single CSS-class pattern with consistent props. Use these — don't reach for the raw .gcell or .verdict-pill classes from page templates.
GradeCell — 28×28 (md) or 24×24 (sm) heatmap tile
<GradeCell grade={letter} />
<GradeCell grade={letter} size="sm" />
<GradeCell grade={letter} transition:name={`vendor-grade-${slug}`} /> GradePill — inline tinted pill, used inside .identity-strip
A · B · C · D · F · —
<GradePill grade={letter} transition:name={`vendor-grade-${slug}`} /> VerdictPill — uppercase tag for verdict columns + counts
42 pass 7 warn 3 fail 1 n/a · pass warn fail na
<VerdictPill verdict="pass">{count} pass</VerdictPill>
<VerdictPill verdict="fail" /> {/* falls back to literal "fail" label */} DeltaCell — signed-percentage cell, color reflects sign
| Case | Δ |
|---|---|
| positive, positive-is-bad (default) | +12.3% |
| positive, positive-is-good | +12.3% |
| negative, positive-is-bad (default) | -4.0% |
| negative, positive-is-good (goodWhenNegative) | -4.0% |
| zero | 0% |
| null / unmeasured | — |
<DeltaCell value={f.delta_pct} /> {/* default: + is bad */}
<DeltaCell value={f.delta_pct} goodWhenNegative /> {/* flip the polarity */} Data table
The .dt-data class is the dense scorecard table used on /vendors and /vendor/:slug. Sticky header, hairline row dividers, monospace numerics, 1px accent slide-in on row hover (look at the left edge as your cursor enters a row).
| # | Grade | Score | Agency | Scored | Portfolio |
|---|---|---|---|---|---|
| 1 | A | 94.2 | Internet.nl Reference internet.nl | 312 | 312 |
| 2 | B | 81.7 | Tranco Top-Tier Hosts tranco-list.eu | 248 | 260 |
| 3 | C | 72.0 | Have I Been Pwned haveibeenpwned.com | 1 | 1 |
| 4 | D | 64.5 | Sample Mid-Pack Agency midpack.example | 18 | 32 |
| 5 | F | 41.2 | Sample Low-Score Vendor lowscore.example | 4 | 12 |
Column helper classes
- .num — right-align, mono, tabular-nums, no-wrap. For every numeric cell.
- .t — mono, smaller, fg-mute. For metadata strings (timestamps, ranks, IDs).
- .col-rank / .col-portfolio / .col-coverage / .col-sites / .col-cohort / .col-source — drop on mobile via media query in global.css.
- .col-evidence — drops on mobile (factors table evidence column).
- .score-heat-{a..f} — full-cell tinted background scaled to grade. Use on the score column so the eye sees the heatmap pattern when scanning down.
- .agency-cell — bold name + dim mono domain inline, HN-style.
- .row-link — cursor pointer; pair with a data-href attribute and a tiny click handler so the whole row navigates.
Identity strip
Single-line metadata pill at the top of every data page. Replaces the old hero gauge / score block. The grade pill is the lede; the rest is whatever metadata the page has — composite, verdict counts, scan timestamp, attribution, etc. Wraps freely on narrow viewports.
example.com · B 82/100 composite · 78/86 factors scored · 62 pass 12 warn 4 fail · method v0.5.0 · scanned 2026-04-27
<p class="identity-strip">
<GradePill grade={letter} />
<span><strong>{score}</strong>/100 composite</span>
<span>·</span>
<VerdictPill verdict="pass">{n} pass</VerdictPill>
...
</p> Facts grid
Compact 4-column label/value grid. Two pairs per row at desktop, stacks to 2-column on mobile. Used on /d/:domain for site-facts. Don't use it for prose — it's deliberately hairline-dense.
<div class="facts-grid"> <div class="fact-k">Hosting</div> <div class="fact-v">Cloudflare (US)</div> ... </div>
Spacing scale
4px base. Every margin and padding pulls from this — vertical rhythm only holds if you don't reach for arbitrary 0.7rem values. If you find yourself wanting one, the answer is almost always "round to the nearest token."
- --s-1 4px
- --s-2 8px
- --s-3 12px
- --s-4 16px
- --s-5 20px
- --s-6 24px
- --s-8 32px
- --s-10 40px
- --s-12 48px
- --s-16 64px
Motion
All transitions consume the duration + easing tokens. Reduced-motion users get instant changes via the media-query override in tokens.css.
| Token | Value | When to use |
|---|---|---|
| --dur-fast | 120ms | hover/focus state changes, table row tint |
| --dur-medium | 220ms | view-transition morphs (default) |
| --dur-slow | 400ms | rare — only when distance traveled is large |
| --ease-out | cubic-bezier(0.16, 1, 0.3, 1) | outbound motion (entering, expanding) |
| --ease-std | cubic-bezier(0.4, 0, 0.2, 1) | all-other transitions (recolor, hover) |
What NOT to do
- No card-grid for comparable data. If the rows have the same shape, render a table. Cards make scanning impossible above five rows.
- No gradients on UI chrome. Grade tile backgrounds and report-card heads are the only places a tinted gradient appears. Buttons, headers, dividers stay flat.
- No decorative emoji. The brand is public-utility. Emoji read as marketing-deck.
- No color-only encoding. Every color signal carries a redundant glyph or letter (A on green, F on red, "+12.3%" on red). Colorblind users get the same message.
- No hero gauges. The identity strip replaced the giant circular meter. Don't bring it back.
- No drop shadows. Elevation is communicated by hairline rules and panel backgrounds, not by light. Tokens.css doesn't even define a shadow.
- No personality voice. No "Hey there!", no "We're so excited to..." The data speaks. See about + brand.md.