diff --git a/vocero-frontend/src/styles.css b/vocero-frontend/src/styles.css index 9c5b8e0..af72fe3 100644 --- a/vocero-frontend/src/styles.css +++ b/vocero-frontend/src/styles.css @@ -1,5 +1,6 @@ @layer theme, base, components, utilities; @import "tailwindcss/theme.css" layer(theme); +@import "./styles/theme.css"; @import "./styles/legacy-theme.css" layer(components); @import "./styles/layout.css" layer(components); @import "tailwindcss/utilities.css" layer(utilities); diff --git a/vocero-frontend/src/styles/legacy-theme.css b/vocero-frontend/src/styles/legacy-theme.css index 082d566..9d5ddc0 100644 --- a/vocero-frontend/src/styles/legacy-theme.css +++ b/vocero-frontend/src/styles/legacy-theme.css @@ -1,73 +1,3 @@ -:root { - --bg: #07080d; - --bg-elev: rgba(22, 25, 37, 0.55); - --bg-elev-2: rgba(31, 35, 52, 0.55); - --border: rgba(255, 255, 255, 0.08); - --border-strong: rgba(255, 255, 255, 0.16); - --glass: rgba(18, 20, 30, 0.45); - --glass-strong: rgba(18, 20, 30, 0.62); - --text: #f5f6fa; - --text-dim: rgba(245, 246, 250, 0.7); - --text-faint: rgba(245, 246, 250, 0.48); - - --brand: #8b5cf6; - --brand-2: #ec4899; - --brand-3: #22d3ee; - --accent: #a78bfa; - --accent-2: #22d3ee; - --listening: #22d3ee; - --speaking: #a78bfa; - --processing: #fbbf24; - --error: #ff6a75; - --success: #34d399; - - --brand-gradient: linear-gradient(135deg, #8b5cf6 0%, #ec4899 50%, #22d3ee 100%); - --brand-soft-gradient: linear-gradient( - 135deg, - color-mix(in srgb, #8b5cf6 55%, transparent), - color-mix(in srgb, #ec4899 48%, transparent) 55%, - color-mix(in srgb, #22d3ee 55%, transparent) - ); - - /* Mono is the "machine voice": reserved for system text the app emits — - * the orb's status caption, tool calls, the transport tag. Body/UI stays - * Inter. */ - --font-mono: "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace; - --font-sans: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; - - /* Thesis: color belongs to the voice. The chrome is monochrome; saturated - * hue appears only on the orb and on these tiny role echoes, which mirror - * the orb's own state colors (you listening = cyan, assistant = violet, - * tool = amber) so the transcript reads in the same color language. */ - --voice-user: var(--accent-2); - --voice-assistant: var(--accent); - --voice-tool: var(--processing); - - /* Smoothed mic RMS in [0..1], updated every frame from JS while a session - * is active. Used by audio-reactive circle states. */ - --audio-level: 0; - /* Five log-spaced frequency bands extracted from the mic analyser; - * drive each bar's height independently for a real "spectrum" feel. */ - --bar0: 0; - --bar1: 0; - --bar2: 0; - --bar3: 0; - --bar4: 0; - - --radius-sm: 10px; - --radius-md: 16px; - --radius-lg: 26px; - --radius-xl: 34px; - - --shadow-soft: 0 10px 40px rgba(0, 0, 0, 0.35); - --shadow-glow-brand: - 0 0 40px -10px color-mix(in srgb, var(--brand) 60%, transparent), - 0 0 80px -30px color-mix(in srgb, var(--brand-2) 55%, transparent); - --shadow-glass: 0 8px 40px rgba(0, 0, 0, 0.32), 0 2px 8px rgba(0, 0, 0, 0.18); - - color-scheme: dark; -} - * { box-sizing: border-box; } @@ -3985,53 +3915,6 @@ html[data-theme="white"] .bubble-stack { so the native dark theme is 100% preserved when the flag is off. ═══════════════════════════════════════════════════════════════════════ */ -html[data-theme="white"] { - color-scheme: light; - /* ── Token overrides ─────────────────────────────────────────────── */ - --bg: #ffffff; - --bg-elev: #f8fafc; - --bg-elev-2: #f1f5f9; - --border: rgba(15, 23, 42, 0.08); - --border-strong: rgba(15, 23, 42, 0.16); - --glass: rgba(255, 255, 255, 0.85); - --glass-strong: rgba(248, 250, 252, 0.92); - --text: #0f172a; /* slate-900 */ - --text-dim: rgba(15, 23, 42, 0.72); - --text-faint: rgba(15, 23, 42, 0.55); - - --brand: #1f6feb; /* deep corporate blue */ - --brand-2: #3b82f6; /* medium blue */ - --brand-3: #60a5fa; /* light blue */ - --accent: #2563eb; /* primary blue */ - --accent-2: #3b82f6; /* call-to-action blue */ - --listening: #2563eb; - --speaking: #1d4ed8; - --processing: #d97706; - --error: #dc2626; - --success: #16a34a; - - --brand-gradient: linear-gradient(135deg, #2563eb 0%, #3b82f6 55%, #60a5fa 100%); - --brand-soft-gradient: linear-gradient( - 135deg, - rgba(37, 99, 235, 0.18), - rgba(59, 130, 246, 0.14) 55%, - rgba(96, 165, 250, 0.18) - ); - - --voice-user: #2563eb; - --voice-assistant: #64748b; - --voice-tool: #d97706; - - --shadow-soft: 0 6px 20px rgba(15, 23, 42, 0.06); - --shadow-glow-brand: 0 0 28px -6px rgba(37, 99, 235, 0.45), 0 0 60px -18px rgba(59, 130, 246, 0.4); - --shadow-glass: 0 2px 12px rgba(15, 23, 42, 0.06), 0 1px 3px rgba(15, 23, 42, 0.04); - - --radius-sm: 10px; - --radius-md: 16px; - --radius-lg: 22px; - --radius-xl: 28px; -} - /* ── Page chrome (strip aurora gradients / dot patterns) ────────────── */ html[data-theme="white"] body { background: #ffffff !important; diff --git a/vocero-frontend/src/styles/theme.css b/vocero-frontend/src/styles/theme.css new file mode 100644 index 0000000..0b67a14 --- /dev/null +++ b/vocero-frontend/src/styles/theme.css @@ -0,0 +1,142 @@ +/* Palette values switch with server configuration; utilities resolve them at use time. */ +@custom-variant white (&:where([data-theme="white"], [data-theme="white"] *)); +@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *)); +@custom-variant compact (@media (max-width: 600px)); +@custom-variant short (@media (max-height: 640px)); + +@theme inline { + --color-surface: var(--bg); + --color-surface-raised: var(--bg-elev); + --color-surface-overlay: var(--bg-elev-2); + --color-foreground: var(--text); + --color-muted: var(--text-dim); + --color-faint: var(--text-faint); + --color-line: var(--border); + --color-line-strong: var(--border-strong); + --color-brand: var(--brand); + --color-brand-secondary: var(--brand-2); + --color-brand-tertiary: var(--brand-3); + --color-accent: var(--accent); + --color-accent-secondary: var(--accent-2); + --color-listening: var(--listening); + --color-speaking: var(--speaking); + --color-processing: var(--processing); + --color-error: var(--error); + --color-success: var(--success); + --color-voice-user: var(--voice-user); + --color-voice-assistant: var(--voice-assistant); + --color-voice-tool: var(--voice-tool); + --radius-control: var(--radius-sm); + --radius-panel: var(--radius-md); + --radius-card: var(--radius-lg); + --shadow-panel: var(--shadow-soft); + --shadow-brand: var(--shadow-glow-brand); + --shadow-card-glass: var(--shadow-glass); +} + +@theme { + --font-sans: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + --font-mono: "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace; +} + +@layer base { + :root { + --bg: #07080d; + --bg-elev: rgba(22, 25, 37, 0.55); + --bg-elev-2: rgba(31, 35, 52, 0.55); + --border: rgba(255, 255, 255, 0.08); + --border-strong: rgba(255, 255, 255, 0.16); + --glass: rgba(18, 20, 30, 0.45); + --glass-strong: rgba(18, 20, 30, 0.62); + --text: #f5f6fa; + --text-dim: rgba(245, 246, 250, 0.7); + --text-faint: rgba(245, 246, 250, 0.48); + + --brand: #8b5cf6; + --brand-2: #ec4899; + --brand-3: #22d3ee; + --accent: #a78bfa; + --accent-2: #22d3ee; + --listening: #22d3ee; + --speaking: #a78bfa; + --processing: #fbbf24; + --error: #ff6a75; + --success: #34d399; + + --brand-gradient: linear-gradient(135deg, #8b5cf6 0%, #ec4899 50%, #22d3ee 100%); + --brand-soft-gradient: linear-gradient( + 135deg, + color-mix(in srgb, #8b5cf6 55%, transparent), + color-mix(in srgb, #ec4899 48%, transparent) 55%, + color-mix(in srgb, #22d3ee 55%, transparent) + ); + --voice-user: var(--accent-2); + --voice-assistant: var(--accent); + --voice-tool: var(--processing); + --audio-level: 0; + --bar0: 0; + --bar1: 0; + --bar2: 0; + --bar3: 0; + --bar4: 0; + + --radius-sm: 10px; + --radius-md: 16px; + --radius-lg: 26px; + --radius-xl: 34px; + + --shadow-soft: 0 10px 40px rgba(0, 0, 0, 0.35); + --shadow-glow-brand: + 0 0 40px -10px color-mix(in srgb, var(--brand) 60%, transparent), + 0 0 80px -30px color-mix(in srgb, var(--brand-2) 55%, transparent); + --shadow-glass: 0 8px 40px rgba(0, 0, 0, 0.32), 0 2px 8px rgba(0, 0, 0, 0.18); + + color-scheme: dark; + } + + html[data-theme="white"] { + color-scheme: light; + --bg: #ffffff; + --bg-elev: #f8fafc; + --bg-elev-2: #f1f5f9; + --border: rgba(15, 23, 42, 0.08); + --border-strong: rgba(15, 23, 42, 0.16); + --glass: rgba(255, 255, 255, 0.85); + --glass-strong: rgba(248, 250, 252, 0.92); + --text: #0f172a; + --text-dim: rgba(15, 23, 42, 0.72); + --text-faint: rgba(15, 23, 42, 0.55); + + --brand: #1f6feb; + --brand-2: #3b82f6; + --brand-3: #60a5fa; + --accent: #2563eb; + --accent-2: #3b82f6; + --listening: #2563eb; + --speaking: #1d4ed8; + --processing: #d97706; + --error: #dc2626; + --success: #16a34a; + + --brand-gradient: linear-gradient(135deg, #2563eb 0%, #3b82f6 55%, #60a5fa 100%); + --brand-soft-gradient: linear-gradient( + 135deg, + rgba(37, 99, 235, 0.18), + rgba(59, 130, 246, 0.14) 55%, + rgba(96, 165, 250, 0.18) + ); + + --voice-user: #2563eb; + --voice-assistant: #64748b; + --voice-tool: #d97706; + + --shadow-soft: 0 6px 20px rgba(15, 23, 42, 0.06); + --shadow-glow-brand: 0 0 28px -6px rgba(37, 99, 235, 0.45), 0 0 60px -18px rgba(59, 130, 246, 0.4); + --shadow-glass: 0 2px 12px rgba(15, 23, 42, 0.06), 0 1px 3px rgba(15, 23, 42, 0.04); + + --radius-sm: 10px; + --radius-md: 16px; + --radius-lg: 22px; + --radius-xl: 28px; + } +}