Seven phases under one umbrella
A complete visual redesign across 18 catalogued surfaces, guided by a token migration plan, oklch contrast math, and a seven-phase base branch umbrella.
A complete visual redesign replaced the game’s typography and color palette across 18 catalogued surfaces, from buttons and game board tiles to tooltips and brand assets. Rather than landing dozens of isolated style edits directly on main, the work was structured around a dedicated token-migration decision record and sequenced through a single base branch umbrella.
Token replacement without parallel generations#
The first structural decision in the migration plan was to replace existing --color-* and --font-* custom properties directly in src/app.css as each phase landed. The repo did not introduce a parallel --color-v3-* token set to let old and new themes coexist, continuing the in-place custom property pattern set during design-system-v2-first-attempt. The codebase is a single CSS file with one running instance, so parallel token generations between phases would only ever sit as dead code. Between phases, the live app on the base branch temporarily mixed old and new surfaces, which was accepted as a normal transient state rather than gated behind feature flags.
--font-display: 'Baloo 2', display, system-ui, sans-serif;
--font-ui: 'Nunito', system-ui, sans-serif;
--font-weight-extrabold: 800;
Space Grotesk was retired in favor of Baloo 2 for headings and Nunito for body copy and buttons, while Space Mono was retained for score and game-log data. Both new fonts carry the SIL Open Font License 1.1, matching the existing font licenses and requiring no new firewall rules.
Contrast math over raw hex#
The mockup provided flat hex values, but those values were treated as a reference palette rather than raw colors to copy into CSS. Checking the mockup’s raw hex against documented WCAG contrast requirements revealed four real contrast failures before any code was committed. In particular, the raw hex for banana fruit text against the light grid background produced a 2.74:1 contrast ratio, failing the 3:1 WCAG 1.4.11 threshold and repeating an exact bug previously fixed in premature-norwegian.
dark text-muted #8f93a8 vs surface-raised #2b2c3a: 4.53:1 (needs >=4.8:1)
light text-muted #6b7280 vs surface #ffffff: 4.83:1 (needs >=5.3:1)
dark accent #e2456a vs white text: 3.97:1 (needs >=4.5:1)
banana/golden #a98a2e vs light grid #E8EAF0: 2.74:1 (needs >=3:1)
Re-expressing colors in oklch() allowed tuning lightness values specifically for perceptual uniformity while satisfying every contrast floor documented in src/app.css.
Seven phases, one base branch#
The implementation proceeded across seven grouped phase tickets: foundations, board tiles, notifications, duel flow, result panels, home menu, and brand assets. Each phase landed as its own focused PR targeting the base branch docs/design-system-redesign-1067 rather than main, following the same umbrella pattern previously used in norwegian-done-properly.
main only sees this work once, when the base PR merges.
When all seven phases were complete on the base branch, the umbrella merged to main in a single merge commit (a6cf9d0c), closing all seven phase issues at once without exposing main to intermediate breakage.
Catching contrast on press#
A ten-minute follow-up pass addressed backspace visual treatment and replaced remaining literal color: white declarations with --color-text-on-accent. Audit testing during that follow-up surfaced a second-order bug: pressed keys hardcoded white text against a light-theme active background (94% lightness), yielding a 1.23:1 contrast ratio where pressed key labels became invisible. Removing the hardcoded color allowed pressed keys to inherit proper theme text tokens while keeping submit-key overrides intact.