feat: add dark theme support with CSS custom properties and Naive UI integration
Implement runtime theme switching using CSS custom properties delegated through SCSS variables, with light/dark/system modes, FOUC prevention, sidebar toggle, and settings selector. Add theme-aware video assets for sidebar and chat thinking indicator. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Theme transition (applied programmatically on toggle, not on load)
|
||||
html.theme-transitioning,
|
||||
html.theme-transitioning *,
|
||||
html.theme-transitioning *::before,
|
||||
html.theme-transitioning *::after {
|
||||
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease,
|
||||
box-shadow 0.3s ease, fill 0.3s ease, stroke 0.3s ease !important;
|
||||
}
|
||||
|
||||
html, body, #app {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
@@ -67,7 +76,7 @@ a {
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: rgba($accent-primary, 0.3);
|
||||
background: rgba(var(--accent-primary-rgb), 0.3);
|
||||
}
|
||||
|
||||
// Shared page header
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { GlobalThemeOverrides } from 'naive-ui'
|
||||
|
||||
export const themeOverrides: GlobalThemeOverrides = {
|
||||
export const lightThemeOverrides: GlobalThemeOverrides = {
|
||||
common: {
|
||||
primaryColor: '#333333',
|
||||
primaryColorHover: '#1a1a1a',
|
||||
@@ -69,3 +69,83 @@ export const themeOverrides: GlobalThemeOverrides = {
|
||||
borderRadius: '6px',
|
||||
},
|
||||
}
|
||||
|
||||
export const darkThemeOverrides: GlobalThemeOverrides = {
|
||||
common: {
|
||||
primaryColor: '#e0e0e0',
|
||||
primaryColorHover: '#f5f5f5',
|
||||
primaryColorPressed: '#ffffff',
|
||||
primaryColorSuppl: '#e0e0e0',
|
||||
bodyColor: '#1a1a1a',
|
||||
cardColor: '#2a2a2a',
|
||||
modalColor: '#2a2a2a',
|
||||
popoverColor: '#2a2a2a',
|
||||
tableColor: '#2a2a2a',
|
||||
inputColor: '#2a2a2a',
|
||||
actionColor: '#252525',
|
||||
textColorBase: '#e0e0e0',
|
||||
textColor1: '#e0e0e0',
|
||||
textColor2: '#a0a0a0',
|
||||
textColor3: '#666666',
|
||||
dividerColor: '#3a3a3a',
|
||||
borderColor: '#3a3a3a',
|
||||
hoverColor: 'rgba(255, 255, 255, 0.06)',
|
||||
borderRadius: '8px',
|
||||
borderRadiusSmall: '6px',
|
||||
fontSize: '14px',
|
||||
fontSizeMedium: '14px',
|
||||
heightMedium: '36px',
|
||||
fontFamily: 'Inter, system-ui, -apple-system, sans-serif',
|
||||
fontFamilyMono: 'JetBrains Mono, Fira Code, Consolas, monospace',
|
||||
},
|
||||
Layout: {
|
||||
color: '#1a1a1a',
|
||||
siderColor: '#202020',
|
||||
headerColor: '#1a1a1a',
|
||||
},
|
||||
Menu: {
|
||||
itemTextColorActive: '#e0e0e0',
|
||||
itemTextColorActiveHover: '#e0e0e0',
|
||||
itemTextColorChildActive: '#e0e0e0',
|
||||
itemIconColorActive: '#e0e0e0',
|
||||
itemIconColorActiveHover: '#ffffff',
|
||||
itemColorActive: 'rgba(255, 255, 255, 0.08)',
|
||||
itemColorActiveHover: 'rgba(255, 255, 255, 0.12)',
|
||||
arrowColorActive: '#e0e0e0',
|
||||
},
|
||||
Button: {
|
||||
textColorPrimary: '#1a1a1a',
|
||||
colorPrimary: '#e0e0e0',
|
||||
colorHoverPrimary: '#f5f5f5',
|
||||
colorPressedPrimary: '#ffffff',
|
||||
},
|
||||
Input: {
|
||||
color: '#2a2a2a',
|
||||
colorFocus: '#2a2a2a',
|
||||
border: '1px solid #3a3a3a',
|
||||
borderHover: '1px solid #666666',
|
||||
borderFocus: '1px solid #e0e0e0',
|
||||
placeholderColor: '#666666',
|
||||
caretColor: '#e0e0e0',
|
||||
},
|
||||
Card: {
|
||||
color: '#2a2a2a',
|
||||
borderColor: '#3a3a3a',
|
||||
},
|
||||
Modal: {
|
||||
color: '#2a2a2a',
|
||||
},
|
||||
Tag: {
|
||||
borderRadius: '6px',
|
||||
},
|
||||
Switch: {
|
||||
railColor: '#3a3a3a',
|
||||
railColorActive: '#66bb6a',
|
||||
loadingColor: '#e0e0e0',
|
||||
opacityDisabled: 0.4,
|
||||
},
|
||||
}
|
||||
|
||||
export function getThemeOverrides(isDark: boolean): GlobalThemeOverrides {
|
||||
return isDark ? darkThemeOverrides : lightThemeOverrides
|
||||
}
|
||||
|
||||
@@ -1,41 +1,150 @@
|
||||
// 黑白水墨 — Pure Ink
|
||||
// 纯黑白灰,无彩色
|
||||
// 支持 light / dark 双主题
|
||||
|
||||
// ─── CSS Custom Properties ─────────────────────────────────────
|
||||
|
||||
:root {
|
||||
// Backgrounds
|
||||
--bg-primary: #fafafa;
|
||||
--bg-secondary: #f0f0f0;
|
||||
--bg-sidebar: #f5f5f5;
|
||||
--bg-card: #ffffff;
|
||||
--bg-card-hover: #fafafa;
|
||||
--bg-input: #ffffff;
|
||||
|
||||
// Borders
|
||||
--border-color: #e0e0e0;
|
||||
--border-light: #ebebeb;
|
||||
|
||||
// Accent
|
||||
--accent-primary: #333333;
|
||||
--accent-hover: #1a1a1a;
|
||||
--accent-muted: #888888;
|
||||
|
||||
// Text
|
||||
--text-primary: #1a1a1a;
|
||||
--text-secondary: #666666;
|
||||
--text-muted: #999999;
|
||||
|
||||
// Status
|
||||
--success: #2e7d32;
|
||||
--error: #c62828;
|
||||
--warning: #f57f17;
|
||||
|
||||
// Message bubbles
|
||||
--msg-user-bg: #f5f5f5;
|
||||
--msg-assistant-bg: #f5f5f5;
|
||||
--msg-system-border: #bdbdbd;
|
||||
|
||||
// Code
|
||||
--code-bg: #f4f4f4;
|
||||
|
||||
// Utility
|
||||
--text-on-accent: #ffffff;
|
||||
--text-on-overlay: #ffffff;
|
||||
--accent-info: #4a90d9;
|
||||
|
||||
// RGB components (for rgba() usage)
|
||||
--accent-primary-rgb: 51, 51, 51;
|
||||
--accent-hover-rgb: 26, 26, 26;
|
||||
--text-primary-rgb: 26, 26, 26;
|
||||
--text-muted-rgb: 153, 153, 153;
|
||||
--success-rgb: 46, 125, 50;
|
||||
--error-rgb: 198, 40, 40;
|
||||
--warning-rgb: 245, 127, 23;
|
||||
--accent-info-rgb: 74, 144, 217;
|
||||
}
|
||||
|
||||
.dark {
|
||||
// Backgrounds
|
||||
--bg-primary: #1a1a1a;
|
||||
--bg-secondary: #252525;
|
||||
--bg-sidebar: #202020;
|
||||
--bg-card: #333333;
|
||||
--bg-card-hover: #333333;
|
||||
--bg-input: #2a2a2a;
|
||||
|
||||
// Borders
|
||||
--border-color: #3a3a3a;
|
||||
--border-light: #333333;
|
||||
|
||||
// Accent
|
||||
--accent-primary: #e0e0e0;
|
||||
--accent-hover: #f5f5f5;
|
||||
--accent-muted: #888888;
|
||||
|
||||
// Text
|
||||
--text-primary: #e0e0e0;
|
||||
--text-secondary: #a0a0a0;
|
||||
--text-muted: #666666;
|
||||
|
||||
// Status
|
||||
--success: #66bb6a;
|
||||
--error: #ef5350;
|
||||
--warning: #ffb74d;
|
||||
|
||||
// Message bubbles
|
||||
--msg-user-bg: #252525;
|
||||
--msg-assistant-bg: #252525;
|
||||
--msg-system-border: #555555;
|
||||
|
||||
// Code
|
||||
--code-bg: #1e1e1e;
|
||||
|
||||
// Utility
|
||||
--text-on-accent: #1a1a1a;
|
||||
--text-on-overlay: #ffffff;
|
||||
--accent-info: #6ba3d6;
|
||||
|
||||
// RGB components
|
||||
--accent-primary-rgb: 224, 224, 224;
|
||||
--accent-hover-rgb: 245, 245, 245;
|
||||
--text-primary-rgb: 224, 224, 224;
|
||||
--text-muted-rgb: 102, 102, 102;
|
||||
--success-rgb: 102, 187, 106;
|
||||
--error-rgb: 239, 83, 80;
|
||||
--warning-rgb: 255, 183, 77;
|
||||
--accent-info-rgb: 107, 163, 214;
|
||||
}
|
||||
|
||||
// ─── SCSS Variables (delegate to CSS custom properties) ────────
|
||||
|
||||
// Backgrounds
|
||||
$bg-primary: #fafafa;
|
||||
$bg-secondary: #f0f0f0;
|
||||
$bg-sidebar: #f5f5f5;
|
||||
$bg-card: #ffffff;
|
||||
$bg-card-hover: #fafafa;
|
||||
$bg-input: #ffffff;
|
||||
$bg-primary: var(--bg-primary);
|
||||
$bg-secondary: var(--bg-secondary);
|
||||
$bg-sidebar: var(--bg-sidebar);
|
||||
$bg-card: var(--bg-card);
|
||||
$bg-card-hover: var(--bg-card-hover);
|
||||
$bg-input: var(--bg-input);
|
||||
|
||||
// Borders
|
||||
$border-color: #e0e0e0;
|
||||
$border-light: #ebebeb;
|
||||
$border-color: var(--border-color);
|
||||
$border-light: var(--border-light);
|
||||
|
||||
// Accent
|
||||
$accent-primary: #333333;
|
||||
$accent-hover: #1a1a1a;
|
||||
$accent-muted: #888888;
|
||||
$accent-primary: var(--accent-primary);
|
||||
$accent-hover: var(--accent-hover);
|
||||
$accent-muted: var(--accent-muted);
|
||||
|
||||
// Text
|
||||
$text-primary: #1a1a1a;
|
||||
$text-secondary: #666666;
|
||||
$text-muted: #999999;
|
||||
$text-primary: var(--text-primary);
|
||||
$text-secondary: var(--text-secondary);
|
||||
$text-muted: var(--text-muted);
|
||||
|
||||
// Status
|
||||
$success: #2e7d32;
|
||||
$error: #c62828;
|
||||
$warning: #f57f17;
|
||||
$success: var(--success);
|
||||
$error: var(--error);
|
||||
$warning: var(--warning);
|
||||
$info: $accent-primary;
|
||||
|
||||
// Message bubbles
|
||||
$msg-user-bg: #e8e8e8;
|
||||
$msg-assistant-bg: #f5f5f5;
|
||||
$msg-system-border: #bdbdbd;
|
||||
$msg-user-bg: var(--msg-user-bg);
|
||||
$msg-assistant-bg: var(--msg-assistant-bg);
|
||||
$msg-system-border: var(--msg-system-border);
|
||||
|
||||
// Code
|
||||
$code-bg: #f4f4f4;
|
||||
$code-bg: var(--code-bg);
|
||||
|
||||
// Typography
|
||||
$font-ui: 'Inter', system-ui, -apple-system, sans-serif;
|
||||
|
||||
Reference in New Issue
Block a user