Files
calctext/_bmad-output/C-UX-Scenarios/05-theming/5.1-theme-system/5.1-theme-system.md
C. Cassel 0d38bd3108 feat(web): implement complete workspace with themes, tabs, sidebar, and mobile
Transform CalcText from a single-document calculator into a full workspace
application with multi-document support, theming, and responsive mobile experience.

- Theme system: 5 presets (Light, Dark, Matrix, Midnight, Warm) + accent colors
- Document model with localStorage persistence and auto-save
- Tab bar with keyboard shortcuts (Ctrl+N/W/Tab/1-9), rename, close
- Sidebar with search, recent, favorites, folders, templates, drag-and-drop
- 5 templates: Budget, Invoice, Unit Converter, Trip Planner, Loan Calculator
- Status bar with cursor position, engine status, dedication to Igor Cassel
- Results panel: type-specific colors, click-to-copy, error hints
- Format toolbar: H, B, I, //, color labels with live preview toggle
- Syntax highlighting using theme CSS variables
- Error hover tooltips
- Mobile: bottom results tray, sidebar drawer, touch targets, safe areas
- Docker multi-stage build (Rust WASM + Vite + Nginx)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:12:05 -04:00

10 KiB
Raw Blame History

5.1 — Theme System

Previous Step:Templates Next Step:Mobile Experience


Page Metadata

Property Value
Scenario 05 — Theming
Page Number 5.1
Platform Web (PWA), portable to macOS/Windows
Page Type Dropdown Panel + Settings Section

Overview

Page Purpose: Enable users to personalize their workspace with preset themes and custom accent colors. Theming is a personality differentiator — especially the Matrix theme, which gives CalcText a unique identity in the calculator app market.

Success Criteria:

  • Theme switches instantly (< 16ms, no flash of unstyled content)
  • Matrix theme makes users want to screenshot and share
  • Custom accent color gives ownership feeling
  • Theme persists across sessions

Theme Architecture

All theming works through CSS custom properties on :root. Switching themes = swapping property values. No component changes, no re-renders beyond what CSS handles natively.

interface ThemeDefinition {
  id: string
  name: string
  icon: string  // emoji or svg
  tokens: {
    // Backgrounds
    bg: string
    bgSecondary: string
    codeBg: string

    // Text
    text: string
    textH: string

    // Borders
    border: string

    // Accent
    accent: string
    accentBg: string
    accentBorder: string

    // Semantic
    success: string
    warning: string
    error: string

    // Syntax highlighting
    syntaxVariable: string
    syntaxNumber: string
    syntaxOperator: string
    syntaxKeyword: string
    syntaxFunction: string
    syntaxCurrency: string
    syntaxComment: string
    syntaxHeading: string

    // Result colors
    resultNumber: string
    resultUnit: string
    resultCurrency: string
    resultDatetime: string
    resultBoolean: string

    // Stripes
    stripe: string

    // Special
    fontOverride?: string  // for Matrix: force monospace everywhere
    specialEffect?: string // CSS class for effects like scanlines
  }
}

Preset Themes

Light

Token Value
--bg #ffffff
--bg-secondary #f8f9fa
--text #6b6375
--text-h #08060d
--border #e5e4e7
--accent #6366f1
--stripe rgba(0, 0, 0, 0.02)
Feel Clean, airy, professional

Dark

Token Value
--bg #16171d
--bg-secondary #1a1b23
--text #9ca3af
--text-h #f3f4f6
--border #2e303a
--accent #818cf8
--stripe rgba(255, 255, 255, 0.025)
Feel Calm, focused, modern

Matrix

Token Value
--bg #0a0a0a
--bg-secondary #0f1a0f
--text #00ff41
--text-h #33ff66
--border #003300
--accent #00ff41
--stripe rgba(0, 255, 65, 0.03)
--syntax-* Green spectrum (#00cc33 to #39ff14)
--result-currency #ffff00 (yellow — stands out in green)
fontOverride 'Courier New', 'Fira Code', monospace
specialEffect matrix-scanlines
Feel Iconic, hackery, fun

Matrix Special Effects

Scanlines (optional, subtle):

.matrix-scanlines::after {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    0deg,
    transparent,
    transparent 2px,
    rgba(0, 0, 0, 0.08) 2px,
    rgba(0, 0, 0, 0.08) 4px
  );
  z-index: 9999;
}

Cursor glow:

.matrix-theme .cm-cursor {
  border-color: #00ff41;
  box-shadow: 0 0 4px #00ff41, 0 0 8px rgba(0, 255, 65, 0.3);
}

Text glow (subtle):

.matrix-theme .result-value {
  text-shadow: 0 0 2px rgba(0, 255, 65, 0.3);
}

Midnight

Token Value
--bg #0f172a
--bg-secondary #1e293b
--text #94a3b8
--text-h #e2e8f0
--border #334155
--accent #38bdf8 (sky-400)
--stripe rgba(56, 189, 248, 0.03)
Feel Deep blue, serene, late-night coding

Warm

Token Value
--bg #fffbf5
--bg-secondary #fef3e2
--text #78716c
--text-h #1c1917
--border #e7e5e4
--accent #f97316 (orange-500)
--stripe rgba(249, 115, 22, 0.03)
Feel Paper-like, warm, comfortable for long sessions

Theme Picker Dropdown

OBJECT ID: theme-picker

Property Value
Trigger Click theme icon in header or status bar, or Ctrl+Shift+T
Position Dropdown below header button, right-aligned
Width 240px
Background var(--bg)
Border 1px solid var(--border)
Border radius 8px
Shadow 0 4px 16px rgba(0, 0, 0, 0.15)
Padding space-xs (6px)
Animation Scale from 95% + fade, 150ms ease-out
Dismiss Click outside, Esc, or select theme

Theme Picker Layout

┌──────────────────────────┐
│  Themes                  │  section header
│                          │
│  ☀️  Light          ✓   │  active indicator
│  🌙  Dark               │
│  💻  Matrix              │
│  🌊  Midnight            │
│  📜  Warm                │
│                          │
│  ──────────────────────  │  separator
│                          │
│  Accent Color            │  section header
│  [●][●][●][●][●][●][●]  │  color swatches
│                          │
│  ──────────────────────  │
│  ⚙️  System (auto)       │  follows OS preference
└──────────────────────────┘

Theme Item

Property Value
Height 36px
Padding 8px 12px
Layout Icon + name + optional checkmark
Hover var(--accent-bg) background, border-radius 4px
Active Checkmark ✓ on right side, weight 500
Click Apply theme instantly, close picker
Preview On hover, show a 4-color mini-swatch (bg, text, accent, secondary)

Color Swatch Preview (on hover)

Property Value
Size 4 circles, 12px each, 4px gap
Colors bg, bg-secondary, accent, text — of the hovered theme
Position Inline after theme name

Accent Color Picker

OBJECT ID: theme-accent-picker

Property Value
Location Inside theme picker dropdown, below themes
Presets 7 color swatches in a row
Swatch size 20px circles, 6px gap
Active swatch 2px ring in var(--text-h)
Custom Last swatch is rainbow gradient → opens native color picker
Behavior Overrides --accent, --accent-bg, --accent-border for current theme
Persistence Stored as accentColor in localStorage

Preset Accent Colors

Name Value Hex
Indigo (default) Indigo 500/400 #6366f1 / #818cf8
Teal Teal 500/400 #14b8a6 / #2dd4bf
Rose Rose 500/400 #f43f5e / #fb7185
Amber Amber 500/400 #f59e0b / #fbbf24
Emerald Emerald 500/400 #10b981 / #34d399
Sky Sky 500/400 #0ea5e9 / #38bdf8
Custom User pick Via <input type="color">

Each preset has light/dark variants. The correct variant is selected based on current base theme.


System Theme Option

OBJECT ID: theme-system

Property Value
Behavior Follows OS prefers-color-scheme
Matches Light ↔ Light theme, Dark ↔ Dark theme
Override Selecting any specific theme disables system following
Re-enable Select "System (auto)" in picker
Label "⚙️ System (auto)" with current resolved theme name

Theme Application Flow

1. User clicks theme → store in localStorage
2. Apply CSS class to <html>: `data-theme="matrix"`
3. CSS variables resolve from theme class
4. All components instantly update (CSS-only, no React re-render)
5. CodeMirror theme needs manual reconfiguration (dispatch theme compartment)
6. PWA theme-color meta tag updates for status bar color

CodeMirror Theme Sync

Step Action
1 Theme changes → dispatch new baseTheme to EditorView
2 Syntax highlighting colors update via CSS variables (no extension swap)
3 Stripe colors update via CSS
4 Active line highlight updates via CSS
5 Only the base theme extension needs reconfiguration

Responsive

Breakpoint Theme Picker
>= 768px Dropdown panel below header button
< 768px Bottom sheet (slides up from bottom, 60vh max height)

Mobile Bottom Sheet

Property Value
Width 100vw
Max height 60vh
Border radius 12px 12px 0 0 (top corners)
Drag handle 32px × 4px pill, centered
Backdrop 50% black overlay
Animation Slide up 200ms ease-out
Close Swipe down, tap backdrop

Page States

State When Behavior
First launch No theme preference Follow OS (system). If OS is dark, use Dark.
Theme selected User picked a theme Applied instantly, persisted. System mode disabled.
System mode User selected "System (auto)" Listens to OS changes in real-time.
Custom accent User picked accent color Overrides accent tokens. Works with any base theme.
Matrix active Matrix theme selected Font override applied. Scanline effect enabled. Green cursor glow.

Technical Notes

  • No Flash of Unstyled Content (FOUC): Load theme from localStorage in <script> in <head> BEFORE React mounts. Set data-theme on <html> synchronously.
  • CSS structure: Each theme is a [data-theme="name"] selector block overriding :root variables.
  • Matrix performance: Scanline effect uses pointer-events: none and is pure CSS. No performance impact.
  • Cross-platform: macOS/Windows use native appearance APIs. Theme names map to native equivalents. Matrix theme = custom dark palette on all platforms.
  • PWA: Update <meta name="theme-color"> dynamically when theme changes for native status bar color.

Created using Whiteport Design Studio (WDS) methodology