Skip to content

Add multi-theme system with light, dark, ocean, forest, and high-contrast options - #111

Open
asherp wants to merge 1 commit into
stagingfrom
claude/appearance-change-issue-c0ag84
Open

Add multi-theme system with light, dark, ocean, forest, and high-contrast options#111
asherp wants to merge 1 commit into
stagingfrom
claude/appearance-change-issue-c0ag84

Conversation

@asherp

@asherp asherp commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Summary

Replaces the binary dark mode toggle with a comprehensive multi-theme system supporting five distinct color themes: light, dark, high-contrast, ocean, and forest. The implementation uses CSS custom properties (tokens) for theme-agnostic styling, with dark mode retaining its legacy .dark-mode class for backward compatibility while new themes use a generic .themed class driven by token values.

Key Changes

  • New theme system architecture (styles/themes.css):

    • Defines a generic token-based surface layer that applies to body.themed
    • Provides five complete theme token sets (light, dark, high-contrast, ocean, forest)
    • Light theme uses default tokens in variables.css
    • Dark theme keeps legacy dark-mode.css overrides for compatibility
    • Other themes use purely token-driven styling via body.themed
  • CSS custom properties (styles/variables.css):

    • Introduced comprehensive color token system: --color-primary, --color-bg, --color-surface, --color-text, --color-border, --color-sidebar, --color-danger, etc.
    • Light theme values serve as defaults; themes override via :root[data-theme="..."]
  • Theme management (js/app.js):

    • Replaced setDarkMode() with setTheme(theme) that handles all five themes
    • Added getSavedTheme() to resolve active theme, honoring legacy darkMode preference for backward compatibility
    • Updated event listeners from dark mode toggle to theme picker with data-theme-value attributes
    • Maintains darkMode localStorage key in sync for older code paths
    • Reflects selection state in Settings picker via aria-checked attributes
  • Pre-paint theme application (index.html):

    • Added inline <head> script that applies saved theme to <html> before first paint, eliminating flash of wrong theme
    • Implements same backward-compatibility logic as getSavedTheme()
  • Settings UI (index.html):

    • Replaced dark mode toggle button with accessible theme picker using ARIA radio buttons
    • Five visual swatches with preview gradients and theme names
    • Swatches styled with hover effects and selection indicators
  • Stylesheet updates (across contacts.css, forms.css, dm.css, email.css, buttons.css, navigation.css, loading.css, relay.css, qr-scanner.css, dark-mode.css):

    • Replaced hardcoded color values (#667eea, #764ba2, etc.) with CSS custom property references
    • Updated gradients to use var(--color-primary-gradient)
    • Ensures all themes automatically inherit proper colors from token system
  • Utility updates (js/utils.js, js/contacts-service.js, js/email-service.js):

    • Changed dark mode detection from class-based to theme-aware
    • Updated to read colors from CSS custom properties instead of hardcoded values
    • Ensures email rendering and form styling respect active theme

Implementation Details

  • Backward compatibility: Legacy darkMode localStorage preference is automatically migrated to the new theme key; setDarkMode() and toggleDarkMode() remain as shims
  • No flash of unstyled content: Inline script in <head> applies theme before DOM renders, preventing visual flicker
  • Accessibility: Theme picker uses semantic ARIA radio buttons with proper aria-checked states
  • Token-driven design: New themes require only token definitions; visual layer automatically applies via .themed class
  • Dark mode preservation: Dark theme retains its legacy .dark-mode class and dark-mode.css overrides to maintain existing behavior

https://claude.ai/code/session_01QxyBudD1ouFYxsBpB2wy9M

Replaces the binary light/dark toggle with a proper theme system per
issue #91.

- Tokenize theme-able colors as CSS custom properties in variables.css
  (brand/primary, surfaces, text, borders, danger, sidebar) and replace
  the scattered hardcoded brand hex values (#667eea/#764ba2 gradient and
  variants) across the component CSS with var(...) references.
- Add styles/themes.css: a generic, token-driven surface layer (keyed on
  body.themed) plus theme token sets defined under :root[data-theme]. Ships
  light, dark, high-contrast, ocean, and forest themes.
- Dark stays visually identical: it keeps the legacy body.dark-mode class
  and dark-mode.css overrides, with matching tokens so brand colors retint.
- Settings → Appearance now shows a swatch-based theme picker with previews
  instead of a single dark toggle.
- Persist the selection in localStorage ('theme'), respect the legacy
  'darkMode' value for existing users, and apply the theme on <html> before
  first paint via an inline head script (no flash of the wrong theme).
- Update JS that hardcoded dark colors (email-service, contacts-service,
  utils) to read the active theme's tokens instead of the dark-mode class.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QxyBudD1ouFYxsBpB2wy9M
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.

2 participants