Skip to content

Improve theme token coverage to fix washed-out light themes #3346

@gregpriday

Description

@gregpriday

Summary

The built-in theme system derives many secondary tokens automatically in createCanopyTokens, using formulas that were calibrated against dark themes. Light themes produce washed-out, low-contrast results because these derivations don't account for the fundamentally different luminance relationships in light color modes. More tokens need to be independently tunable per-theme.

Problem Statement

The theme system was built during Daintree development, a dark theme. The createCanopyTokens helper in shared/theme/themes.ts fills in a large set of secondary tokens from a small required set — borders, overlays, accent variants, and a few surface-adjacent tokens are all derived automatically. This works well on dark backgrounds where the alpha-layering math produces meaningful contrast. On light themes, the same formulas collapse:

  • border-subtle, border-strong, border-divider are derived via withAlpha(overlayTone, alpha) where overlayTone is text-primary. On light themes this produces faint borders that are nearly invisible against pale surfaces, causing the sidebar and worktree panels to look flat and undifferentiated.
  • overlay-* tokens use fixed dark/light alpha multipliers (0.02–0.10 for dark, 0.04–0.14 for light). These produce very different effective contrasts depending on how far the surface color is from pure white or black. Light themes with warm or tinted surfaces get lower effective contrast than expected.
  • accent-soft and accent-muted are derived from accent-primary at fixed opacities. The Canopy green accent (#3F9366) works well on dark backgrounds but feels over-saturated and visually heavy on light warm surfaces.
  • terminal-black, terminal-white, and terminal-bright-black fall back to surface-canvas and text-primary — reasonable for dark but causing incorrect terminal contrast on light themes.
  • The activity-* tokens (activity-active, activity-working, activity-waiting) are not derived but most light themes copy the dark defaults (neon greens and ambers), which don't adapt to the lighter, softer look expected from light color modes.
  • category-* label colors use static oklch values that are tuned for dark-on-dark but not for visibility on light surfaces — they lack sufficient contrast on pale backgrounds.

The result is that every light theme looks washed out and low-contrast, particularly in the worktree sidebar, panel borders, and agent state indicators. This is a systemic issue, not a per-theme one: light themes need a richer set of explicitly-specified tokens.

Relevant code:

Desired Behavior

Light themes should have enough explicitly-defined token slots that they can achieve proper visual hierarchy without relying on the dark-calibrated derivation formulas. Specifically:

  • Border tokens (border-subtle, border-strong, border-divider) should be overridable with explicit hex/oklch values in each light theme definition, rather than always derived.
  • Overlay tokens should either be theme-specific or the derivation formula should account for the actual surface luminance so that pale-surface light themes get the same effective contrast as mid-tone dark themes.
  • accent-soft and accent-muted should be overridable per theme to accommodate themes where the global green accent doesn't blend well.
  • The light theme E2E smoke test (core-light-theme-smoke.spec.ts) should measure more contrast points — sidebar vs. canvas, worktree card background vs. header text, panel vs. grid background — so that regressions in these areas are caught automatically.
  • A theme author guide (or inline code comments) should document which tokens must be explicitly set for light themes to achieve good hierarchy vs. which are safe to leave as derived.

Context

This is the foundation issue for a full light theme redesign. All five light themes (Bondi, Svalbard, Atacama, Serengeti, Hokkaido) are being redesigned in parallel issues, and each redesign will rely on having the right set of overridable tokens. Fixing the derivation system first ensures each theme redesign doesn't require hacking around the helper.

The terminal color scheme matching in PR #3345 is relevant context: each app theme now has a designated terminal palette. The terminal tokens in themes.ts must remain consistent with those paired terminal schemes after any refactor.

Acceptance Criteria

  • Light theme definitions in themes.ts can explicitly set border-tier tokens (border-subtle, border-strong, border-divider) and have those values respected — no override by the derivation.
  • The derivation formulas for overlay tokens produce the same effective visual contrast on a typical light surface as on a typical dark surface (or the tokens are simply required to be explicit for light themes).
  • The light-theme E2E smoke test measures at least 5 contrast pairs: toolbar text vs. toolbar background, sidebar card background vs. canvas, worktree section header vs. card body, panel background vs. grid, and border default vs. adjacent surface.
  • All five existing light themes pass the expanded contrast checks without modification (i.e., the fix improves the system, not the individual themes — individual theme color work is tracked in separate issues).
  • No change to dark themes: all dark theme E2E tests pass unmodified.

Edge Cases & Risks

  • The withAlpha helper outputs rgba() for hex colors and color-mix() for oklch/other — the derivation fix must handle both formats consistently.
  • Custom imported themes (JSON) that currently rely on derived tokens must continue to work; any new required fields must be backwards-compatible or have sensible fallbacks.
  • The LEGACY_THEME_TOKEN_ALIASES map and the INTERNAL_LIGHT_FALLBACK_SCHEME must stay consistent with any token restructuring.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions