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:paritybefore 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
sxhacks 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 discovery that shapes everything
Section titled “The discovery that shapes everything”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
#0057B8anywhere a deliberate brand decision (client brand book, AngelPay requirement)? All repo evidence says DESIGN.md/#0052CCis the intent, but this is the one call that must not be silently wrong, because everything downstream inherits it.
Guiding principles
Section titled “Guiding principles”- 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.
- Measure, don’t eyeball. Every fix’s definition of done is a re-captured screenshot whose
sampled hex/geometry matches the token — the same
imgtoolsampler the audit used. - 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. - 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/). - 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)”- Confirm the
#0052CCdecision (above). - One generator, one source. Rewrite
scripts/generate-tokens.js(or a newscripts/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 emitsrc/styles/tokens.generated.ts: colors (light + a derived/authored dark scheme), type scale, radius scale, spacing scale. - Extend DESIGN.md with the roles the audit proved missing — it’s the canonical file, so gaps
get fixed there:
action/disabledcolors (finding #10), data-viz series (#18), shadows/ elevation (it has none today), dark scheme (todaytokens.local.jsonjust duplicates light). - Retire the drift:
tokens.local.json,src/styles/tokens.local.tsand the stale parts ofsrc/styles/tokens.tsstop being imported;src/ui/theme/tokens.tsreads only the generated file.pnpm tokens:buildworks again and CI can verify freshness (same pattern asverify: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-softfill + 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:
loadingstate 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
*-softtokens that already exist instead ofalpha(main, 0.12). - Icon registry — no
giftglyph (#369): the EmptyState reference illustration is a gift glyph; the registry’s nearest product glyph (Tag) stands in (EmptyStateShowcase.tsx). Add agiftglyph or acceptTag. (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, soButtonShowcasepins 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
selectedprop (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
sxhacks into real props (#17): BalanceCardtone, IconButton variants, ProviderTile active, ListRow trailing, SummaryRow tone, Toast dark. Until these are API, parity can never be captured honestly.
Phase 3 — Molecule/organism residuals
Section titled “Phase 3 — Molecule/organism residuals”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:
- Migrate the 2
GenericTablestragglers; retiresrc/ui/Generics(the legacy-component audit already owns this). - 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.
- 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.
Verification loop (the efficiency engine)
Section titled “Verification loop (the efficiency engine)”# per fixpnpm screenshots --grep <slug> # recapture just the affected surfacenode <scratchpad>/imgtool.mjs sample … # sampled hex == token?pnpm docs:parity # notes preserved, gallery rebuilt# per phasepnpm 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 viacreateTheme(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) fromprimaryat theme-build time, or merchants overridingprimarywill keep the default blue tint.
Leverage map
Section titled “Leverage map”| 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.