Skip to content

Design-parity remediation plan

Método reutilizable: las lecciones de este esfuerzo (para el repo móvil y proyectos futuros) están destiladas en terminales-solution/docs/design/design-parity-playbook.md.

Purpose: the order of operations to close the 153 audited gaps between Figma and the app with the least total work. Companion to README.md. The core idea: almost every per-screen difference is a symptom of a handful of systemic defects, so we fix layers, not screens — tokens → theme → atoms → patterns — and re-run the audit after each layer to watch notes flip to ✅ instead of hand-fixing them.

Progress (2026-07-30): Phases 0–2 done and re-audited; Phases 3–4 largely delivered. Since the 2026-07-23 note below, the molecule/organism residuals and the screen patterns were worked as real tickets — the admin list/screen build-outs (#460–#474), the full-page detail views (#466–#471), the tender-picker rework (#472) and the filled-glyph residuals (#474) all merged 2026-07-25, followed by a round-2 polish pass across login, dashboard, terminal and shifts on 2026-07-28 and a fresh gallery re-capture. What remains of Phase 4 is the PO decision list (dialog-vs-page and similar), now carried as web issues #498–#501. Re-run pnpm screenshots + pnpm docs:parity before trusting any per-screen verdict below.

Progress (2026-07-23): Phases 0, 1, 2 are DONE and re-audited. DESIGN.md is wired as the token source, the MUI theme carries the ~15 systemic overrides, the atoms are fixed, and the showcase sx hacks are promoted to real props. The re-audit against the post-fix captures moved the per-screen verdict from 96 ❌ / 7 ⚠️ / 0 ✅ to 37 ❌ / 58 ⚠️ / 6 ✅ — most screens dropped from major to minor. What remains is captured as the residual backlog in the audit’s Hallazgos sistémicos section (11 items, measured, with culprit files) — that list supersedes the original 18 findings below, which are kept for the record. Phases 3–5 below are the remaining work.

The audit’s hardest open question — which values are the truth (#0052CC vs #0057B8, the Tailwind-ish grey ramp vs the published tokens) — turns out to be already answered:

terminales-solution/docs/design/DESIGN.md is the canonical spec, and the Figma frames were painted from it. Every value the audit measured in the Figma renders appears in DESIGN.md verbatim:

Role DESIGN.md Measured in Figma renders App today (tokens.local.json)
primary #0052CC #0052CC #0057B8 ❌
primary-strong (pressed) #003D99 #003D99 #003A8F ❌
primary-soft #E6EEFA #E6EEFA (role missing) ❌
text-primary / secondary / tertiary #1F2933 / #6B7280 / #9CA3AF same #242B35 / #384252 / #8292AA ❌
border #E5E7EB #E5E7EB #C8CED9 ❌
success / warning / danger + softs #10B981/#ECFDF5 #F59E0B/#FFFBEB #EF4444/#FEF2F2 same mains ✓, softs unwired
info / info-soft #3B82F6 / #EFF6FF same (role missing) ❌
Radius scale sm 8 / md 12 / lg 16 / xl 24 matches (dialog 16, buttons 12…) flat borderRadius: 8 ❌

This resolves systemic findings #3, #6, #8, #12, #13 as one decision instead of five component-by-component debates. It also reframes finding #13: the “legacy Tailwind palette” is actually the canonical palette; the “published tokens” are the drift.

Meanwhile the app’s token pipeline is broken: pnpm tokens:build calls scripts/generate-tokens.js, which no longer exists — src/styles/tokens.local.ts is a frozen artifact of a deleted generator, and src/styles/tokens.ts (the “Figma Token Engine” export) is stale (its CTA primary is grey #384252). Three competing sources, none of them DESIGN.md.

⚠️ One confirmation needed from the PO/design before Phase 0 lands: is #0057B8 anywhere a deliberate brand decision (client brand book, AngelPay requirement)? All repo evidence says DESIGN.md/#0052CC is the intent, but this is the one call that must not be silently wrong, because everything downstream inherits it.

  1. Fix at the highest layer that causes the defect. Never patch a screen for what a token causes; never patch a component for what a theme override causes. The audit’s leverage map (below) assigns every systemic finding to its layer.
  2. Measure, don’t eyeball. Every fix’s definition of done is a re-captured screenshot whose sampled hex/geometry matches the token — the same imgtool sampler the audit used.
  3. Re-audit instead of re-annotating. After each phase: pnpm screenshots → re-run the comparison for affected entries → notes flip to ✅ mechanically. The gallery is the progress dashboard.
  4. Dimensions: web frames win over DESIGN.md. DESIGN.md is brand-neutral and mobile-leaning (input height 56, button 52); the web frames use denser desktop metrics (~40–44 fields). Colors, type scale, radius and spacing come from DESIGN.md; web component dimensions come from the 1440px web frames (design/web/).
  5. Don’t restyle what has a pending structural decision. 13 screens have a dialog-vs-full-page mismatch (see Phase 4) — styling them before the PO decides the container is wasted work.

Phase 0 — Source of truth + pipeline repair (small, unblocks everything)

Section titled “Phase 0 — Source of truth + pipeline repair (small, unblocks everything)”
  1. Confirm the #0052CC decision (above).
  2. One generator, one source. Rewrite scripts/generate-tokens.js (or a new scripts/generate-tokens.mjs) to read DESIGN.md (it’s YAML frontmatter + sections — already machine-readable, and already the format of the design.md spec the frontend should stay compatible with) and emit src/styles/tokens.generated.ts: colors (light + a derived/authored dark scheme), type scale, radius scale, spacing scale.
  3. Extend DESIGN.md with the roles the audit proved missing — it’s the canonical file, so gaps get fixed there: action/disabled colors (finding #10), data-viz series (#18), shadows/ elevation (it has none today), dark scheme (today tokens.local.json just duplicates light).
  4. Retire the drift: tokens.local.json, src/styles/tokens.local.ts and the stale parts of src/styles/tokens.ts stop being imported; src/ui/theme/tokens.ts reads only the generated file. pnpm tokens:build works again and CI can verify freshness (same pattern as verify:api-types).

Kills systemic findings #3, #6, #8, #12, #13, and the token halves of #10/#18.

Phase 1 — MUI theme as the enforcement point (the big lever)

Section titled “Phase 1 — MUI theme as the enforcement point (the big lever)”

This is where the audit’s findings concentrate: 11 of 18 systemic defects are missing or wrong theme overrides, not component bugs. Work through src/ui/theme/theme.ts override by override, verifying each against its playground capture before moving on:

Override Fixes finding What
palette (primary, info, action, divider, text) #3 #8 #10 #12 wire generated tokens; add info, action.disabled*
Typography color codemod #1 color="text.secondary" → textSecondary in the 18 files (MUI v9 no-op) + an ESLint no-restricted-syntax rule so it can’t come back
MuiTextField / MuiOutlinedInput / MuiInputBase / MuiInputLabel #2 white fill, #E5E7EB border, web field height (40–44 per web frames), radius md
MuiButton #4 #5 remove the contained boxShadow (it silently defeats disableElevation), add size="small", wire the hover token
MuiCheckbox / MuiRadio / MuiSwitch #11 designed geometry (rounded 20×20 checkbox, 44×24 track + white 18px thumb), #D1D5DB unchecked stroke
MuiCard #7 flat + hairline 1px #E5E7EB, radius lg
MuiAlert #9 explicit soft backgrounds from tokens; stop MUI’s lighten()/darken() recomputation
MuiChip / MuiTooltip / MuiSkeleton #16 designed height/font, opaque #1F2933 tooltip, #F0F4F8 skeleton
MuiTableCell / MuiTableHead (Data Table note) header fill #F7F9FC, #E5E7EB row borders, designed row heights
MuiDialog / MuiDialogTitle/Content/Actions (Confirm Dialog note) radius lg, 24px action padding, designed gaps
shape → radius scale usage #14 components pick sm/md/lg from the scale instead of one flat 8

Kills #1, #2, #4, #5, #7, #9, #10, #11, #14, #16 and most of every screen-level color/spacing note. This phase alone should flip the majority of the 96 ❌.

Phase 2 — Atom repairs + API promotion (small; most “atom” work died in Phase 1)

Section titled “Phase 2 — Atom repairs + API promotion (small; most “atom” work died in Phase 1)”

Real component defects the theme can’t fix:

  • Avatar: soft variant (primary-soft fill + primary initials — today inverted), L-size font step.
  • Icon: default size + expose the 16/20/24/32/40 scale in the API, not just the showcase (#15).
  • Button: loading state must keep the active fill + white spinner + visible label (today it collapses to disabled-grey).
  • Banner: solid status glyphs, radius sm, 14px/700 title.
  • StatusBadge: use the *-soft tokens that already exist instead of alpha(main, 0.12).
  • Icon registry — no gift glyph (#369): the EmptyState reference illustration is a gift glyph; the registry’s nearest product glyph (Tag) stands in (EmptyStateShowcase.tsx). Add a gift glyph or accept Tag. (Gap confirmed + traced by the #617 showcase sweep.)
  • Button — no :active/pressed treatment (gap DS-2) (#359): the DS Button defines no distinct pressed fill, so ButtonShowcase pins Pressed to the same fill as Hover. Add a pressed token or accept Pressed≡Hover. (#617.)
  • KeypadKey — Pressed is fill-only (#371): the pressed capture uses the atom’s real selected prop (brand-tint fill), which matches the Figma frame — no shadow/scale/border delta. Confirmed fill-only against Figma; no change needed unless the design adds depth cues. (#617.)
  • Promote showcase sx hacks into real props (#17): BalanceCard tone, IconButton variants, ProviderTile active, ListRow trailing, SummaryRow tone, Toast dark. Until these are API, parity can never be captured honestly.

Work down the remaining ❌ notes that survive Phases 0–2 (re-audit first — don’t fix from stale notes): ConfirmDialog layout (centered column, equal-width actions, tinted icon container), PageHeader type hierarchy (2.0× title/subtitle ratio), chart series colors from the new data-viz tokens, DataTable row-hover/selected treatment.

Phase 4 — Screen patterns + the PO decision list

Section titled “Phase 4 — Screen patterns + the PO decision list”

The consolidation the plan assumed is mostly already true: the DS DataTable backs 34 screens vs 2 legacy GenericTable usages, and GenericForm backs 20 forms. So “fix once, apply everywhere” is the architecture already — Phase 1’s table/dialog overrides restyle nearly all screens at once. Remaining screen work:

  1. Migrate the 2 GenericTable stragglers; retire src/ui/Generics (the legacy-component audit already owns this).
  2. The 13 structural mismatches need a product decision each — Figma designs a full page, the app implements a dialog over the list (W21 producto, W30 detalle de venta, W33 corte Z, W38/39 empleado/invitar, W56 tabs, W58, W46b, Categoría/Impuesto alta…). Per screen: keep the dialog (cheaper, revise Figma) or build the page (parity with design). Until decided, these screens’ style notes stay frozen.
  3. Forms without a Figma frame follow the house standard (useDialog + GenericForm + DS atoms); forms with a frame follow the frame.

Phase 5 — Collection hygiene (parallel to everything)

Section titled “Phase 5 — Collection hygiene (parallel to everything)”

The 37 ⛔ entries: fix the /reset-password capture (add ?userId&secret to its target), add targets for reachable-but-uncaptured states (W02 MFA), export the missing Figma nodes (W12, the four CLI frames), and re-run pnpm screenshots + pnpm docs:parity.

Terminal window
# per fix
pnpm screenshots --grep <slug> # recapture just the affected surface
node <scratchpad>/imgtool.mjs sample … # sampled hex == token?
pnpm docs:parity # notes preserved, gallery rebuilt
# per phase
pnpm screenshots && re-run the audit workflow for affected entries → notes flip to ✅

Anti-regression: the ESLint rule from Phase 1, verify:tokens freshness check in CI, and the byte-stable screenshot harness (git diff on docs/design-parity/screenshots/ as a visual- regression signal).

Compatibility: design.md spec + white-label

Section titled “Compatibility: design.md spec + white-label”
  • design.md spec: DESIGN.md already follows it. Phase 0 makes the app actually consume it, which is the spec’s whole point — one machine-readable file drives the product. Future themes = another DESIGN.md-shaped document per brand.
  • White-label: mergeMerchantTheme (src/ui/theme/WhiteLabelThemeProvider.tsx) layers merchant overrides (primary, accent, fontFamily, mode) over the base theme via createTheme(base, overrides) — untouched by this plan. The base theme becomes derived from DESIGN.md; merchant overrides keep applying on top at runtime. One caution for Phase 1: derive tint roles (primary-soft) from primary at theme-build time, or merchants overriding primary will keep the default blue tint.
Systemic finding Killed in
#3 #6 #8 #12 #13 Phase 0 (one source of truth)
#1 #2 #4 #5 #7 #9 #10 #11 #14 #16 Phase 1 (theme overrides)
#15 #17 Phase 2 (atom APIs)
#18 Phase 0 (tokens) + Phase 3 (charts wiring)

Order of execution: 0 → 1 → 2 → (re-audit) → 3 → 4, with 5 running in parallel whenever.