Give /scanner a md+ camera, live match inspector, and history strip (with device picker, batch scan, and tips) without regressing the mobile immersive checkout. Co-authored-by: Cursor <cursoragent@cursor.com>
10 KiB
Design-System Audit — scanner-desktop-layout
Role: role-design-system-auditor
Surface: convoy/scanner-desktop-layout workstation UI (git diff origin/main + untracked scanner components)
Convoy: .convoys/scanner-desktop-layout.md
Multitask group: audit-scanner-desktop-layout-local
Date: 2026-08-15
Scope: UI / DS only — read-only; no code changes
Inputs reviewed
| Source | Notes |
|---|---|
Diff UI (vs origin/main) |
pages/scanner.js, components/scanner/ScannerCamera.js (+ unrelated dashboard/Layout noise in same branch tip — out of audit scope) |
| Untracked (convoy) | ScannerTips.js, ScannerResultPanel.js, ScannerHistoryStrip.js + component tests |
| Direction lock | design_direction v1 + ## Design direction + ## UX |
| Tokens | docs/DESIGN_TOKENS.md, styles/globals.css |
| Primitives | components/ui/{GlassSurface,Button,Modal,Input}.js |
| Skill | .cursor/skills/design-systems/SKILL.md not installed; report follows prior Liquid Glass audit shape + role file |
Audit summary
| Check | Status | Notes |
|---|---|---|
| Direction lock (md+ workstation grid) | ✅ Strong | Framed camera + desk bar + inspector rail + history strip + Tips Modal; mobile immersive left alone |
| Hex in JSX (new convoy files) | ❌ | #ffffff on Queue/Duplicates badge in ScannerHistoryStrip.js |
| VOCAB / stale strings | ✅ | VOCAB.ADD_TO_* / MY_COLLECTION; no Wishlist / Mark Owned / All My Cards |
backdrop-filter on strip chips |
✅ | Chips solid --bg-secondary; container is GlassSurface tint="mid" |
| GlassSurface / Button / Modal reuse | ✅ High | Tips=Modal; inspector/strip/desk/camera frame=GlassSurface; primary CTAs=Button |
| Custom modal scrim | ✅ | Tips + leave + list picker use <Modal> only |
| Sev ≥ 3 findings | 1 | Hex literal on tab badges |
Maturity scoring (repo DS + this surface)
Scores 0–4. Evidence cites paths/counts.
| Axis | Score | Evidence |
|---|---|---|
| Tokens (T) | 3 | Workstation surfaces consume --glass-surface-* via GlassSurface, --text-*, --accent-ember / --accent-flame, --ember-rim-*, --border. Residual: on-accent text still #ffffff in new strip badges (and pre-existing Button.js primary/danger). No --text-on-accent alias in globals.css. |
| Components (C) | 3 | Kit: GlassSurface, Modal, Button, Input, SearchBar, StatCard. New surfaces compose GlassSurface + Button + Modal. Gaps: no Switch / TabList primitive — Auto-detect + Foil switches and strip tabs are raw <button> (acceptable; pattern repeated ×3). |
| Patterns (P) | 3 | Locked workstation pattern largely shipped: Layout chrome="default" at md+, camera variant="workstation" with ember brackets + reduced-motion scan-line (ScannerCamera.js DetectionFrame), desk GlassSurface mid, inspector tint="low", strip three tabs + batch banner, Tips <Modal size="md">. Minor drift: confidence fill uses --color-warning / --color-success vs direction’s flame/gold highlight row (aligned with UX “reuse ReviewCardItem” instead). |
| Governance (G) | 3 | Convoy design_direction locked 2026-08-15; ui-and-theming.mdc + vocab CI; forbidden-hex-in-jsx documented in DESIGN_TOKENS.md. Design-systems skill package still absent in-repo. |
| Adoption (A) | 3 | Desktop surface counts: <Button> ×12 across scanner.js + ResultPanel + HistoryStrip + Tips; <GlassSurface> on camera frame, desk bar, inspector, strip container, Auto-detect badge; <Modal> ×1 Tips + leave/list on page. Chips correctly avoid GlassSurface blur. Raw <button> ×7 mostly switches/tabs/Clear All (direction allows Clear All as text link). |
Composite: T3 / C3 / P3 / G3 / A3
Top leverage: Tokens (T) — introduce a shared on-accent text token (or route badge text through <Button> / a tiny Badge primitive) and purge #ffffff from convoy JSX so the hex gate and Liquid Glass lock stay green.
Direction lock
Locked fields enforced: Liquid Glass; md+ workstation (camera + inspector + strip); ember accents; VOCAB CTAs; no hex; no per-chip backdrop-filter; Tips via <Modal>; no custom scrim; mobile immersive unchanged.
| Locked requirement | Diff status |
|---|---|
md+ chrome="default"; mobile immersive |
✅ pages/scanner.js |
| Page title / subtitle + Scanner Tips | ✅ Header + ScannerTips |
| Camera framed panel + ember brackets | ✅ variant="workstation" + DetectionFrame L-brackets + scan-line + prefers-reduced-motion |
| Desk bar: Camera / Upload / Batch / Auto-detect | ✅ GlassSurface tint="mid" + native <select> + Button×2 + switch |
| Device picker fallbacks inline (not modal) | ✅ camera.devicePickerMessage under select |
Inspector rail w-[360px] + GlassSurface low |
✅ ScannerResultPanel |
| Empty / confidence / VOCAB primary + Rescan / Add to List | ✅ |
| Strip: Recent / Queue / Dupes + Clear All + View All | ✅ ScannerHistoryStrip |
Chips solid --bg-secondary; focused ember rim |
✅; no chip backdrop-filter |
| Batch progress + Cancel | ✅ BatchProgressBanner |
Tips = <Modal> five tips (not popover) |
✅ ScannerTips.js |
| No hex / no Wishlist / Mark Owned | ❌ one #ffffff; vocab clean |
| Checkout sheet mobile-only | ✅ md:hidden mount |
Token audit (3-tier)
| Tier | Expectation | Diff notes |
|---|---|---|
| Primitives | Hex only in CSS token definitions | No new CSS hex from convoy. JSX #ffffff in strip badge violates consumer rule. |
| Aliases | --glass-surface-*, blur, rim, elevation, text, accent |
Correctly used via GlassSurface props + var(--*) styles. Missing alias for on-accent / inverse text (primitives still hardcode #ffffff). |
| Components | GlassSurface / Button / Modal compose aliases | Desk, inspector, strip, Tips comply. Tab badge invents parallel on-accent styling instead of composing Button/Badge. |
Component / adoption audit (scoped desktop surface)
| Element | Expected | Actual | Rate |
|---|---|---|---|
| Camera frame | GlassSurface low + ember rim |
Yes (ScannerCamera workstation branch) |
High |
| Desk control bar | GlassSurface mid |
Yes (pages/scanner.js) |
High |
| Inspector | GlassSurface low |
Yes (ScannerResultPanel) |
High |
| History strip shell | GlassSurface mid |
Yes | High |
| Strip chips | Solid --bg-secondary (no blur) |
Yes | High |
| Tips | <Modal> |
Yes | High |
| Primary / secondary CTAs | <Button> |
Yes (Upload, Batch, Add, Rescan, Cancel, View All) | High |
| Auto-detect / Foil | Switch pattern | Raw <button role="switch"> ×2 |
Medium — no Switch primitive |
| Strip tabs | tablist | Raw <button role="tab"> |
Medium — correct a11y; not Button |
| Clear All | Text link | Raw <button> ember text |
Medium — matches direction “text link” |
| Leave / List | <Modal> |
Yes (page) | High |
Missing primitive (optional follow-up): Switch (and optionally Badge) would remove duplicated toggle/#ffffff badge styling across desk + inspector + strip.
Findings
Severity 0–4. Cap prioritized; sev ≥ 3 blocks merge recommendation for DS gate.
| ID | Sev | Finding | Evidence | Fix |
|---|---|---|---|---|
| DS-1 | 3 | Hardcoded hex in convoy JSX | ScannerHistoryStrip.js tab badge color: '#ffffff' (≈L250) |
Replace with theme token (add --text-on-accent / reuse Button’s on-accent once tokenized) so forbidden-hex-in-jsx stays green |
| DS-2 | 2 | Toggle knobs use Tailwind bg-white |
pages/scanner.js Auto-detect knob; ScannerResultPanel.js Foil knob |
Prefer backgroundColor: 'var(--bg-primary)' or a documented knob token — avoid raw white utility on themed surfaces |
| DS-3 | 1 | Confidence bar accent drift vs Design direction color table | ScannerResultPanel confidenceRingColor: mid --color-warning, high --color-success |
Optional align to --accent-flame / --accent-gold for ≥90% match badge; or document UX override (ReviewCardItem reuse) as accepted |
| DS-4 | 1 | Clear All lacks destructive hover | Always --accent-ember |
Direction: destructive on hover — add hover token mix if shipping polish pass |
| DS-5 | 1 | Systemic on-accent hex in primitive (context) | components/ui/Button.js primary/danger still #ffffff / #dc2626 |
Out of convoy scope but explains copy-paste; tokenize in a follow-up DS convoy |
Passes (not findings)
- No
"Add to Collection","Save to Wishlist","Mark Owned","All My Cards"in scoped new UI. - Inspector + strip CTAs import
VOCAB.ADD_TO_MY_COLLECTION/VOCAB.ADD_TO_LIST. - Tips uses
<Modal size="md">with convoy-authored five tips; no custom scrim. - Strip chips: solid
--bg-secondary, focused--ember-rim-pronounced; no per-chip blur. - Camera detection chrome: ember/flame L-brackets + scan-line with
prefers-reduced-motionstatic mid-line. - Auto-detect badge:
motion-safe:animate-pulsewhen ON. - Confidence bar width transition gated with
@media (prefers-reduced-motion: reduce). - Icons are SVG (lightbulb / empty-state camera), not emoji.
- Mobile checkout sheet remains
md:hidden; workstation controlshidden md:flex.
Governance notes
- Contribution path:
docs/DESIGN_TOKENS.md+.cursor/rules/ui-and-theming.mdc. - CI:
forbidden-hex-in-jsx+ stale-vocab gates — DS-1 will fail the hex gate once these files are in CI’s path. - Design-systems skill/templates not under
.cursor/skills/design-systems/— G capped partly by missing shared audit tooling.
Recommendation
Treat DS as not merge-clean until DS-1 is fixed (one-line / token swap on the strip badge). DS-2 is the next polish item for light/dark knob contrast. DS-3–DS-5 are non-blocking documentation / follow-up token work.
Child tasks (sev ≥ 3):
- Remove
#fffffffromScannerHistoryStrip.jsbadge text — use a CSS variable (prefer adding--text-on-accent: #fffinglobals.cssand referencing it from Button + badge).
Hand-off
DS audit complete. Maturity: T3/C3/P3/G3/A3. Top leverage: invest in Tokens (on-accent alias + purge convoy hex). Sev ≥ 3 findings: 1. Report: .convoys/scanner-desktop-layout/audits/design-system-20260815.md.