Design Tokens home Audit · v3.2 · Final

Component Audit · v3.2

Gap analysis between the 34 component groups on the Components page (fysBbW10LRJUNgisaBKXJo4050:50253) and the locked v3.2 token architecture. Captures token-binding violations, WCAG 2.2 AA conformance issues, structural duplication, and a five-phase remediation plan. Read-only audit — no Figma writes were made.

2026-04-30 · sourced via Figma MCP get_metadata, get_screenshot, get_variable_defs

01Scope & method

v3.2 landed in the variable layer on 2026-04-29. The components on this page were authored against earlier token states (v3.0 / v3.1 + remote libraries). This audit measures the gap between component reality today and the v3.2 invariants, plus WCAG 2.2 AA conformance.

Two locked invariants drive the token-side findings:

Probed via Figma MCP: full canvas metadata (179 KB XML) plus visual screenshots and resolved variable bindings for the ten highest-impact component groups (Buttons, Inputs, Toggle, Modal Dialogs, Bottom sheets, Navigation, Cards, Headers, List Items, Pills, Banners, Notifications).

02The 34 component groups

Top-level frames on canvas 4050:50253, listed roughly by audit priority.

34 component groups · node IDs · why audited
#GroupNodeAudit focus
01Buttons4050:64556CTA radius, contrast, focus, target size
02Inputs4050:56823Labels, errors, placeholder contrast, autocomplete
03Select4050:57100Combobox semantics
04Toggle4050:57210Target size, contrast, non-color affordance
05Modal Dialogs4050:63769Focus trap, dismiss, sizing
06Bottom sheets4050:62808Drag affordance, dismiss, mobile target
07Navigation4050:62261Active-state contrast, chrome interaction
08Cards4050:53708Reuse pattern, spacing scale
09Headers4050:56038Header active-state pattern
10Compliance Headers4050:56198Regulatory chrome variants
11List Items4050:61483Touch targets, dividers, OLD/NEW dedup
12Pills · Badges4050:55890 · 4050:55972Contrast on tinted bg
13Banners4050:61205Inline messaging, dismissal
14Notifications4050:55720Live-region intent, contrast on color
15Hero Sections4050:57235Layout, type scale
16Footer · Full-width4050:57970 · 4050:58084Landmark, link contrast
17Search4050:56708Combobox / listbox semantics
18Accordion4050:61436Disclosure semantics
19Pagination4050:57684aria-current="page", target size
20Tournament4050:52648Domain pattern reuse
21Calendar4050:62600Date-grid keyboard model
22Progress indicators4050:53162Status announcement
23Effects · Spacers · Informational4050:61012 · 4050:61076 · 4050:61128Scaffolding · move to Foundations page
24Product Filter4050:57890Multi-select pattern
25Balance4050:57773Number formatting
26iOS Elements4050:58251Platform chrome
27Compliance Header Token System Components4050:64853Documentation frames · move
28Originals Title Animated · Originals Logo4050:64921 · 4050:64942Branded micro-asset
29Table of contents4050:53028Page-internal navigation

03Component structure

3.1 · OLD/NEW duplication shipped live

3.2 · Variant axes that should be properties, not separate components

3.3 · Cards — uncontrolled growth

The Cards group is 20,017 px tall — the largest group on the page. Game-tile / promo-tile patterns have grown organically into many one-off variants. Without consolidation, downstream consumers cannot reliably pick one "card" — every product team forks a new variant.

3.4 · Slot vs. instance

Notifications mixes the "container with leading icon + dismiss + optional CTA" pattern across 3 colors × 4 variants instead of using one component with Status = info|success|warning|error + Action = none|inline|stacked + Dismissible = true|false properties. Tournament rebuilds list-item/header patterns instead of instancing List Items / Tournament / Header — the file already has the master, but Tournament also has hand-built copies.

3.5 · Documentation frames mixed with components

Compliance Header Token System Components and Originals Title Animated / Originals Logo are documentation/asset frames mixed into the component canvas. They are not reusable components — should move to a separate "Tokens & Assets" page. Spacers and Effects are scaffolding; they belong in a foundations page beside the token tables.

3.6 · Naming inconsistency

Multiple naming conventions co-exist on the canvas: List Item / Tournament / Header (forward slash, title case) · Variant =Main (space-equals) · User=No, Initials=Yes (CSV-style) · Whiteframe/Heading 5 new (legacy product prefix). Pick one convention and rename the rest.

04Spacing & layout

4.1 · Off-grid radii — violates strict 4-point invariant

Radii currently bound on the components page
TokenCurrentv3.2 target
Extras/CTA Radius3scale/4
Extras/Input Radius3scale/4
Base/Radius 33scale/4
Extras/Box Radius6scale/4 or scale/8
Extras/Card Radius12scale/12
Extras/Pill Radius100scale/96 (or scale/160 for true pill)

~41 affected radius aliases were already remapped in the variable layer on 2026-04-29; the components on this page still show the old values because component bindings have not been swept.

4.2 · Off-grid typography — applies to type too

Typography rounds up per the locked rule.

Type sizes currently bound
TokenCurrentv3.2 target (round up)
Font/Size/X Small8scale/8 ✓ (deprecate as semantic body)
Font/Size/Small11scale/12
Font/Line Height/Small14scale/16
Font/Size/Caption12scale/12
Font/Size/Label15scale/16
Font/Size/Heading 516scale/16
Font/Size/Heading 417scale/20
Font/Size/Heading 319scale/20
Font/Size/Heading 222scale/24
Font/Size/Heading 128scale/28
Font/Line Height/Heading 136scale/40
Font/Size/Display 250scale/48
Font/Line Height/Display 260scale/64

4.3 · Magic-number spacing

token('space.100') = 8 appears bound on most components — Atlassian-style legacy naming. v3.2 already exposes Semantic/spacing/{xxs..3xl}. Components should consume Semantic spacing tokens, not the literal.

4.4 · Layout alignment

05Token application — v3.2 conformance

5.1 · Components still bind legacy library tokens

Every probed component (Buttons, Inputs, Modals, Headers, Notifications, List Items) returns variable names from the old library:

Main/Text  ·  Main/Primary  ·  Main/Background  ·  Main/On-Primary
Main/Text-Disabled  ·  Main/Text-Supportive  ·  Main/Disabled CTA
Generic/Stroke 10  ·  Generic/Stroke 30  ·  Generic/Dividers
Generic/Input-Border  ·  Generic/Overlay-80  ·  Generic/IOS-Bar
Lobby Header/Background  ·  Lobby Header/Surface-Primary  ·  Lobby Header/Active-Selected
Lobby Header/On-Surface-Primary  ·  Lobby Header/CTA  ·  Lobby Header/Text  ...
Extras/CTA Radius  ·  Extras/Input Radius  ·  Extras/Pill Radius
Extras/Box Radius  ·  Extras/Card Radius
Font/Size/*  ·  Font/Line Height/*  ·  Font/Family/*  ·  Font/Weight/*
Whiteframe/*   # Museo Sans family — clearly stale
Base/Base White  ·  Base/Base Black  ·  Base/Base White 10  ·  Base/Radius 3
System/Error  ·  System/Success  ·  System/Informative  ·  System/Orange  ·  System/Purple

None of these match the v3.2 namespace (Global/*, Modes/*, Theme/*, Semantic/*, Component Packs/*). The variable rename + restructure landed in collections; component bindings did not follow.

5.2 · v3.2 tokens that exist but are not consumed

This is the largest single gap: the v3.2 abstractions exist but nothing on the component page reaches them.

5.3 · Three font families coexist

Open Sauce One (current) · Museo Sans (legacy Whiteframe/*) · SF Pro Text (iOS native — fine for iOS Elements only). Museo Sans appears inside Inputs and Modal Dialogs — these are NOT iOS-only contexts. Audit and remove Museo Sans bindings everywhere except where intentionally legacy.

06Raw-hex violations

Tokens currently carrying raw hex outside the two allowed homes (Global / Modes DNA).

Raw-hex violations — current binding to v3.2 alias target
TokenHexShould alias
Generic/Stroke 10#0000001aGlobal/color/black-alpha/10
Generic/Stroke 30#0000004dGlobal/color/black-alpha/30
Generic/Input-Border#b7b8bcModes/color/neutral/300
Generic/Dividers#b7b8bcModes/color/neutral/300
Base/Base White 10#ffffff1aGlobal/color/white-alpha/10
Base/Base White 30#ffffff4dGlobal/color/white-alpha/30
Base/Base Black 10#0000001aGlobal/color/black-alpha/10
Base/Base-12#000000Global/color/black
System/Error#e92636Global/color/system/error/500
System/Success#00c76bGlobal/color/system/success/500
System/Informative#0082ebGlobal/color/system/info/500
System/Orange#f9ae41Global/color/system/warning/500
System/Purple#a864ffno v3.2 home — add tertiary slot or move to brand DNA
Lobby Header/Active-Selected#1c1d20Modes/color/neutral/900 → primary/500 (per rule)
Lobby Header/Surface-Primary#5b5c60Modes/color/neutral/600
Lobby Header/Surface-1#98989cModes/color/neutral/400
Main/Disabled CTA#98989cModes/color/neutral/400
Main/On-Disabled CTA#b7b8bcModes/color/neutral/300
Main/Text-Disabled#b7b8bcModes/color/neutral/300
Main/Text-Supportive#98989cModes/color/neutral/400
Main/Text#1c1d20Modes/color/neutral/900
Default/SystemBlue/Light#007AFFiOS native — leave or add Modes/color/native/ios-blue

07WCAG 2.2 AA accessibility

Computed from the hex values returned by get_variable_defs. AA thresholds: 4.5:1 normal text, 3:1 large text (≥18pt or 14pt bold) and UI components.

08Contrast failures

Computed contrast ratios for foreground × background pairings
PairingForegroundBackgroundRatioVerdict
Disabled CTA label#b7b8bc#98989c1.27:1Critical
Notification / Success body#f8f9fd#00c76b2.05:1Critical
Notification / Info (small)#f8f9fd#0082eb3.59:1High Large only
Notification / Warning#f8f9fd#f9ae411.93:1Critical
Notification / Purple promo#f8f9fd#a864ff2.94:1High Large only
Text-Supportive#98989c#f8f9fd2.74:1High Helper text fails
Lobby Header / On-Surface-1#f8f9fd#98989c2.74:1High White text on light grey
Header / Active-Selected on Surface-Primary#1c1d20#5b5c603.74:1Medium Visually muddy
Header / On-Surface-Primary white#f8f9fd#5b5c605.83:1✓ Passes
Notification / Error body#f8f9fd#e926365.04:1✓ Passes
Body text on page#1c1d20#f8f9fd15.5:1✓ Passes AAA

Notification colour fix

Every failing surface either needs (a) the foreground swapped to neutral/900 (dark) for warm/light bodies (success, warning, info, purple) or (b) the surface stop deepened to a 700/800 shade. Error red is the only AA-passing surface today.

Text size minimums

09Touch / pointer target sizes

WCAG 2.2 SC 2.5.8 (AA) requires interactive targets ≥ 24×24 CSS px (with exceptions for inline links and dense lists). Mobile lift to 44×44 (Apple HIG) / 48 dp (Material).

Estimated hit areas from screenshots — measurement still required
ElementEstimatedVerdict
Toggle drag thumb~16 pxHigh below 24×24
Notification dismiss ×~16 pxHigh wrap in 24×24 hit area
Modal dismiss ×~16 pxHigh wrap in 24×24 hit area
Micro Button~24×24 or belowHigh measurement required
Page Header chevrons~24×24Medium borderline
Pagination numeralssmallMedium measurement required
Button / Default~40 px✓ Passes
List Item rows (mobile)~44 px✓ Passes

10Focus visibility

Components do not show a defined focus state in any screenshot probed. There is no Theme/focus-ring or Global/focus/* token in the variable defs returned. Without a focus token, every interactive primitive (Buttons, Pills, Inputs, Toggle, List Items, Pagination, Tabs) lacks a designed :focus-visible style.

WCAG 2.2 adds 2.4.11 Focus Not Obscured (Minimum) and 2.4.13 Focus Appearance at AA — focus must be ≥ 2 px outline at 3:1 contrast against both component and adjacent background. None of this is currently encoded.

Recommended focus tokens

  • Global/focus/ring-colorModes/color/border/focusModes/color/primary/500
  • Global/focus/ring-widthGlobal/scale/4 (used as 2px stroke)
  • Global/focus/ring-offsetGlobal/scale/4

Add an explicit State = Focus variant to every interactive component. Verify the focus ring resolves to 3:1 against both surface and adjacent neighbours per brand mode.

Color-only state encoding

Component-specific accessibility

11Responsiveness

11.1 · No breakpoint-driven layout

Variable defs returned zero Global/breakpoint/* consumption across all probed components. v3.2 declares breakpoint/{mobile, tablet, desktop, desktop-wide} but they are unused.

11.2 · Responsive variants are separate components

11.3 · Mobile-first patterns missing breakpoint adaptation

11.4 · Dark mode

v3.2 ships Light + Dark in Theme. Components on this canvas are presented in Light only. There is no Dark-mode preview frame to validate token resolution. Add a Dark variant frame per major component group, or a single "Component canvas (Dark)" page.

12Severity summary

Issue ranking across the 34 component groups
#IssueSeverityAffected groups
ANotification text-on-color fails AA (success, warning, info, purple)CriticalNotifications, Banners, inline alerts
BComponents still bind legacy library tokens (Main/*, Generic/*, Lobby Header/*, Extras/*, Whiteframe/*) — v3.2 namespace not adoptedCriticalAll 34 groups
COff-grid radii (3, 6, 100) and typography (11, 15, 17, 19, 22, 36, 50, 60)HighAll groups via shared tokens
DRaw hex in non-Global / non-DNA tokensHighAll groups
ENo focus tokens / focus states defined (WCAG 2.4.7 / 2.4.11 / 2.4.13)HighAll interactive components
FTouch target sizes below 24×24 (Toggle thumb, Notification ×, Modal ×, Micro Button)HighToggle, Notifications, Modals, Buttons
GOLD/NEW component duplication shippedHighList Items, Navigation
HHeaders split into 4 components for one logical patternMediumHeaders
IPills split into 4 components for one familyMediumPills
JBanners has 8+ size variants of same templateMediumBanners
KCards group has uncontrolled growth (20,017 px)MediumCards
LFont/Size/X Small = 8 px retained as semantic sizeMediumHeaders, Footer (fine print)
MThree font families coexist outside iOS scopeMediumInputs, Modal Dialogs
NNo breakpoint tokens consumedMediumAll groups
ONo Dark-mode preview framesMediumAll groups
PDocumentation/asset frames mixed with componentsLowCompliance Header Token System, Originals Title/Logo, Spacers, Effects
QNaming conventions inconsistentLowAll groups
RColor-only state encodingMediumToggle, Inputs, Pagination

13Header active-state rule

Locked rule

Modes/color/header/active-selected must not alias the same neutral stop as Modes/color/header/surface-primary for a brand. Pin active-selected to primary/500 instead — this prevents invisible nav active state on coloured headers (Snabarre blue, Mobilespin red).

Today's bindings on the Components page:

Two stops apart — passes the rule by hex, but contrast is weak (3.74:1) so the active state is visually muddy. Re-point active-selected to primary/500 per the rule.

14Phased remediation

Phase 0

Triage

  1. Snapshot the page (page export PNG + every variable rebuilt to JSON) before any rewire.
  2. Lock the page to read-only for non-DS editors during sweep.
  3. Confirm with team which "OLD" components can be retired.
Phase 1

Token rewire — mechanical, ID-preserving

  1. Rename legacy tokens to v3.2 names in-place to preserve binding IDs:
    • Main/TextModes/color/text/primary (alias neutral/900)
    • Main/PrimaryModes/color/brand/primary (alias primary/500)
    • Main/BackgroundModes/color/surface/page
    • Generic/Stroke 10Modes/color/border/subtle (alias Global/color/black-alpha/10)
    • Generic/Stroke 30Modes/color/border/strong (alias Global/color/black-alpha/30)
    • Lobby Header/*Modes/color/header/*
    • Extras/CTA Radius → alias Global/scale/4
    • Extras/Box Radius → alias Global/scale/4 (or 8) — confirm with team
    • Extras/Pill Radius → alias Global/scale/96
    • Font/Size/Heading {1..5} → alias Global/typography/size/{N} rounded up
    • System/Error → alias Global/color/system/error/500 · same for success/info/warning (orange→warning, purple → tertiary or new home)
  2. Variable IDs persist across renames, so component instances pick up new names automatically — no per-component edit needed for the rename pass.
  3. Run getLocalVariablesAsync audit to confirm zero raw hex outside Global / Modes DNA.
Phase 2

Accessibility fixes

  1. Notification colors: re-bind text on success / warning / info / purple to neutral/900. Verify contrast ≥ 4.5:1 per pairing.
  2. Add focus tokens (Global/focus/ring-color, /ring-width, /ring-offset) and apply a State = Focus variant on every interactive component.
  3. Touch targets: enforce 24×24 minimum on Toggle thumb, Notification ×, Modal ×, Pagination items, Micro Button. Mobile lift to 44×44.
  4. Disabled CTA: choose either lower opacity of normal CTA OR re-bind label to a higher-contrast neutral. Document that disabled state is not focusable.
  5. Toggle: add a non-color affordance (✓ icon in thumb when on, OR distinct knob position + visible OFF/ON label).
  6. X Small (8 px): deprecate from semantic typography. Migrate consumers to Small (12 px) minimum.
Phase 3

Component consolidation

  1. Headers: collapse 4 components into 1 with Layout + Size props.
  2. Pills: collapse 4 components into 1 with Size + Leading + Trailing + State props.
  3. Banners: collapse 8 size variants into 1 with Size = sm|md|lg|xl|fullwidth.
  4. Notifications: 1 component with Status × Action × Dismissible props instead of 12 frames.
  5. Cards: split into named domain families (Card / Game Tile, / Promo, / Payment) and dedupe within each family.
  6. List Items / Payment Method: retire OLD when NEW is signed off.
  7. Navigation / Sub Tabs: dedupe duplicate chip rows.
  8. Move documentation frames (Compliance Header Token System Components, Originals Title/Logo, Spacers, Effects) to a separate "Foundations" page.
Phase 4

Responsiveness

  1. Add Size = Mobile | Tablet | Desktop variant to Headers, Hero Sections, Footer, Cards, Banners.
  2. Add per-group Dark-mode preview frame (or a single mirror page set to Theme = Dark).
  3. Replace fixed-width canvases (Cards 1864 px) with breakpoint-sized frames anchored to breakpoint/desktop = 1024 / desktop-wide = 1440.
  4. Add long-string snapshots (German DE, "longest brand name", "URL with query string") to Headers, Banners, Notifications, Hero.
Phase 5

Hygiene

  1. Standardise naming to Domain / Component / Variant. Variant property names use PascalCase, variant values use Title Case.
  2. Adopt Semantic/spacing/* and Semantic/icon-size/* everywhere space.100 and free-FLOAT icon sizes appear.
  3. Adopt Global/elevation/0..4 Effect Styles in place of inline Effect(type: DROP_SHADOW, …).
  4. Strip Whiteframe/* (Museo Sans) bindings outside scoped legacy areas.

15Auto-layout & padding deep audit

Second-pass structural audit run via the Figma Plugin API on 2026-04-30, after the user asked us to look harder at paddings, auto-layout, and element grouping. We walked the 11 highest-impact component masters and tagged every frame for layout-mode, padding values, item-spacing, sizing modes, asymmetry, group-vs-frame, and nesting depth. The aggregate is below; the table beneath it has per-group counts.

15.1 · Aggregate across 11 masters

The audit walked ~70,683 nodes across 28,695 frame-like containers in: Buttons, Inputs, Toggle, Modal Dialogs, Navigation, Cards, Headers, List Items, Banners, Pills, Notifications.

Aggregate findings — 11 component masters
FindingCount% of framesSeverityWCAG link
Frames with layoutMode = NONE (no auto-layout)9,363~33%Critical1.3.2 Meaningful Sequence — z-order export
Frames with off-grid padding (not a 4-pt scale stop)4,373~15%Highv3.2 invariant · 1.4.10 Reflow risk
Frames with off-grid itemSpacing5,022~18%Highv3.2 invariant
Frames with asymmetric padding (top≠bottom or left≠right)4,120~14%Medium2.4.7 Focus Visible · 2.4.11 Focus Not Obscured
Adjacent interactive children with itemSpacing = 0989Critical2.5.8 Target Size (Minimum)
GROUP nodes (vs Frames with auto-layout)800High1.3.2 Meaningful Sequence
GROUP nodes nested inside an auto-layout parent429Critical1.3.2 + responsive break

15.2 · Per-master counts

Layout findings — 11 component masters
Master Walked Frames No auto-layout Off-grid pad Off-grid gap Asym pad Zero-gap interactive Groups Group-in-AL Max depth
Cards24,6279,8323,6801,5811,9071,59529131031016
Navigation4,7182,036401186160297135912
Headers2,2009302209458112513311
Modal Dialogs1,9257201451511536424333314
List Items1,809771203110257246202012
Buttons1,23453714324298046969
Banners1,191513113176302211252515
Notifications7222905672536167710
Pills5792494220224116559
Inputs4962154133781823312
Toggle541753410667

15.3 · Off-grid padding values that recur most

The top off-grid padding values across the 6 deeply-counted masters (Cards · Headers · List Items · Banners · Pills · Notifications). Bulk-fixable by editing the underlying token alias once each.

Recurring off-grid padding values
Value (px)OccurrencesClosest 4-pt stopWhere
62,411scale/4 or scale/8Cards (1,952), Headers (147), List Items (42), Banners (222), Pills (40), Notifications (8)
21,153scale/4Cards (783), Banners (308), Headers (58), Pills (2), Notifications (3)
15870scale/16Cards "Hover Overlay" (15/15/15/15)
5784scale/4Cards "Info Tag" (2/5/2/5), List Items "Number Badge" (0/5/0/5)
3164scale/4Cards (120), Banners (44) — Inputs "Payment Method" (3/0/3/0)
60121scale/56 or scale/64Cards (111), Banners (10) — Banner content padding (12/60/12/60)
1860+scale/16 or scale/20List Items "Bonus Widget", Pills "Filter Item / Filter Pill" (18/18/18/18) — also recurs in Inputs Credit Card / Amount Field
3657scale/32 or scale/40Cards (53), Headers (4)
1013+scale/8 or scale/12Headers Filter chips (6/8/6/8), Inputs "Container" (10/0/10/0)
1440scale/12 or scale/16List Items, iOS Status Bar (12/14/12/21)
100~4scale/96Modal Dialogs "Campaign Modal" (100/100/100/100)
26~6scale/24 or scale/28Modal "Content" (24/26/24/26) — mixes 24 and 26

15.4 · Off-grid itemSpacing values that recur most

Recurring off-grid item-spacing values
Value (px)OccurrencesClosest 4-pt stopNotes
10608+scale/8 or scale/12Most common gap across every master
2826scale/4Cards-heavy
6675scale/4 or scale/8All masters
50~25scale/48Section gaps in Buttons, Inputs, Toggle, Pills, Banners
30~10scale/28 or scale/32Pills, Inputs
9626N/A — hackHeaders — abuse of itemSpacing as "push to far edge" instead of primaryAxisAlignItems = SPACE_BETWEEN
14222N/A — hackList Items — same anti-pattern
37~5scale/32 or scale/40Toggle, Pills, Banners, Notifications, Headers

15.5 · Specific concerning patterns

Toggle component variants have no auto-layout

All three Toggle states (Active=False, Disabled=False, Active=True, Disabled=False, Active=False, Disabled=True) are layoutMode = NONE. The thumb is positioned absolutely inside the track. This is why hit-target measurement is fragile and why the Toggle screenshot shows inconsistent visual alignment between states. Migrate each state to HORIZONTAL auto-layout with primaryAxisAlignItems = MIN on the off state and MAX on the on state.

Cards is structurally compromised

9,832 frames, 3,680 without auto-layout (37%), max nesting depth 16, 310 GROUPs all sitting inside auto-layout parents (the worst-of-both pattern), 1,595 frames with asymmetric padding, 1,907 frames with off-grid itemSpacing. The most common off-grid pad is 6 (1,952 occurrences). Rebuild the game-tile family from scratch on auto-layout, with explicit Card / Game Tile, Card / Promo, Card / Payment bases — do not try to in-place fix.

Modal Dialogs depth-14 nesting

"Campaign Modal" uses [100, 100, 100, 100] padding (off-grid). "Content" mixes 24 with 26. Six "Checckbox old" GROUPs (sic — typo) at depth 11 indicate an in-flight migration that was abandoned. Modal Dialogs is the second-worst structural offender after Cards.

Recurring 6/12/6/12 padding on "_ System Component / Label"

A shared base component appears across Buttons, Inputs, Toggle, Headers, List Items, Notifications with padding [6, 12, 6, 12]. The vertical 6 is off-grid; the horizontal 12 is on-grid. Single fix: edit the base component once, change vertical padding to scale/8; every consumer inherits.

Recurring 18/18/18/18 padding

Used on Inputs "Credit Card" / "Amount Field", Pills "Filter Pill" / "Filter Item", List Items "Bonus Widget", Buttons "Social Button". 18 px is off-grid; round up to scale/20 or down to scale/16 per family. Treat as an auditable token: introduce Semantic/spacing/card-inset aliasing the chosen stop.

Anti-pattern: itemSpacing = 962 and 142

Headers and List Items use enormous itemSpacing values to push children to opposite ends of a row. The correct pattern is primaryAxisAlignItems = SPACE_BETWEEN with layoutSizingHorizontal = FILL. Replace 28 instances total. They will break responsive layouts at any width below the hard-coded gap.

"Payment Left" / "Payment Right" asymmetric padding

A widely-used pattern: [0, 0, 0, 12] on the left half and [0, 12, 0, 0] on the right half — used to put a 12 px gap between two halves of a payment-method row. Replace with a single parent at itemSpacing = 12 and zero padding on the children. Reduces wrapper count and removes the asymmetry signal.

"Error/Helper Message" left-only padding

Inputs uses [0, 0, 0, 12] on every error/helper line — a 12 px indent so the message aligns under the input value, not the label. Functionally correct but encoded as asymmetric padding. Move to a stack-level auto-layout with paddingLeft = 12 on the message frame, OR prefix the input + helper as a single column with consistent left padding.

15.6 · GROUP node patterns to dismantle

For every GROUP inside auto-layout: convert to a Frame with the same auto-layout direction. Where the GROUP wraps a single instance, unwrap it. The "Old" / "Checckbox old" GROUPs are leftover migration artefacts — delete after the new variant is signed off.

15.7 · Adjacent interactive children with zero gap (WCAG 2.5.8)

989 instances across the 11 masters where two interactive-looking children (INSTANCE nodes or items named button · cta · chip · tab · item · link · toggle) sit at itemSpacing = 0. WCAG 2.2 SC 2.5.8 (Target Size — Minimum, AA) allows targets smaller than 24×24 only if there is at least 24 px of spacing between centers — zero gap with small tap targets fails by both prongs.

Worst offenders:

15.8 · Refactor priorities for the layout pass

  1. Toggle — convert 3 component variants to auto-layout. Tiny scope, immediate quality lift, blocks the larger Toggle target-size fix.
  2. "_ System Component / Label" base — change vertical padding from 6 → 8. Single edit, ripples to ~6 masters.
  3. "Payment Left" / "Payment Right" pattern — collapse asymmetric children into itemSpacing on the parent. Bulk-applicable across Buttons, Inputs, Modal Dialogs.
  4. "itemSpacing = 962 / 142" hacks — replace with SPACE_BETWEEN. 28 instances.
  5. GROUP-inside-auto-layout — 429 conversions to Frame. Cards is 310 of those; treat Cards as its own project.
  6. Recurring 18/18/18/18 and 15/15/15/15 — introduce a Semantic alias and re-point.
  7. Modal "Campaign Modal" — replace [100, 100, 100, 100] with scale/96 or rebuild on a viewport-relative wrapper. Single component.
  8. Cards rebuild — last; do this from scratch on auto-layout, splitting into named families. Do not try to fix in place.

Method: Plugin API walk via use_figma, figma.skipInvisibleInstanceChildren = true, BFS over each master root, recording layoutMode, paddingTop/Right/Bottom/Left, itemSpacing, layoutSizingHorizontal/Vertical, type, depth, and parent-LM context. Off-grid = not in the canonical 20-stop scale set. Read-only — no mutations.

16Verification plan

After each phase, verify with read-only Plugin API (use_figma in dry-run mode):

  1. Token sweep auditgetLocalVariablesAsync() filtered to COLOR; assert every var outside Global/* and Modes/color/{primary,secondary,neutral,tertiary}/* resolves to VARIABLE_ALIAS in every mode. Zero raw hex tolerated.
  2. Scale audit — assert every Global/scale/* value is in the canonical 20-stop set; assert every dimension consumer aliases a scale/N.
  3. Contrast audit — for every (text-token, surface-token) pair used on the page, compute APCA + WCAG 2.x ratios; assert ≥ 4.5:1 for normal text, ≥ 3:1 for large/UI.
  4. Target-size audit — walk every interactive component; assert hit area ≥ 24×24 (desktop) and ≥ 44×44 (mobile variants).
  5. Focus state audit — assert every interactive component has a State = Focus variant or :focus-visible styling layer with the focus tokens applied.
  6. Variant audit — assert no two siblings on the page differ only by Size or Status (must be variants, not separate components).
  7. Visual regression — render a per-brand snapshot of each component group at start and after each phase; diff against pixel baseline. Tolerate ≤ 2 px drift on radii (3 → 4 round-up).
  8. Dark-mode resolution — render the same component grid under Theme = Dark; assert no token resolves to its Light value.

End-to-end success criteria

  • Every component on the canvas binds only v3.2 tokens.
  • Zero raw hex outside Global / Modes DNA.
  • WCAG 2.2 AA conformance verified across all 34 groups in both themes.
  • getLocalVariablesAsync audit returns 0 violations.
  • Component count reduced by an estimated ~30% via consolidation.