Skip to content

Theme switcher: pick accent + surface colors (#87)#90

Merged
thalida merged 18 commits into
mainfrom
feat/issue-87-theme-switcher
Jul 12, 2026
Merged

Theme switcher: pick accent + surface colors (#87)#90
thalida merged 18 commits into
mainfrom
feat/issue-87-theme-switcher

Conversation

@thalida

@thalida thalida commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Closes #87

Theme switcher: accent + surface presets

A Settings Appearance tab (renamed from Preview) to pick an accent preset and a surface preset, persisted across sessions. Applied by overriding root custom properties via data-cc-accent / data-cc-surface on <html>, so every color-mix derivation in tokens.css cascades automatically — no per-component changes.

Design decisions (brainstormed first, per the issue)

  • Curated presets, not freeform — so contrast can be verified per preset.
  • Scene + gem/layer brand tokens stay fixed — the 3D scene reads no CSS vars; theming is 2D-CSS-only. Out of scope: scene material theming, per-project memory, light mode.
  • Dark-only — surface presets hold the lightness ladder (app .151 / chrome .162 / sidebar .181); accents hold the default blue's L/C profile and vary hue. All OKLCH.

How it works

  • Two persistedSignal stores (ACCENT_THEME / SURFACE_THEME), mirroring syntaxTheme.ts (autosave, no draft/Save step).
  • A module-scope effect() sets the data-cc-* attributes before first render (imported in main.tsx ahead of render(); persistedSignal hydrates synchronously) — flash-free, no extra script. Default preset sets no attribute, so tokens.css :root stands.
  • themes.css holds one [data-cc-*] block per preset (including explicit blue/cool blocks so the default swatch chips preview their own color rather than inheriting the active theme).
  • Swatch pickers are ARIA radiogroups (roving tabindex; arrow keys move focus + selection).

Presets: accents — Amber, Green, Cyan, Blue (default), Purple, Pink (rainbow order). Surfaces — Cool (default), Neutral, Green, Warm.

Contrast: analytically verified — surfaces hold the lightness ladder (contrast drift ≤0.23% vs default), accents hold L/C; amber (L=0.78) computes ~9.72:1 on --cc-bg-app, passing AA. A live in-app eyeball pass across all 6×4 combinations is still recommended (cyan is slightly outside sRGB and browser-gamut-mapped).

Also on this branch (owner-directed UI polish)

  • Left sidebar defaults to 1/3 viewport (matching the right sidebar).
  • Active activity-bar icon: accent glyph + faint fill, no left border.
  • Shortcut key/mouse badges use neutral surface colors instead of the accent.
  • Modal: soft backdrop blur, even (non-downward) shadow, faint border.
  • New-project form: kind-aware example placeholders.
  • Overview: "Updated" → "Last Updated", exact date + relative age on both it and Founded; meta list is a max-content grid (no hardcoded label-column width).
  • Refactor: centralized the pane content inset on a new --cc-pane-inset token + shared .pane-inset class (one knob; every pane body, row inset, bleed margin, empty-state, and ActionsBar keys off it). Inset tightened 16 → 12px.

Testing

  • just lint clean; npx vitest run2510/2510 pass (new tests for the theme stores, preset↔CSS coupling, and the swatch radiogroup incl. arrow-key focus).

🤖 Generated with Claude Code

thalida and others added 16 commits July 12, 2026 10:05
Renames the Preview subtab to Appearance and adds a radiogroup swatch
picker for ACCENT_THEME/SURFACE_THEME (Task 1/2 stores + presets),
mounted alongside the existing syntax-theme picker. Also fixes a
pre-existing tsconfig gap where tests/styles/themePresets.test.ts
(Task 2) used Node builtins without being excluded from the browser
tsconfig, which broke `just lint`'s typecheck step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ocus

- default (blue/cool) swatch chips inherited the active theme; give them
  explicit themes.css blocks so each chip previews its own preset
- reorder accents by hue (Amber, Green, Cyan, Blue, Purple, Pink)
- arrow keys now move focus with selection (WAI-ARIA radiogroup)
- refresh stale "Preview" tab-name comments to "Appearance"

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mirrors the right sidebar's default; keeps the 280/600px drag clamps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t border

Selected sidebar tab now shows via the glyph taking the accent color instead
of a left border + soft-accent fill.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ut keys

- restore the soft fill behind the selected activity-bar icon (accent glyph + fill)
- shortcut key/mouse badges use surface + neutral border instead of the accent
  palette, so they read as chrome and don't retint with the accent

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- pane-header used 16px left / 12px right, so a leading focus button read as
  more inset than the trailing close button; make horizontal padding symmetric
- lighten the active activity-bar icon fill (6% vs the standard 10% tint)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… match

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Repo URL -> https://github.com/owner/repo; Local Path -> ~/projects/my-repo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Updated -> Last Updated, shown as an exact date (like Founded) with the
  relative age appended (both rows now show date + "N years ago")
- meta list is a max-content grid so the label column sizes to its content
  (no hardcoded 64px rail; "Last Updated" no longer wraps)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ace-6)

Introduce a semantic --cc-pane-inset token (var(--cc-space-6)) as the single
knob for the distance from a pane edge to its content, and a shared .pane-inset
class. Every panel body (Settings, Overview, Legend, README, Commit, Street)
now uses the class instead of redeclaring padding: var(--cc-space-7), and the
coupled consumers (.row inset, .row--bleed margins, empty-state, ActionsBar,
search) point at the same token so everything stays aligned. Inset goes 16 -> 12px.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ring the modal with an equal 0-offset shadow (slightly darker) instead of a
downward drop, and add a 1px --cc-border-subtle border so it separates from the
blurred backdrop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thalida thalida linked an issue Jul 12, 2026 that may be closed by this pull request
thalida and others added 2 commits July 12, 2026 11:42
The surface chips previewed only near-black --cc-bg-app, so all four read alike.
Show the whole surface ladder (app -> chrome -> sidebar) as a gradient; the
lighter sidebar end surfaces the hue so presets are distinguishable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each swatch is now a chip-over-label tile. The visible name is the honest way
to distinguish presets, especially surfaces whose near-black tints can't read
in a small chip. Active tile: accent outline ring + brightened label.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thalida thalida merged commit 35d344e into main Jul 12, 2026
1 check passed
@thalida thalida deleted the feat/issue-87-theme-switcher branch July 12, 2026 16:02
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.

Theme switcher: pick accent + surface colors

1 participant