Reviewed by /design-review on 2026-05-22 against the live admin (/admin/, /admin/observability/, /admin/replay/). Audited via CDP through real Chrome behind Cloudflare Access. Two outside-voice passes (Codex source audit + Claude consistency subagent) added to the live findings.

Full audit + screenshots + baseline.json live in ~/.gstack/projects/dghauri0-ihnyc-rc-cal-landing/designs/design-audit-20260522/ on the operator’s machine. This doc is the durable summary that should outlive that local artifact.

Baseline

MetricGradeNotes
Design ScoreB-Solid information design; sloppy plumbing underneath
AI Slop ScoreANo purple gradients, no icon-circle SaaS template look, no decorative blobs
Visual HierarchyBKPI numbers dominate correctly
TypographyC+Inverted h1/h2/h3 sizes; h3 drifts between 13px and 15px across pages
Color & ContrastA-Coherent slate-on-white + 4-color semantic system
Spacing & LayoutC+Magic-number padding everywhere; KPI strip chrome drifts admin/replay vs observability
Interaction StatesDNo :focus-visible rings; drawer aria-hidden but DOM-present; rows mouse-only
ResponsiveCAdmin has column-toggle; observability and replay don’t
PerformanceALCP 275ms, no CLS

Findings (18 total)

4 HIGH, 8 MEDIUM, 6 POLISH. Five carry cross-model confidence (both Codex and the Claude subagent flagged independently): triplicated CSS tokens, missing focus rings, shell drift across pages, ship-dark jargon in UI, deliverability grid hard-coded 6-up.

The single biggest revelation: each of the three admin pages re-declares the same :root color token block inline, plus the same [data-theme="dark"] override block. They’re identical today only by discipline — a --accent-soft tweak today is a 3-place edit that will eventually drift.

What shipped this pass (PR #9)

Tier A (quick wins, no functional behavior change) + Tier B (keyboard a11y, additive only). Seven atomic commits, one per finding:

FindingTypeEffect
F-004 ship-darkflag offcopyRemoves dev jargon from observability status pill
F-006 Sparkline axis separatorcopy30d ago · today instead of visually colliding “30d agotoday” in narrow drawer
F-015 Drop emoji from Test MixpanelcopyOperator UI shouldn’t lean on emoji for visual signal
F-013 Drop label pills from deliverability tilescontentPills previously always rendered green regardless of value, miscommunicating “everything healthy” when actually “no data yet”
F-001 :focus-visible ringsa11y CSS:where() selector keeps specificity at 0; mouse interactions unchanged; keyboard users finally see focus
F-002 Drawer inert when closed + focus managementa11y JSClosed drawer is no longer tab-reachable; open captures activator, close restores focus
F-011 Rows keyboard-actionablea11y HTML+JStabindex=0 role=button + Enter/Space keydown mirroring click path with same kebab/menu skip rules

Decisions held during triage

DecisionChosenAlternative rejected
F-016 /admin/* CSP block in _headersDefer — touching CSP near Cloudflare Access gating needs its own deploy-test cycle per ops rulesAdd default-src 'self'; img-src 'self' data:; … inline now
F-015a Ghosts filter chip → InactiveKeep “Ghosts” — operator-chosen term with clear tooltip (“Active+unrevoked tokens that haven’t fetched in 30+ days”)Rename to neutral “Inactive” / “Lapsed” / “Dormant”
F-017 h2.section:first-child margin reset on adminKeep current 26px top margin — reads as intentional breathing room under the anomaly bannerMatch obs/replay behavior
Big system fixes (F-003, F-005, F-007, F-008, F-009, F-010, F-012)Defer to dedicated Tier C PRBundle into this PR

Admin UI conventions (preserve when editing)

These are now load-bearing. Future edits to public/admin/**/*.html should keep them.

  1. :focus-visible ring is universal. Every interactive element (buttons, links, inputs, selects, summary, [tabindex]) gets a 2px --accent outline via a :where() rule placed near the top of each page’s <style> block. Do not add outline: none on :focus without replacing with a :focus-visible rule.

  2. Drawer is inert when closed. Always toggle the inert attribute alongside aria-hidden so closed drawer controls aren’t tab-reachable. On open, capture document.activeElement; focus the drawer’s close button. On close, restore focus to the captured element.

  3. Subs table rows are activators, not decorations. Each <tr data-token-id> carries tabindex="0", role="button", and aria-label. The page-level keydown handler mirrors the click path’s skip rules for kebab/menu/inline-action descendants.

  4. Status pills mean state, not labels. A pill that always renders green regardless of value is decoration, not signal — drop it. The deliverability tiles followed this rule after F-013; new tiles should too.

Tier C — known deferred work (next PR)

Listed in priority order. The first item is the unlock for most of the others.

  1. F-005 Extract /public/admin/_admin.css — single <link rel="stylesheet"> per page, hosts the token block, dark-theme override, focus-visible rule, KPI chrome, section headings, skeleton utility. ~300 LOC of duplication collapses; F-001 stops being three identical CSS additions.
  2. F-003 Heading hierarchy rework. Rename h2.section<p class="eyebrow"> (it’s a label, not a heading). Introduce real per-page <h2> page titles (Admin / Observability / Replay) at 18–20px semibold. Demote h1.app-title to non-heading or hide from AT.
  3. F-007 Consolidate KPI strip chrome — promote the admin/replay “stitched strip” pattern, remove .health-card border chrome from observability.
  4. F-008 Deliverability grid → grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)) so the 6-tile row wraps gracefully at narrow viewports.
  5. F-009 Skeleton-loader utility. Observability’s first viewport currently reads as broken for ~200ms while five “Loading…” placeholders sit empty.
  6. F-010 Touch-target standardization. Introduce --touch-min: 32px token; tab links currently 22px on admin/replay vs 26px on observability.
  7. F-012 Mobile responsive overhaul — extend admin’s column-toggle to replay and observability.
  8. F-014 Drawer “active (token revoked)” status — separate fields instead of internally-contradictory parenthetical.
  9. F-016 /admin/* CSP block (see Decisions above).
  10. F-018 Default sort — surface healthy active subs above revoked/expired rows so first-impression isn’t a wall of red status pills.