# Theme Architecture — The Chameleon Engine > *"The only constant in design is change — a theme system embraces it."* > — Unknown --- ## 🎯 Filosofi Theme Sistem theme BETE dibangun di atas **CSS Custom Properties**: 1. **Separation of value from token** — Nilai warna tidak pernah dirujuk langsung 2. **Single source of truth** — Satu set CSS variables, dua tema (dark/light) 3. **Runtime switching** — Tema bisa diganti tanpa reload 4. **Component-agnostic** — Komponen tidak tahu tema apa yang aktif --- ## 🧬 Theme Architecture ``` CSS Custom Properties (oklch values) │ ▼ ┌─────────────────────────────────────┐ │ :root / [data-theme] │ ← Tema didefinisikan di level root │ --clr-surface-base: oklch(...) │ │ --clr-primary: oklch(...) │ │ --clr-text: oklch(...) │ └──────────────┬──────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ Tailwind Config Mapping │ ← Map CSS vars ke Tailwind utilities │ colors: { │ │ background: "oklch(var(--...))" │ │ } │ └──────────────┬──────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ Component Styles │ ← Komponen pakai Tailwind/CSS vars │
│ │ .card { background: var(--clr..) } │ └─────────────────────────────────────┘ ``` --- ## 🌗 Theme Definitions ### Dark Theme (Default) ```css [data-theme="dark"] { /* Surfaces */ --clr-surface-base: oklch(0.11 0.010 286); --clr-surface-elevated: oklch(0.14 0.015 286); --clr-surface-overlay: oklch(0.17 0.020 286); --clr-surface-sunken: oklch(0.08 0.005 286); --clr-border: oklch(0.22 0.020 286); /* Text */ --clr-text: oklch(0.95 0.005 286); --clr-text-secondary: oklch(0.70 0.015 286); --clr-text-tertiary: oklch(0.50 0.020 286); --clr-text-inverse: oklch(0.11 0.010 286); /* Brand - brighter in dark */ --clr-primary: oklch(0.62 0.150 255); --clr-primary-bg: oklch(0.25 0.060 255 / 0.20); --clr-primary-400: oklch(0.62 0.150 255); --clr-primary-500: oklch(0.55 0.175 255); --clr-primary-600: oklch(0.47 0.160 255); /* Interactive */ --clr-interactive-hover: oklch(0.20 0.025 286); --clr-interactive-active: oklch(0.24 0.030 286); --clr-interactive-selected: oklch(0.25 0.060 255 / 0.15); /* Shadows */ --sh-card: 0 2px 8px rgba(0, 0, 0, 0.3); --sh-hover: 0 4px 16px rgba(0, 0, 0, 0.4); --sh-elevated: 0 8px 32px rgba(0, 0, 0, 0.5); --sh-modal: 0 16px 48px rgba(0, 0, 0, 0.6); /* Glass */ --glass-bg: oklch(0.15 0.015 286 / 0.60); --glass-border: oklch(0.25 0.030 286 / 0.20); } ``` ### Light Theme ```css [data-theme="light"] { /* Surfaces */ --clr-surface-base: oklch(0.97 0.002 286); --clr-surface-elevated: oklch(1.00 0.000 286); --clr-surface-overlay: oklch(0.95 0.003 286); --clr-surface-sunken: oklch(0.92 0.004 286); --clr-border: oklch(0.87 0.005 286); /* Text */ --clr-text: oklch(0.11 0.010 286); --clr-text-secondary: oklch(0.50 0.020 286); --clr-text-tertiary: oklch(0.70 0.025 286); --clr-text-inverse: oklch(0.97 0.005 286); /* Brand - standard in light */ --clr-primary: oklch(0.55 0.175 255); --clr-primary-bg: oklch(0.90 0.060 255 / 0.25); --clr-primary-400: oklch(0.55 0.175 255); --clr-primary-500: oklch(0.47 0.160 255); --clr-primary-600: oklch(0.40 0.140 255); /* Interactive */ --clr-interactive-hover: oklch(0.90 0.005 286); --clr-interactive-active: oklch(0.85 0.008 286); --clr-interactive-selected: oklch(0.90 0.060 255 / 0.3); /* Shadows — lighter in light theme */ --sh-card: 0 2px 8px rgba(0, 0, 0, 0.08); --sh-hover: 0 4px 16px rgba(0, 0, 0, 0.12); --sh-elevated: 0 8px 24px rgba(0, 0, 0, 0.08); --sh-modal: 0 16px 48px rgba(0, 0, 0, 0.12); /* Glass — lighter opacity */ --glass-bg: oklch(0.97 0.002 286 / 0.50); --glass-border: oklch(0.87 0.005 286 / 0.30); } ``` --- ## 🔄 Theme Switching ### React Implementation ```tsx // hooks/useTheme.ts type Theme = 'light' | 'dark'; function useTheme() { const [theme, setTheme] = useState