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

Source · Dan, 2026-07-24

"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 pathWithout the shared helper
the main card loopthe one place everyone remembers to fix
pending / placeholder cardssilently prints the raw label
the empty-window brancha 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:

Machine
DOMAIN RECONCILIATION (APPROVED · MONITORED · ON-THE-LIST)
unapproved=1 · resolving=1
Face
 Domain reconciliation
An unapproved domain is resolving — reconcile the approved list.

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

Where it applies

Reusable elements

The named, copyable pieces — lift any one without the others:

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 FACE map · _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.