Plate № 20 · Typography · report masthead
A pattern from the gf.cx specimen book
Report header · quiet by default
A report's masthead should sound like the document beneath it. The dare.co.uk report header drifted away from its own body — louder, more designed, in a different voice. This is the pattern that brings it back, developed live before it graduates.
"The body of the report seems solid — whereas the header seems playful, overly complicated… I think it's the italic headline, oversized, that's doing the drift work."
The problem
Reports are rendered by two generators (seo_render_html.py for the markdown corpus, dare_cf_analytics.py for the analytics dashboard), each carrying its own hand-rolled header CSS. Neither referenced the shared page-header primitive — the canonical breadcrumb + kicker + h1 + stamp typography the rest of the portfolio uses. So the header drifted while the body stayed solid.
The specific tell: a serif italic h1 scaling up to ~54px sat above a body whose own headings are restrained Atkinson sans. The title spoke a different language from the document — that mismatch reads as "playful, overcomplicated." The body was never the problem; the masthead was.
The shape
- Adopt the primitive, don't bespoke. Import
page-header.cssand emit its canonical.nav · .kicker · h1 · .stampmarkup. This deletes header CSS from the generator rather than adding it — the drift surface goes away. - Match the body's voice. A
.sans-titlevariant on the primitive switches the h1 to the body's Atkinson sans, so the header stops looking designed-separately. Named + shared, not a per-report override. - One quiet fix in passing. The breadcrumb's first crumb read
pa.gf.cxbut linked tohome.gf— corrected tohome.gf.
The specimens, in-page
Same content, same primitive, three h1 treatments. These are real iframes loading assets.gf.cx/page-header/page-header.css + the gf.cx palette; only the headline rule differs. Pick the voice.
Specimen A · Sans — matches the body voice
Atkinson sans, ~28px, weight 600 — the same family and weight as the report's h2 headings. The header now speaks the body's language; the most de-drifted of the three.
Performance trend — gf.cx
90-day Cloudflare analytics — requests, cache health, status codes, and the week's notable movements.
dare_perf_trend_gf.cx.md · Rendered 2026-06-25 14:30 UTCSpecimen B · Upright serif — editorial but quiet · ✓ chosen + shipped 2026-06-25
Newsreader serif but upright, ~30px, weight 500. Keeps a document feel for the title without the playful italic. A middle path between the body's sans and the old oversized italic.
Performance trend — gf.cx
90-day Cloudflare analytics — requests, cache health, status codes, and the week's notable movements.
dare_perf_trend_gf.cx.md · Rendered 2026-06-25 14:30 UTCSpecimen C · Primitive italic — canonical, the trait flagged
The shared primitive exactly as it ships today: Newsreader serif italic, 38px — already smaller than the ~54px the report ran. Matches status.gf.cx. Shown for honest comparison; it keeps the italic Dan called out.
Performance trend — gf.cx
90-day Cloudflare analytics — requests, cache health, status codes, and the week's notable movements.
dare_perf_trend_gf.cx.md · Rendered 2026-06-25 14:30 UTCThe category chip
The reports colour-code by category. That chip — colour variants (and optional icons) — graduates into the shared primitive as page-header.css v0.2.0, so every surface can use it without re-forking. The report wears the quiet, text-only form:
The graduation path
Shipped 2026-06-25 — Specimen B (.upright-title) graduated in three moves; nowhere else holds a copy:
- Asset. The
.upright-titlevariant + the.kicker--health/signal/ideascolour variants shipped inassets.gf.cx/page-header/page-header.cssv0.2.0. - Generator.
seo_render_html.pynow imports the primitive, lifts the<h1>into a canonical<header class="upright-title">, emits.nav · .kicker · h1 · .stamp, and the bespoke header CSS is gone. - Backfill. The full corpus (461 reports) was force-re-rendered through
dare_dev_reports_publish.pyon the Mac and redeployed to dev.dare.co.uk — not the dashboard-refresh GHA, which builds the analytics dashboard, a separate output.
The report header is the primitive now — no second source to drift from.
Shipped 2026-07-14 — a third title voice, .roman-title, joined the sibling family on the primitive: masthead-scale upright serif (slant only removed, 38px / weight 400 kept), so an italic <em> accent word inside the h1 distinguishes itself by both slant and colour. First adopter growth.gf.cx/reading — h1 "A product-designer reading list, decoded."
When it breaks
- Re-bespoking the header in the generator. The whole point is to defer to the shared asset. A new local
.rpt-headerrule is the drift returning. - A title louder than its body. The header should sound like the document. If the report body is sans and restrained, an oversized italic serif masthead will always read as a different design.
- Forking the chip. Colour variants belong in the asset so they're shared. A per-report copy is the favicon-drift problem in a new place.
Sibling of the built-with provenance footer and the back-to-top jump icon — same single-source, control-plane discipline, applied to the top of the page instead of the bottom.
Where it applies
- Use when a report generator produces multiple documents from a shared template — the masthead must derive from the shared primitive, not local CSS baked into the generator.
- Use when you detect drift: a bespoke header style that no longer matches the body voice (serif h1 vs. sans body, oversized vs. restrained, or diverged colour palette).
- Use when a new report surface ships — wire
page-header.cssfirst, before the first commit, so there is never a drift period. - Skip when the report is one-off, print-targeted, or heavily branded with its own design system — the primitive targets canonical web reports in the gf.cx palette.
- Skip when the report already imports the primitive correctly and the header is already voiced to match the body.
Reusable elements
assets.gf.cx/page-header/page-header.css— one link tag; supplies the full breadcrumb + kicker + h1 + stamp typography system..upright-titleon<header>— Newsreader serif, upright, weight 500. Specimen B; shipped corpus-wide 2026-06-25..sans-titleon<header>— Atkinson sans, weight 600. Specimen A; matches the report body voice most closely..roman-titleon<header>— Newsreader serif, upright, at full masthead scale (38px / weight 400 kept, slant only removed). Unlike.upright-titleit does not shrink or restrain; the point is to let an italic<em>accent word inside the h1 pop by both slant and colour rather than colour alone. Shipped 2026-07-14; first adopter growth.gf.cx/reading..kicker--health/.kicker--signal/.kicker--ideas— category colour chips: deep green / deep navy / warm brown. Added inpage-header.cssv0.2.0.nav.nav— breadcrumb row; first crumb links to the home surface, last crumb is the current section slug.div.stamp— render-time and source-file provenance line in small-caps mono, shown below the lede.
Reference
- Source
- Dan, 2026-06-25 · emerged from a live drift audit of the dare.co.uk report masthead.
- In use / example
- devreports.dare.co.uk — 461-report corpus rendered via
seo_render_html.py, using the.upright-titlevariant shipped 2026-06-25; also at dash.gf.cx/reports/. Copy the header snippet fromseo_render_html.pyas the starting template. - Reusable elements
page-header.css·.upright-title/.sans-title/.roman-titleh1 voice variants ·.kicker--health/signal/ideascolour chips ·nav.navbreadcrumb ·div.stamprender line.- Origin
- dare.co.uk report corpus, 2026-06-25 — Specimen B graduated corpus-wide the same session;
page-header.cssv0.2.0 added the voice variants and colour chips.