# Design-System Audit — scanner-mobile-checkout

**Role:** `role-design-system-auditor`  
**Surface:** uncommitted `feat/scanner-mobile-checkout` UI (`git diff origin/main` + untracked scanner UI)  
**Convoy:** `.convoys/scanner-mobile-checkout.md`  
**Date:** 2026-08-14  
**Scope:** UI / DS only — read-only; no code changes  

**Inputs reviewed**

| Source | Notes |
| --- | --- |
| Diff UI | `Layout.js`, `ScannerCamera.js`, `ScannerCountPill.js`, `ScannerToast.js`, `ReviewCardItem.js`, `ScannerReview.js`, `pages/scanner.js` |
| Untracked | `ScannerCheckoutSheet.js`, `ScannerScanPeek.js` |
| Direction lock | `design_direction` v1 + `## Design direction` in convoy |
| 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** in this repo; audit follows role file + prior Liquid Glass audit shape + `docs/DESIGN_TOKENS.md` |

---

## Audit summary

| Check | Status | Notes |
| --- | --- | --- |
| Direction lock (Liquid Glass + immersive camera + sheet) | ⚠️ Partial | Chrome bars + VOCAB CTAs + Modal reuse land; **L-brackets / scan-line missing**; sheet tint recipe drifts |
| Hex in JSX | ❌ | 1 literal `#ffffff` in `ReviewCardItem.js` |
| Binder / stale vocab | ✅ | No `binder` / `Mark Owned` / `Save to binder` in scoped UI |
| `backdrop-filter` on cart **thumbnails** | ✅ | Thumbs are solid image/`--bg-tertiary`; no filter on `<img>` |
| GlassSurface / Button / Modal reuse | ⚠️ Mixed | Camera chrome + peek: GlassSurface; checkout sheet/toast/rows: `glass-panel*` / hand-rolled |
| Sev ≥ 3 findings | **2** | Hex literal; missing bracket + scan-line direction lock |

---

## Maturity scoring (repo DS + this surface)

Scores 0–4. Evidence cites paths/counts.

| Axis | Score | Evidence |
| --- | --- | --- |
| **Tokens (T)** | **3** | Documented 3-tier glass surface / blur / rim / elevation / scrim in `docs/DESIGN_TOKENS.md` + `styles/globals.css`. Diff mostly consumes `var(--*)`. Residual: `.glass-panel` still hardcodes `blur(12px) saturate(180%)` (not `--glass-blur-*` / `--glass-saturate`); toast uses literal `blur(12px)`. |
| **Components (C)** | **3** | Kit under `components/ui/`: GlassSurface, Modal, Button, Input, SearchBar, StatCard (+ chrome). Scanner adopts GlassSurface on camera bars/icons/peek (9 call sites in `ScannerCamera.js`); Modal×2 + Button in `pages/scanner.js` / checkout footer. Gaps: toast + sheet panel + cart rows skip GlassSurface. |
| **Patterns (P)** | **2** | Locked pattern “immersive camera + bottom-sheet checkout” partially shipped (immersive Layout, sheet shell mirroring Disambiguation, peek non-interactive). **Detection chrome still filled rectangles** (info/success), not ember L-brackets + scan-line. Confidence mid-band maps to gold, not direction’s amber/ember table. |
| **Governance (G)** | **3** | Convoy `design_direction` locked 2026-08-14; `ui-and-theming.mdc` + vocab CI; `forbidden-hex-in-jsx` / forbidden stale strings. No design-systems skill package in-repo (template path missing). |
| **Adoption (A)** | **2** | This surface: GlassSurface on live chrome/peek ✅; checkout sheet + toast + rows use utility classes / ad-hoc styles ❌; Review N pill is raw `<button>` with gradient (OK shape) but skips ember-rim + dead GlassSurface import. Vocab CTAs ✅. |

**Composite:** T3 / C3 / P2 / G3 / A2

**Top leverage:** **Patterns (P)** — implement Screen 1 bracket + scan-line recipe and align checkout sheet to `GlassSurface tint="low"` + toast to GlassSurface/`--glass-surface-high` so the locked Liquid Glass pattern reads as one system, not chrome-good / body-utility.

---

## Direction lock

Locked fields enforced: Liquid Glass; immersive overlay + bottom sheet; ember/flame accents as rim/glow/gradient only; VOCAB CTAs; no binder; no hex; no per-card Add on peek; no `backdrop-filter` on cart thumbs; reuse GlassSurface / Modal / Button.

| Locked requirement | Diff status |
| --- | --- |
| Immersive `chrome="immersive"` mobile | ✅ `Layout.js` + `pages/scanner.js` |
| Top/bottom glass bars (`--glass-surface-mid`) | ✅ `GlassSurface tint="mid"` in `ScannerCamera.js` |
| L-brackets `--accent-flame` / `--accent-ember` + scan-line | ❌ Filled `border` boxes + `--color-info` / `--color-success` overlays; **no** scan-line / `prefers-reduced-motion` gate |
| Scan peek glass, tap-to-cart only | ✅ `ScannerScanPeek.js` |
| Toast glass-high + SVG check | ⚠️ SVG ✅; surface is `rgba(--bg-secondary-rgb)` + literal blur, not GlassSurface / `--glass-surface-high` |
| Review N gradient + `--ember-rim-pronounced` | ⚠️ Gradient ✅; **no** ember rim; unused `GlassSurface` import |
| Sheet scrim `--modal-scrim` + blur-high | ✅ `ScannerCheckoutSheet.js` styled-jsx |
| Sheet panel `--glass-surface-low` via GlassSurface | ❌ `glass-panel-strong` → surface-**high** recipe |
| Footer VOCAB + Button primary/ghost | ⚠️ VOCAB ✅; secondary uses `variant="secondary"` not `ghost` |
| Confidence rings tokenized (green / amber / orange) | ⚠️ Tokens used; mid=`--accent-gold`, low=`--color-warning` vs lock’s ember/amber table |
| No hex / no binder | ❌ one `#ffffff`; binder clear |
| Modal for leave + list picker | ✅ |
| No backdrop on thumbnails | ✅ |

---

## Token audit (3-tier)

| Tier | Expectation | Diff notes |
| --- | --- | --- |
| Primitives | Palette / RGB triples in CSS only | No new hex in CSS from this diff. JSX `#ffffff` violates consumer rule. |
| Aliases | `--glass-surface-*`, `--glass-blur-*`, rims, scrim | Camera/scrim/peek mostly correct. Toast blur literal `12px` should be `var(--glass-blur-low)`. `.glass-panel` utility (row list) still non-token blur/saturate (pre-existing; amplified by N cart rows). |
| Components | GlassSurface / Button / Modal compose aliases | Sheet + toast + rows compose utilities/hand styles instead of primitives. |

---

## Component / adoption audit (scoped surface)

| Element | Expected | Actual | Rate |
| --- | --- | --- | --- |
| Glass chrome bars | `<GlassSurface>` | Yes (`ScannerCamera`) | High |
| Scan peek | `<GlassSurface>` | Yes | High |
| Toast | `<GlassSurface tint="high">` or equivalent | Hand-rolled | Low |
| Checkout sheet panel | `<GlassSurface tint="low">` | `glass-panel-strong` | Low |
| Cart rows | GlassSurface high **or** solid `--bg-secondary` | `.glass-panel` (blur per row) | Medium — OK container glass; GPU stack risk |
| Footer CTAs | `<Button>` | Yes | High |
| Leave / List dialogs | `<Modal>` | Yes | High |
| Review N | Button or GlassSurface + ember rim | Raw `<button>` + gradient | Medium |

**Missing primitive usage (not missing primitives):** toast + sheet should compose existing GlassSurface rather than inventing parallel glass.

---

## 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 JSX | `ReviewCardItem.js` qty `+` button `color: '#ffffff'` | Use theme-safe token (e.g. dark-theme `--text-primary` on ember fill, or shared on-accent pattern used by `<Button variant="primary">`) |
| DS-2 | **3** | Direction lock: detection chrome ≠ L-brackets + scan-line | `ScannerCamera.js` filled rect overlays via `overlayBorderColor` (`--color-info` / `--color-success`); no bracket SVG/corners; no ember scan-line / reduced-motion | Ship Screen 1 bracket recipe (`--accent-ember` / `--accent-flame`) + gradient scan-line; disable loop under `prefers-reduced-motion` |
| DS-3 | **2** | Toast bypasses glass recipe | `ScannerToast.js`: `rgba(var(--bg-secondary-rgb), 0.9)` + `backdropFilter: 'blur(12px)'` | Wrap with `<GlassSurface tint="high" blur="low|mid" rim="subtle">`; drop literal blur px |
| DS-4 | **2** | Checkout sheet panel wrong surface tier + no GlassSurface | `ScannerCheckoutSheet.js` `glass-panel-strong` (high) for panel + sticky footer | Panel: GlassSurface `tint="low"` inside scrim; footer sticky on same recipe or solid inner; match Modal panel docs |
| DS-5 | **2** | Review N missing ember-pronounced rim; dead import | `ScannerCountPill.js` imports GlassSurface unused; no `--ember-rim-pronounced`; `text-white` utility | Apply rim via GlassSurface `as="button"` `rim="ember-pronounced"` or box-shadow token; remove dead import; avoid raw white |
| DS-6 | **2** | Confidence band colors drift from lock | `confidenceRingColor`: &lt;70 `--color-warning`, 70–84 `--accent-gold`, else success | Align to lock: &lt;70 `--accent-ember`, 70–84 amber/warning token, ≥85 `--color-success` |
| DS-7 | **1** | Secondary CTA variant | Checkout footer `Button variant="secondary"` | Prefer `variant="ghost"` per design direction |
| DS-8 | **1** | Leave-modal dismiss copy | `pages/scanner.js` label `"Stay"` | Direction: primary dismiss **"Keep scanning"** |
| DS-9 | **1** | Hardcoded vocab fallback | `ReviewCardItem.js` `'My Collection'` string | `VOCAB.MY_COLLECTION` |
| DS-10 | **1** | Stacked row `backdrop-filter` via `.glass-panel` | Each cart row blurs (`blur(12px)` in utility) | Prefer solid `--bg-secondary` (or GlassSurface without stacking N blurs) for row inners over thumbs — thumbs themselves are clean |

### Passes (not findings)

- No `"binder"` / `"Save to binder"` / `"Mark Owned"` in scoped UI.
- Checkout CTAs import `VOCAB.ADD_TO_MY_COLLECTION` / `VOCAB.ADD_TO_LIST`.
- List picker uses `collectionDisplayName`.
- Peek has no Add / condition / foil; single open-cart control.
- Toast icons are SVG (no emoji glyphs).
- Scrim uses `--modal-scrim` + `var(--glass-blur-high)`.
- Cart thumbnails: no `backdrop-filter`.
- Leave + List picker use `<Modal>`; commit CTAs use `<Button>`.

---

## Governance notes

- Contribution path for glass: `docs/DESIGN_TOKENS.md` + `ui-and-theming.mdc` (“reach for `components/ui/` first”).
- CI: hex-in-JSX + stale vocab gates apply — **DS-1 will fail / should fail `forbidden-hex-in-jsx`**.
- Design-systems skill/templates not present under `.cursor/skills/design-systems/` — maturity G capped partly by missing shared audit tooling in-repo.

---

## Recommendation

**Do not treat DS as merge-clean until DS-1 and DS-2 are fixed.** DS-3–DS-6 are the follow-up pass that makes checkout/toast/pill match the same Liquid Glass composition as the camera chrome.

**Child tasks (sev ≥ 3):**

1. Replace `#ffffff` in `ReviewCardItem.js` (and any sibling `text-white` on detection badges if hex-gate/token policy treats them as on-accent debt).
2. Implement ember/flame L-brackets + reduced-motion-aware scan-line on the viewfinder per locked Screen 1.

---

## Hand-off

DS audit complete. Maturity: **T3/C3/P2/G3/A2**. Top leverage: invest in **Patterns** (brackets/scan-line + sheet/toast GlassSurface alignment). Sev ≥ 3 findings: **2**. Report: `.convoys/scanner-mobile-checkout/audits/design-system-20260814.md`.
