# Figma-vs-Code Fidelity Audit — Synthesis Report

34-component Vue design system · adversarially-verified · 2026-05-29

---

## 1. Executive summary

**How far from "1:1 Figma fidelity" the library actually is:** The library is *structurally* sound but *not* pixel-1:1. The architecture is healthy — every canonical→legacy wrapper layer is a registered divergence, color tokens are overwhelmingly used (essentially zero hardcoded hex found across all 34 components), and the dual-family/eight-set component collapses are all legitimately documented. The fidelity gap is concentrated in three places: (a) a recurring **disabled-state `opacity:0.4` fabrication** across the Select family, (b) a recurring **light-theme token-substitution** bug (`--text-heading` / `--text-2` used where Figma binds a theme-stable token), and (c) one **structurally hollow component (Chart)** that drops its entire card chrome. Most "drift" is low-severity verifier-tolerance noise or extract-pipeline blind spots, not visual mismatch.

**Confirmed drifts by severity** (skeptic-refuted findings excluded; severities use the corrected severity where the skeptic re-graded):

| Severity | Count |
|----------|-------|
| High | 13 |
| Medium | 10 |
| Low | 18 |
| **Total confirmed** | **41** |

**Fidelity distribution (per the audit's `overallFidelity`):**

- **High:** 12 — Badge, Breadcrumb, BreadcrumbItem, InputNumber, PopupBox, Pagination, Rating, Switch, Tab, TabList, Tooltip, TopBar
- **Medium:** 13 — Button, CheckBox, DropDownListSelect, FormItem, InputBoxBase, InputBoxFilled, InputBoxLine, Notification, Progress, PromptMessage, Radio, SelectBoxBase, SelectBoxFilled, SelectBoxLine, Slider, StepItem, Steps, Table, TabItem *(15 medium-rated entries; several are wrappers over the same base)*
- **Low:** 1 — PillStatus
- **Unknown (no Figma SoT):** 1 — PillCounter

**Audit-invisible (no render-verification coverage):** 3 distinct surfaces — **InputBoxBase** (internal base, uncovered directly), **PillCounter**, **PillStatus** (+ base Pill). The main **Button** 8-set family (~3200 variants) is *registered* in `COMPONENT_TARGETS` only via a 4-variant url-link approximation, so its primary color/geometry matrix is effectively audit-invisible too.

---

## 2. Confirmed drift inventory

Ranked by severity, then component. Skeptic-refuted (`real:false`) findings are excluded — see filtered list below.

| Component | Dimension | Severity | Summary | Existing ID / NEW | Suggested fix |
|-----------|-----------|----------|---------|-------------------|---------------|
| Chart | geometry | High | `.tvu-chart` has no padding/radius/gap; Figma card = 16px pad, r=8, gap 8–12 (all 6 variants) | **NEW** | Add `padding:16px; border-radius:8px` + legend gap to `.tvu-chart` |
| Chart | color-token | High | Container bg `--bg-layer3` (#262626/#f0f0f0) dropped entirely; chart renders transparent | **NEW** | Add `background-color: var(--bg-layer3)` |
| CheckBox | a11y | High | `<label @click.prevent>` + span: no native input/role/tabindex/aria-checked/keydown — mouse-only | **NEW** *(CANONICAL-013 class)* | Real `role="checkbox"`+tabindex+aria-checked+keydown, or hidden native input |
| DropDownListSelect | composition-reuse | High | Multi check affordance hand-rolled span+Icon instead of canonical CheckBox | **CANONICAL-012** | Replace inline span with `<CheckBox>` |
| DropDownListSelect | a11y | High | Rows are `<div @click>`; no listbox/option roles, tabindex, or keyboard nav | **CANONICAL-016** | Add role=listbox/option, aria-selected, keydown |
| PillStatus | color-token | High | Status→hue inverts mockup legend: Preview→blue (Figma green), red/On-air missing entirely | **NEW** | Re-map On-air→red, Preview→brand, add red status |
| Progress | color-token | High | Regression: reintroduced `progress--${theme}` + `.progress--dark/.light` track-bg fork that Figma doesn't have; violates registered accepted divergence | **NEW** *(reopen progress-pseudo-theme divergence)* | Re-apply T1b removal; wire verifyHint grep into pre-commit |
| Radio | a11y | High | `<label @click.prevent>`+decorative span: no input/role/tabindex/aria-checked/keydown | **NEW** *(CANONICAL-013 class)* | Real `role="radio"` (or hidden native input) + keyboard handling |
| SelectBoxBase | a11y | High | Chip-remove & clear are inline `<span @click.stop>` — not keyboard-operable | **CANONICAL-013** | Real `<button>` + aria-label + keyboard activation |
| SelectBoxFilled | state-matrix | High | Disabled blanket `opacity:0.4`; Figma disabled = opacity 1, color-token only | **NEW** | Remove `opacity:0.4`; rely on disabled tokens |
| SelectBoxFilled | a11y | High | Chip-remove/clear inline spans nested inside trigger `<button>` (invalid nesting), no keyboard | **CANONICAL-013** | Real buttons, moved out of trigger |
| SelectBoxLine | color-token | High | Disabled blanket `opacity:0.4` over already-correct tokens (verified) | **NEW** | Remove `opacity:0.4` |
| SelectBoxLine | a11y | High | Chip-remove/date-time clear inline `<span @click.stop>`, no keyboard | **CANONICAL-013** | Real `<button>` + aria-label |
| Button | color-token | Medium *(skeptic re-graded from High)* | filling/green/disable text uses `--text-tips` (#595959/#9e9e9e) vs Figma `--color-grey-2` (#f8f8f8) — opposite end of grey scale (verified) | **NEW** | Map to token resolving #f8f8f8, or register divergence |
| Button | color-token | Medium | filling/gray/disable text `--text-placeholder` (#7b7b7b light) vs Figma `--color-grey-6` (#9e9e9e) | **NEW** | Use #9e9e9e-resolving token (light) |
| FormItem | color-token | High | Status=Error doesn't turn label red; no `.form-item--error .form-item__label` rule | **CANONICAL-019** | Add `.form-item--error .form-item__label { color: var(--red); }` |
| InputBoxFilled | geometry | High | feature=yes forces input `padding-left:32px` vs Figma 12px (32 FAILs) | **EXTRACT-007** | Source icon inset from Figma child geometry |
| InputBoxFilled | geometry | High | feature+clear forces `padding-right:30px` vs Figma 12px (4 FAILs) | **EXTRACT-007** | Derive clear inset from child geometry |
| InputBoxFilled | a11y | High | Clear is bare `<span @click>`, no role/tabindex/keydown | **CANONICAL-013** | Real `<button type=button aria-label>` |
| InputBoxLine | geometry | High | feature=yes `padding-left:32px` vs Figma pH=12 (36 FAILs) — genuine visual drift, not just schema | **EXTRACT-007** | Confirm overlay-vs-inset intent; use non-padding offset |
| InputBoxLine | a11y | High | Clear affordance hand-rolled `<span @click>`, not keyboard-operable | **CANONICAL-013** | Real `<button>` + aria-label |
| Notification | geometry | Medium | Border 1px vs Figma strokeWeight 1.2px (all variants) | **NEW** | Set border-width 1.2px (or token) |
| PromptMessage | color-token | High | All four status **text** colors frozen at dark hex; Figma binds theme-varied `--blue/--red/--orange` — bg/text desync in light theme | **CANONICAL-017** | Map text to theme-aware semantic tokens or add light overrides |
| SelectBoxBase | state-matrix | High | Disabled blanket `opacity:0.4`; Figma disabled = opacity 1, per-token colors (verified) | **NEW** | Remove `opacity:0.4` |
| SelectBoxBase | state-matrix | Medium | Filled-disabled gets grey border via unscoped `:disabled` rule; Figma filled-disabled `strokes:[]` | **NEW** | Scope disabled border to `.select-wrap--line` only |
| SelectBoxFilled | composition-reuse | Medium | Multi rows route through DropDownListSelect's hand-rolled check, not CheckBox | **CANONICAL-012** | Reuse CheckBox.vue in DropDownListSelect Multi rows |
| SelectBoxLine | geometry | Medium *(skeptic re-graded from High)* | size=M font-size 13px vs Figma 14px (all M variants) | **NEW** | Set M font-size 14px (or drop per-size override) |
| SelectBoxLine | composition-reuse | Medium | Multi chips hand-rolled spans + DropDownListSelect check/role drift | **CANONICAL-012** | Route check via CheckBox; add listbox/option roles |
| Slider | color-token | High | Value text `--text-2` (#cccccc dark / #434343 light) vs Figma static `--color-grey-5` (#cccccc) — light-theme drift | **CANONICAL-019** | Bind to static `var(--color-grey-5)` |
| Slider | geometry | Medium | Rendered range width ~192px vs Figma 240px (no explicit width on `.slider__input`) | **CANONICAL-019** | Add `width:240px` to `.slider__input` |
| StepItem | color-token | High | Active title `--text-heading` (#000 light) vs Figma `--text-primary-btn` (#fff both themes) — black in light | **NEW** | Use `var(--text-primary-btn)` |
| StepItem | color-token | Medium *(skeptic re-graded from High)* | Active number marker `--text-heading` vs Figma `--text-primary-btn` — black number in light | **NEW** | Use `var(--text-primary-btn)` for glyph (check filled-circle bg too) |
| StepItem | composition-reuse | Medium | Completed marker hand-rolls check via inline `<Icon name=selected>` | **NEW** | Confirm/reuse canonical bullet-check primitive, or document |
| Steps | color-token | Medium *(skeptic re-graded from High)* | Active title/marker `--text-heading` vs Figma `--text-primary-btn` — black in light theme | **NEW** | Switch active title/marker to `var(--text-primary-btn)` |
| Steps | state-matrix | Medium | Pending number-style title font-weight 400 vs Figma 600 (single 400 rule for all pending) | **NEW** | Make pending weight style-conditional (number→600) |
| Table | geometry | High | Cell horizontal padding 16px (`--sp-m`) vs Figma 8px; drives w=120→141.95 overshoot | **CANONICAL-019** | Change cell padding to `var(--sp-xs)` (8px) horizontal |
| Table | geometry | Medium | Cell content inline gap 8px vs Figma itemSpacing 4px | **NEW** | Change `.tbl-cell__content` gap to `var(--sp-xxs)` (4px) |
| TabItem | geometry | Medium | Container has no `gap`; Figma itemSpacing 8px (icon↔label renders 0px) | **NEW** | Add `gap: var(--sp-s)` to `.tab-item` |
| TabItem | a11y | Medium | `<div @click>`, no role=tab/tabindex/keyboard | **CANONICAL-016** | Add role=tab, aria-selected, tabindex, keydown |
| TabList | geometry | Medium | Line container divider 2px vs Figma strokeWeight 1 | **NEW** | Set divider 1px (or register the 2px overlap rationale) |
| InputBoxFilled | color-token | Medium | One light focus L feature=no FAIL: text #9e9e9e vs Figma #434343 (placeholder-vs-entered ambiguity) | **NEW** | Reconcile focus text color; confirm verifier reads correct node |
| InputBoxLine | state-matrix | Medium | Disabled `opacity:0.4` double-dims; Figma disabled uses full-strength grey tokens, opacity 1 | **NEW** | Remove `opacity:0.4`; drive disabled border/text from grey tokens |
| InputBoxLine | color-token | Medium | XL textarea text fill drifts in several light states (#141414 vs #9e9e9e etc.) | **NEW** | Add XL filled/error/hover/text-count text-color rules per Figma greys |

*(Low-severity confirmed items — Badge transparent-border/gap, Breadcrumb/Radio/Switch composite-layer extract gaps, coverage-quality notes, InputNumber spinner tabindex/opacity, etc. — are real but cosmetic/tracking-only; folded into Drift Classes counts below, not re-listed individually.)*

### Filtered false-positives (skeptic refuted, `real:false`)

- **Notification — light-theme background drift (High):** REFUTED. Auditor read a *dark* variant's `--bg-layer4` and misattributed it to light theme. Light Figma binds `--bg-layer2` #f8f8f8, which code matches exactly. Suggested fix would *introduce* drift.
- **PillStatus — label text not status-accent (High):** REFUTED. Compared canonical code against a non-canonical product mockup; no published library Pill exists; ships code-first under registered BRIDGE-MOCKUP-003.
- **PromptMessage — success-bg not theme-aware (High):** REFUTED. `--prompt-success-bg` is a *deliberately* documented fixed token (phase-6.2a plan); team chose not to let success bg flip with theme.
- **SelectBoxFilled — disabled text not status-aware (High):** REFUTED. Code already branches value (`--text-tips`) vs placeholder (`--text-disabled`), and both resolve to exactly the Figma greys; only a token-naming alias, not a value drift.
- **TabItem — active font-weight 700 unbacked (High):** REFUTED. The MCP render cache + documented Phase X.4.2 decision show Figma *does* bold active White-Line/Text/Green-Line via `Roboto:Bold`; the tokenized JSON dropped the per-variant font-family override. Removing 700 would regress.

---

## 3. Drift classes (systemic patterns)

Counts are confirmed findings only (skeptic-refuted excluded). Several are recurring *systemic* classes, not one-offs.

| Class | Confirmed count | High | Pattern / systemic signal |
|-------|-----------------|------|---------------------------|
| **a11y** | 9 | 7 | **Most systemic.** Inline-`<span @click>` / `<div @click>` / `<label @click.prevent>` affordances with no role/tabindex/keyboard recur across CheckBox, Radio, DropDownListSelect, SelectBox×3, InputBox×2, TabItem. One drift class (CANONICAL-013/016) explains nearly all of it. |
| **color-token** | 11 | 6 | Two sub-patterns: (a) **light-theme token substitution** — `--text-heading`/`--text-2` used where Figma binds a theme-stable token (StepItem, Steps, Slider, PromptMessage); (b) **disable-state token miss** (Button green/gray). Highly mechanical, low-effort fixes. |
| **geometry** | 9 | 5 | Two sub-patterns: (a) **feature-icon padding hack** 32/30px vs Figma 12px (InputBox×2, EXTRACT-007); (b) **2× value errors** (Table 16↔8px, content gap 8↔4px, TabItem 0↔8px, TabList 2↔1px, SelectBoxLine 13↔14px). |
| **state-matrix** | 5 | 2 | **Disabled `opacity:0.4` fabrication** dominates — recurs identically across SelectBoxBase/Filled/Line (same `SelectBoxBase.vue:234-239` rule), plus InputBoxLine. One fix kills 4 findings. |
| **composition-reuse** | 4 | 1 | Hand-rolled affordances that should reuse canonical children: Multi-check in DropDownList/SelectBox (CANONICAL-012), StepItem completed-marker check, FormItem field fallbacks. |
| **composite-layer** | 0 confirmed drifts | 0 | **Clean.** Every audited component had single-layer (length≤1) fills/strokes/text.fills — the EXTRACT-008/CANONICAL-018 multi-layer composite class did **not** trigger anywhere. DropDownList's CANONICAL-018 item is an in-flight pipeline-confirm, not new drift. |
| **coverage** | 3 surfaces | — | InputBoxBase (internal base), PillCounter, PillStatus uncovered; Button 8-set family covered only by url-link approximation (CANONICAL-017). |

**Headline systemic insight:** Two fixes neutralize ~13 findings — (1) deleting `opacity:0.4` from `SelectBoxBase.vue:234-239` clears 3-4 state-matrix drifts; (2) the CANONICAL-013/016 keyboard-affordance fix pattern clears 7 of the 9 a11y findings.

---

## 4. Re-prioritization proposal (v0.7 → v1.0)

Ordered by the project principle: **goal-alignment > dependency-unblock > automation/standardization > quick-wins** (NOT effort/cost). The bridge's whole purpose is code 1:1 reproduces Figma, so *visual* drift in shipped themes ranks above a11y polish, and *systemic standardization* ranks above one-off cosmetics.

### Tier 1 — Goal-alignment: visible Figma mismatch in shipped themes (v0.7)
Direct violations of "1:1 Figma" that a user *sees*:

1. **Chart card chrome [NEW]** — bg/padding/radius entirely missing; the most-broken single component (low→). Two findings.
2. **Disabled `opacity:0.4` fabrication [NEW]** — fix once in `SelectBoxBase.vue:234-239`; clears SelectBoxBase/Filled/Line + addresses InputBoxLine analog. High systemic ROI.
3. **Light-theme token substitution [mostly NEW]** — StepItem + Steps (`--text-heading`→`--text-primary-btn`), Slider value (`--text-2`→`--color-grey-5`, absorbed by **CANONICAL-019**), PromptMessage status text (**CANONICAL-017** note). Black-text-on-active in light theme is a clear visible bug.
4. **FormItem error label red [CANONICAL-019]** — error state visibly wrong; already owned.
5. **Progress theme-fork regression [reopen progress-pseudo-theme divergence]** — re-apply T1b *and* add the verifyHint grep to pre-commit (prevents recurrence — also serves Tier 3).

### Tier 2 — Dependency-unblock: extract pipeline + base coverage (v0.8)
These gate verification of everything downstream:

6. **EXTRACT-007 variant-child geometry** — unblocks InputBox feature-padding verdicts (32/30px vs 12px), CheckBox/Radio circle paints, Switch live/disabled tracks, Pagination/TopBar/Tooltip/PopupBox child geometry. Confirm overlay-vs-inset intent with design (the InputBoxLine finding shows Figma pH genuinely = 12, so some of this is *real* drift the gap is masking, not pure schema noise).
7. **CANONICAL-017 coverage gaps** — add InputBoxFilled/Line→base, and a representative-variant sampling strategy for the Button 8-set family, so its color/geometry matrix stops being audit-invisible. Fix the BreadcrumbItem empty-slot and Rating/StepItem textTargetSelector blind spots that hide color drift.

### Tier 3 — Automation / standardization (v0.9)
8. **a11y affordance standardization [CANONICAL-013 + CANONICAL-016]** — single remediation pattern (real `<button>`/`role` + keyboard) across CheckBox, Radio, DropDownList, SelectBox×3, InputBox×2, TabItem. Standardize one fix, apply 9×.
9. **Composition-reuse standardization [CANONICAL-012]** — route Multi-check through CheckBox.vue everywhere (DropDownList, SelectBox); StepItem marker.
10. **Geometry 2× token-discipline sweep [mix NEW]** — Table padding/gap, TabItem gap, TabList divider, SelectBoxLine M font-size, Notification 1.2px border. Mechanical; batch into one standardization pass.

### Tier 4 — Quick-wins / cleanup (v1.0)
11. Button disable text tokens [NEW]; InputBoxLine XL textarea text colors [NEW]; InputBoxFilled focus-text [NEW]; InputNumber spinner tabindex/opacity [NEW]; Chart default size; cosmetic low-severity notes.

### NEW backlog entries required (no existing ID absorbs them)
- Chart card chrome (geometry + color) — 1 entry
- Disabled `opacity:0.4` fabrication (Select family + InputBoxLine) — 1 entry, cross-component
- StepItem / Steps active-state `--text-heading`→`--text-primary-btn` (light theme) — 1 entry
- StepItem completed-marker composition-reuse — folds into CANONICAL-012 if a primitive exists, else NEW
- Button disable text tokens (green + gray) — 1 entry
- Notification 1.2px border + bespoke `--notification-bg-*` token review — 1 entry
- Table content gap 4px; TabItem gap 8px; TabList divider 1px; SelectBoxLine M 14px — geometry 2× sweep, 1 entry
- InputBoxLine XL textarea text colors; InputBoxFilled focus text — 1 entry
- **Progress regression** — reopen `progress-pseudo-theme-accepted-divergence` + add pre-commit guard
- PillStatus hue-remap + PillCounter registry entry — 1 entry (blocked on Figma SoT decision)

### Existing IDs that absorb findings
- **CANONICAL-013:** SelectBox×3 chip/clear, InputBox×2 clear (a11y)
- **CANONICAL-016:** DropDownListSelect rows, TabItem (listbox/tab roles + keyboard)
- **CANONICAL-012:** DropDownList/SelectBox Multi-check reuse
- **CANONICAL-017:** PromptMessage status text, Button 8-set coverage, BreadcrumbItem slot fixture, coverage blind spots
- **CANONICAL-019:** FormItem error label, Slider value-color + width, Table cell padding
- **EXTRACT-007:** InputBox feature/clear padding, CheckBox/Radio/Switch/Progress subnode paints, Pagination/Tooltip/PopupBox/TopBar child geometry

---

## 5. Coverage gap

Components whose fidelity verdict here comes from **manual diff, not the deterministic verifier** (or where coverage exists but is depth/selector-limited):

**No render-verification coverage at all (audit-invisible):**
- **InputBoxBase** — internal canonical base; not in `COMPONENT_TARGETS`, 0 hits in `render-verification.report.json`. Its 6-axis matrix and both wrappers riding on it are unverified. *Fix:* add InputBoxFilled/Line (which fully delegate) to targets. Also undocumented as an internal base — needs a divergences.md entry (alongside SelectBoxBase).
- **PillCounter** — `grep` empty in manifest + report. Code-first, no Figma SoT (BRIDGE-MOCKUP-003); coverage would be self-referential until Figma ships Pill/Counter. `overallFidelity: unknown` by design.
- **PillStatus (+ base Pill)** — not in manifest/report; only truth is a product mockup file. Its high-severity hue-mapping drift would not be caught by the existing pipeline.

**Covered but materially blind (verifier passes trivially or can't see the drift):**
- **Button 8-set family (~3200 variants)** — only the 4-variant url-link set is targeted, and even *that* mis-wires `textTargetSelector: '.btn__label'` against a bare `<slot/>` (no such node), so the only Button text check silently no-ops. The primary color/geometry matrix is effectively audit-invisible. *(CANONICAL-017)*
- **Chart** — covered, but the verifier reads only the empty `.tvu-chart` root and passes trivially while the entire card chrome (bg/padding/radius) is missing; canvas content is not pixel-introspectable.
- **BreadcrumbItem** — covered, but empty `<slot/>` gives the label 0 dimensions, so `textFillHex` reads root `#000000` and all four state colors are effectively unverified (standing FAIL, classified B_RESIDUAL_SCHEMA_GAP).
- **Rating / StepItem / Steps** — covered for geometry, but `textTargetSelector` cannot follow the icon/state text swap, so star color (Rating) and active/completed title color (StepItem/Steps — the exact light-theme drift found manually) pass unchecked.
- **EXTRACT-007 family (InputBox, CheckBox, Radio, Switch, Slider, Progress, Pagination, Tooltip, PopupBox, TopBar, FormItem):** root-level coverage exists but variant-child geometry/paint (circle/dot fills, track colors, feature-icon insets, inner gaps) is not extracted — those values rest on manual inference, not the verifier.

**Net:** of 34 components, 3 surfaces are fully invisible, Button's main family is functionally invisible, and ~10 more are verified only at root level. The deterministic verifier currently anchors high-confidence verdicts on the wrappers; the *interactive sub-elements and child geometry across the library remain manual-diff territory* until EXTRACT-007 and the CANONICAL-017 selector/coverage work land.
