Skip to content

fix(terminal): floor ANSI colors to 3:1 contrast on light themes#7

Merged
slowdini merged 1 commit into
devfrom
fix/terminal-contrast-floor
Jun 23, 2026
Merged

fix(terminal): floor ANSI colors to 3:1 contrast on light themes#7
slowdini merged 1 commit into
devfrom
fix/terminal-contrast-floor

Conversation

@slowdini

Copy link
Copy Markdown
Owner

Problem

Several terminal ANSI colors are illegible on the light-mode backgrounds (#FFF8F5 pastel-light, #FAF6FA neon-light). Terminal colors were pulled straight from the palette with no readability adjustment — the same values that work as sparse, often-bold editor syntax tokens fail as terminal text. Measured WCAG contrast against #FFF8F5: yellow ~1.6:1, cyan ~1.7:1, green ~2.1:1, blue/magenta ~2.2:1, red ~2.9:1 — all below a 3:1 floor.

A second, directional bug compounded it: brightHex/bright/bright8 always lightened the bright variants. Lightening helps on a dark background but pushes colors toward a light one, so bright green/yellow/cyan were the faintest of all (~1.4:1) — the pale prompt/TUI text users saw.

Dark themes were unaffected, which matches what users observed in the wild.

Fix

  • generator/colorUtils.ts — add relativeLuminance, contrastRatio, and ensureContrast. ensureContrast nudges only a color's lightness (preserving hue/saturation) just enough to reach a minimum contrast against the background, darkening on a light bg and lightening on a dark one. It's a no-op when the color already passes.
  • All four terminal targets (wezterm.ts, vscode.ts, zed.ts, neovim.ts) — apply a 3:1 contrast floor to the chromatic ANSI text colors (normal + bright). "Bright" is now direction-aware (lighter on dark, darker on light) before flooring. Structural black/white slots are left untouched.

3:1 was chosen to fix legibility while preserving the synthwave/vaporwave palette as much as possible; the nudge is minimal, so colors that already pass keep their vividness.

Result

Every chromatic terminal color (normal + bright) now clears 3:1 on the light backgrounds:

pastel-light  bg=#FFF8F5
ansi  : red 3.01  grn 3.00  yel 3.01  blu 3.02  mag 3.01  cyn 3.02
bright: red 3.72  grn 3.00  yel 3.01  blu 4.41  mag 3.33  cyn 3.02
neon-light    bg=#FAF6FA
ansi  : red 3.89  grn 3.00  yel 3.02  blu 5.03  mag 4.15  cyn 3.01
bright: red 5.35  grn 3.30  yel 3.01  blu 7.41  mag 5.85  cyn 3.16

Dark themes are byte-for-byte unchanged — the floor is a no-op where colors already pass, so the dark wezterm/vscode output files weren't touched.

Tests

20 new tests (RED→GREEN), covering the WCAG utilities and asserting per-target that light-mode chromatic colors meet 3:1 while dark-mode values stay pinned to their existing hexes.

  • bun test → 111 pass, 0 fail
  • bun run check (biome + tsc --noEmit) → clean

Out of scope

The Starship powerline prompt is a separate code path (hardcodes the dark palette, draws fg:crust on colored segments) and isn't driven by the ANSI mapping. Plain ANSI-colored shell text benefits from this fix; making the Starship segments themselves mode-aware is a follow-up.

🤖 Generated with Claude Code

Terminal ANSI colors were resolved straight from the palette with no
readability adjustment. On the near-white light backgrounds this left
the chromatic colors illegible (yellow ~1.6:1, cyan ~1.7:1, green
~2.1:1, ...). The "bright" variants were worse: brightHex always
*lightened*, which improves contrast on a dark background but pushes
colors toward a light one.

Add WCAG contrast utilities (relativeLuminance, contrastRatio,
ensureContrast) to colorUtils and apply a 3:1 contrast floor to the
chromatic ANSI text colors across all four terminal targets (wezterm,
vscode, zed, neovim). ensureContrast nudges only lightness, preserving
hue/saturation, and is a no-op when a color already passes -- so dark
themes are byte-for-byte unchanged. "Bright" is now direction-aware:
lighter on dark backgrounds, darker on light ones.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@slowdini slowdini merged commit f60eb8e into dev Jun 23, 2026
1 check passed
@slowdini slowdini deleted the fix/terminal-contrast-floor branch June 23, 2026 02:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant