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

372 lines
10 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 5.1 — Theme System
**Previous Step:** ← [Templates](../../04-file-organization/4.2-templates/4.2-templates.md)
**Next Step:** → [Mobile Experience](../../06-mobile-experience/6.1-mobile-shell/6.1-mobile-shell.md)
---
## 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.
```typescript
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):**
```css
.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:**
```css
.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):**
```css
.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_