Plate № 26 · UX primitives · status copy
A pattern from the gf.cx specimen book
Plain-language faces · human copy for a machine's status cards
"Site-health needs user-friendly written content — the cards are jargon-heavy machine status lines."
Why it's human-first
A monitoring job emits status the way a machine thinks: crawled_not_indexed=5, DOMAIN RECONCILIATION (APPROVED · MONITORED · ON-THE-LIST), pa evidence tripwire YELLOW: 1 issue(s) in 18 sampled keys … PIL/Pillow not importable. That's faithful and, to anyone who isn't the person who wrote the check, nearly unreadable. To answer the only question a reader has at a glance — is this fine, or does it want me? — they first have to decode the field names.
The page can do that decoding for them. A face is the humane read of a status card: a plain title ("Search indexing", "Domain reconciliation", "Off-site backup") and one calm sentence chosen by the verdict — green says the reassuring thing, red says the actionable thing. The raw machine summary doesn't vanish; it stays one click away behind the card's detail-report link. Progressive disclosure again: human read on the surface, machine truth on demand.
The card answers "should I worry?"; the report answers "what exactly happened?" — the same check, read at two distances.
The shape — one map, one helper, every path
A FACE map keyed by the check's canonical label (not its internal key — some checks never reach the key→prefix table, and label-keying covers them all). Each entry carries a title and a faces dict of one sentence per verdict. A single helper does the lookup, and — this is the load-bearing rule — every render path calls it, not just the obvious one.
| Render path | Without the shared helper |
|---|---|
| the main card loop | the one place everyone remembers to fix |
| pending / placeholder cards | silently prints the raw label |
| the empty-window branch | a whole grid of raw labels (it returns early) |
the hover title= tooltip | "Latest GSC URL Inspection (per-page verdict) report" |
The element, in-page
The same check, rendered the machine way and the face way. Hover the face card's summary — the raw line it replaced is still reachable on the data-tip, exactly as the real card keeps it behind the report link:
The reference implementation
The map keyed by label, each entry a title plus one sentence per verdict. From ~/Code/dare-pipeline/scripts/dare_cf_analytics.py:
FACE = {
"Domain reconciliation (approved · monitored · on-the-list)": {
"title": "Domain reconciliation",
"faces": {
"green": "All resolving domains are approved and accounted for.",
"yellow": "A few domains are still in review — nothing unapproved.",
"red": "An unapproved domain is resolving — reconcile the approved list.",
"error": "Couldn't reconcile the domain list — see the report.",
},
},
# … one entry per check …
}
And the one helper every path calls — it returns the plain title and sentence, and falls back to the raw label/summary on any miss so it never crashes and never blanks:
def _face(label, verdict, summary, num):
"""(label, verdict) -> (title, face_text); raw label/summary on any miss."""
entry = FACE.get(label)
title = entry["title"] if entry else label # unknown check → raw label
text = summary # unknown verdict → raw summary
if entry:
tmpl = entry["faces"].get(verdict)
if tmpl:
try: text = tmpl.format(n=num) # {n} interpolates a figure
except (KeyError, IndexError, ValueError):
text = summary
return title, text
Wiring it into a path is one line — disp_label, face_text = _face(label, verdict, summary, num) — and the same line goes into every path that renders a card.
The trap — the half-wired map
The failure this pattern is built to prevent. The lookup gets written into the main card loop, the "today" view reads beautifully, and it looks done. But the same cards render through other paths — a pending-placeholder branch, an empty-window branch that returns early, a hover title= attribute — and each of those still emits the raw label and summary.
So on dash.dare.co.uk the 24-hour view was clean while the 7-day / 30-day / 90-day windows leaked jargon: the 90-day grid, whose anchor date predates the check history, hit the empty-window branch and rendered every card as a raw canonical label. The fix wasn't more lookups — it was collapsing them into one shared helper called from all four paths, hoisted high enough in scope that even the early-returning empty-state branch can reach it.
When a humanised card reads clean in one view and raw in another, suspect a second render path that never learned the mapping — and fix it by routing every path through one helper, not by copying the lookup.
Gotchas
- Key faces on the raw verdict, not the CSS class. Use
green/yellow/red/error— the values the parser emits — not a derived class likered-alert. A card whose verdict has no face silently falls back to the raw summary, so an amber-capable card that only defines green/red will leak the moment it goes amber. Audit every entry for the verdicts it can actually emit. {n}can eat a trailing%. If the figure comes from a regex like\b(\d+(?:\.\d+)?(?:%|/\d+)?)\b, the closing\bstrips a%that's followed by a space (no word boundary there) but keeps/Nfractions. So a percentage face must hard-code the sign:"{n}% of links work", not"{n} of links work".- Verdict-only faces are fine. When a summary's leading number isn't the meaningful one, write the sentence with no
{n}and let the verdict alone drive it. - Unknown is safe by construction. An unmapped label or an unmapped verdict returns the raw label/summary — jargon, yes, but never a crash and never a blank cell.
Where it applies
- dash.dare.co.uk — the Site-health grid: every tripwire card shows a plain title + verdict sentence, the raw rollup line behind the report link, across all four time windows
- Any dashboard rendering cards straight from a monitor, linter, CI job, or health-check whose native output is field-names and codes
- Status hubs, backup boards, deploy panels — anywhere a non-author reads a machine's verdict and needs "fine / not fine" before the detail
- Skip when the audience is the author and the raw field is the fastest read (a debugging view, a log tail) — there the jargon is the feature
Reusable elements
The named, copyable pieces — lift any one without the others:
- the
FACEmap — label →{title, faces:{verdict: sentence}}; keyed on the canonical label so checks absent from the internal key table are still covered. _face(label, verdict, summary, num)— the single lookup: returns(title, face_text), degrades to the raw label/summary on any miss.- the call-from-every-path discipline — main loop, pending placeholders, empty-window branch, hover
title=— one helper, hoisted above the earliest-returning branch. - the two-distance idiom — human face on the card, raw machine line kept reachable behind the detail link (or a
data-tip), never discarded. assets.gf.cx/tooltip/tooltip.css— the shared[data-tip]primitive used above to keep the raw line one hover away. See Plate № 10.
Reference
- Source
- Dan, 2026-07-24 — "site-health needs user-friendly written content", brainstormed into a plain-copy spec and built into the dashboard renderer.
- In use / example
- dash.dare.co.uk — the Site-health section; every card is a face, raw summary behind the report link, plain across 24h/7d/30d/90d.
- Reusable elements
- the
FACEmap ·_face()helper · the call-from-every-path discipline · the two-distance idiom ·tooltip.css(listed above). - Origin
- A jargon-heavy Site-health board, 2026-07-24 — machine status lines a non-author couldn't parse. The build surfaced the deeper rule: humane by default, machine-exact on demand, mapped in exactly one helper that every render path calls.