dark-mode-design-expert
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDark Mode Design Expert
深色模式UI设计专家
Master dark mode UI design with atmospheric theming, WCAG accessibility, and cross-platform best practices. Specializes in weather/sky/ocean-inspired color systems that adapt to time of day and environmental conditions.
掌握融合氛围主题、WCAG无障碍标准和跨平台最佳实践的深色模式UI设计,专长于可根据时间和环境条件自适应的天气/天空/海洋灵感配色系统。
When to Use This Skill
何时使用此技能
Activate on:
- "dark mode", "dark theme", "night mode"
- "theme switching", "light/dark toggle"
- "atmospheric UI", "weather theme", "sky gradient"
- "OLED optimization", "battery-friendly dark"
- "elevation in dark mode", "surface layering"
- "prefers-color-scheme", "color-scheme CSS"
- "contrast ratios dark mode", "accessibility dark theme"
NOT for:
- General color palette creation →
color-theory-palette-harmony-expert - Typography and font selection →
typography-expert - Component library architecture →
design-system-creator - Contrast auditing of specific colors →
color-contrast-auditor
触发场景:
- "dark mode"、"dark theme"、"night mode"(深色模式、夜间模式)
- "theme switching"、"light/dark toggle"(主题切换、明暗切换)
- "atmospheric UI"、"weather theme"、"sky gradient"(氛围式UI、天气主题、天空渐变)
- "OLED optimization"、"battery-friendly dark"(OLED优化、省电深色模式)
- "elevation in dark mode"、"surface layering"(深色模式层级、表面分层)
- "prefers-color-scheme"、"color-scheme CSS"(系统配色偏好、CSS配色方案)
- "contrast ratios dark mode"、"accessibility dark theme"(深色模式对比度、无障碍深色主题)
不适用场景:
- 通用调色板创建 →
color-theory-palette-harmony-expert - 排版与字体选择 →
typography-expert - 组件库架构 →
design-system-creator - 特定颜色对比度审核 →
color-contrast-auditor
The Science of Dark Mode
深色模式的科学原理
Why Dark Mode Exists
深色模式的存在意义
| Factor | Light Mode | Dark Mode | Winner |
|---|---|---|---|
| OLED Battery | 100% baseline | 39-47% savings at max brightness | Dark |
| Low Light Comfort | Eye strain, fatigue | Reduced glare | Dark |
| Bright Environment | Better readability | Washed out | Light |
| Astigmatism Users | Easier to read | Halation effect | Light |
| Focus/Immersion | Standard | Content pops forward | Dark |
| Sleep Hygiene | Blue light exposure | Reduced blue light | Dark |
Key Insight: Dark mode isn't universally better—it's contextually better. The best systems respect user preference AND adapt to environment.
| 影响因素 | 浅色模式 | 深色模式 | 优势方 |
|---|---|---|---|
| OLED设备续航 | 100%基准 | 最高亮度下节省39-47%电量 | 深色 |
| 低光环境舒适度 | 视疲劳、眼干涩 | 减少眩光 | 深色 |
| 明亮环境可读性 | 可读性更佳 | 画面泛白 | 浅色 |
| 散光用户体验 | 更易阅读 | 出现光晕效应 | 浅色 |
| 专注度/沉浸感 | 标准体验 | 内容突出 | 深色 |
| 睡眠健康 | 暴露蓝光 | 减少蓝光 | 深色 |
核心见解: 深色模式并非在所有场景下都更优——它是场景化的最优解。优秀的系统既要尊重用户偏好,也要能适配环境。
Contrast Requirements (WCAG 2.1)
WCAG 2.1对比度要求
| Element Type | Minimum Ratio | Target Ratio | Notes |
|---|---|---|---|
| Body text | 4.5:1 | 7:1+ | AAA preferred for readability |
| Large text (≥24px) | 3:1 | 4.5:1+ | Headlines, hero text |
| UI components | 3:1 | 4.5:1+ | Borders, icons, focus rings |
| Disabled elements | None required | 2.5:1 | UX consideration |
| Decorative | None required | - | Pure aesthetic elements |
Dark Mode Gotcha: High contrast (21:1 pure white on black) causes more eye strain than moderate contrast (15:1). Target 12:1 to 16:1 for primary text.
| 元素类型 | 最低对比度 | 目标对比度 | 说明 |
|---|---|---|---|
| 正文文本 | 4.5:1 | 7:1+ | AAA级标准更利于阅读 |
| 大文本(≥24px) | 3:1 | 4.5:1+ | 标题、主视觉文本 |
| UI组件 | 3:1 | 4.5:1+ | 边框、图标、焦点环 |
| 禁用元素 | 无强制要求 | 2.5:1 | 提升UX体验 |
| 装饰性元素 | 无强制要求 | - | 纯美学元素 |
深色模式误区: 高对比度(纯黑背景配纯白文本,21:1)比中等对比度(15:1)更易引发视疲劳。正文文本建议目标对比度为12:1至16:1。
The Three-Tier Token Architecture
三层令牌架构
Foundation: Primitives → Semantic → Component
基础层:原始值 → 语义化 → 组件级
css
/* ══════════════════════════════════════════════════════════════════
TIER 1: PRIMITIVES - Raw color values, never used directly
══════════════════════════════════════════════════════════════════ */
:root {
/* Neutrals */
--color-gray-50: #f8fafc;
--color-gray-100: #f1f5f9;
--color-gray-200: #e2e8f0;
--color-gray-300: #cbd5e1;
--color-gray-400: #94a3b8;
--color-gray-500: #64748b;
--color-gray-600: #475569;
--color-gray-700: #334155;
--color-gray-800: #1e293b;
--color-gray-900: #0f172a;
--color-gray-950: #020617;
/* Brand Colors */
--color-ocean-300: #7dd3fc;
--color-ocean-400: #38bdf8;
--color-ocean-500: #0ea5e9;
--color-ocean-600: #0284c7;
--color-ocean-700: #0369a1;
/* Atmospheric Colors (for weather theming) */
--color-twilight-deep: #0c1222;
--color-twilight-mid: #151b2e;
--color-twilight-surface: #1a1f3a;
--color-dawn-warm: #fef3c7;
--color-sunset-orange: #fb923c;
--color-storm-gray: #374151;
}
/* ══════════════════════════════════════════════════════════════════
TIER 2: SEMANTIC - Purpose-driven, theme-aware
══════════════════════════════════════════════════════════════════ */
/* Light Mode (Default) */
:root, :root.theme-light {
/* Text */
--color-text-primary: var(--color-gray-900); /* 15.3:1 on white */
--color-text-secondary: var(--color-gray-600); /* 7.0:1 on white */
--color-text-muted: var(--color-gray-500); /* 4.6:1 on white */
--color-text-inverse: var(--color-gray-50);
/* Backgrounds */
--color-bg-primary: #ffffff;
--color-bg-secondary: var(--color-gray-50);
--color-bg-elevated: #ffffff;
--color-bg-overlay: rgba(0, 0, 0, 0.5);
/* Surfaces (elevation system) */
--color-surface-base: #ffffff;
--color-surface-raised: #ffffff;
--color-surface-overlay: #ffffff;
/* Borders */
--color-border-default: var(--color-gray-200);
--color-border-muted: var(--color-gray-100);
--color-border-emphasis: var(--color-gray-300);
/* Interactive */
--color-interactive-primary: var(--color-ocean-600);
--color-interactive-hover: var(--color-ocean-700);
--color-interactive-focus: var(--color-ocean-500);
/* Elevation (shadows work in light mode) */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15);
}
/* Dark Mode */
:root.theme-dark {
/* Text - slightly off-white to reduce strain */
--color-text-primary: var(--color-gray-50); /* 15.3:1 on dark */
--color-text-secondary: var(--color-gray-300); /* 9.3:1 on dark */
--color-text-muted: var(--color-gray-400); /* 5.5:1 on dark */
--color-text-inverse: var(--color-gray-900);
/* Backgrounds - NOT pure black (#000) */
--color-bg-primary: var(--color-twilight-deep); /* #0c1222 */
--color-bg-secondary: var(--color-twilight-mid); /* #151b2e */
--color-bg-elevated: var(--color-twilight-surface); /* #1a1f3a */
--color-bg-overlay: rgba(0, 0, 0, 0.7);
/* Surfaces - LIGHTER for elevation (key dark mode principle) */
--color-surface-base: var(--color-twilight-deep);
--color-surface-raised: var(--color-twilight-mid);
--color-surface-overlay: var(--color-twilight-surface);
/* Borders - more visible in dark mode */
--color-border-default: rgba(255, 255, 255, 0.1);
--color-border-muted: rgba(255, 255, 255, 0.05);
--color-border-emphasis: rgba(255, 255, 255, 0.2);
/* Interactive - brighter for visibility */
--color-interactive-primary: var(--color-ocean-400);
--color-interactive-hover: var(--color-ocean-300);
--color-interactive-focus: var(--color-ocean-500);
/* Elevation - GLOW replaces shadows in dark mode */
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);
--shadow-md: 0 4px 8px rgba(0, 0, 0, 0.5);
--shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.5);
--shadow-xl: 0 12px 24px rgba(0, 0, 0, 0.6);
/* Glow effects (unique to dark mode) */
--glow-sm: 0 0 8px rgba(56, 189, 248, 0.2);
--glow-md: 0 0 16px rgba(56, 189, 248, 0.3);
--glow-lg: 0 0 32px rgba(56, 189, 248, 0.4);
}
/* ══════════════════════════════════════════════════════════════════
TIER 3: COMPONENT - Specific usage, consuming semantic tokens
══════════════════════════════════════════════════════════════════ */
:root {
/* Buttons */
--button-bg: var(--color-interactive-primary);
--button-text: var(--color-text-inverse);
--button-border: transparent;
--button-shadow: var(--shadow-sm);
/* Cards */
--card-bg: var(--color-surface-raised);
--card-border: var(--color-border-default);
--card-shadow: var(--shadow-md);
/* Inputs */
--input-bg: var(--color-bg-primary);
--input-border: var(--color-border-default);
--input-focus-ring: var(--color-interactive-focus);
}css
/* ══════════════════════════════════════════════════════════════════
TIER 1: PRIMITIVES - Raw color values, never used directly
══════════════════════════════════════════════════════════════════ */
:root {
/* Neutrals */
--color-gray-50: #f8fafc;
--color-gray-100: #f1f5f9;
--color-gray-200: #e2e8f0;
--color-gray-300: #cbd5e1;
--color-gray-400: #94a3b8;
--color-gray-500: #64748b;
--color-gray-600: #475569;
--color-gray-700: #334155;
--color-gray-800: #1e293b;
--color-gray-900: #0f172a;
--color-gray-950: #020617;
/* Brand Colors */
--color-ocean-300: #7dd3fc;
--color-ocean-400: #38bdf8;
--color-ocean-500: #0ea5e9;
--color-ocean-600: #0284c7;
--color-ocean-700: #0369a1;
/* Atmospheric Colors (for weather theming) */
--color-twilight-deep: #0c1222;
--color-twilight-mid: #151b2e;
--color-twilight-surface: #1a1f3a;
--color-dawn-warm: #fef3c7;
--color-sunset-orange: #fb923c;
--color-storm-gray: #374151;
}
/* ══════════════════════════════════════════════════════════════════
TIER 2: SEMANTIC - Purpose-driven, theme-aware
══════════════════════════════════════════════════════════════════ */
/* Light Mode (Default) */
:root, :root.theme-light {
/* Text */
--color-text-primary: var(--color-gray-900); /* 15.3:1 on white */
--color-text-secondary: var(--color-gray-600); /* 7.0:1 on white */
--color-text-muted: var(--color-gray-500); /* 4.6:1 on white */
--color-text-inverse: var(--color-gray-50);
/* Backgrounds */
--color-bg-primary: #ffffff;
--color-bg-secondary: var(--color-gray-50);
--color-bg-elevated: #ffffff;
--color-bg-overlay: rgba(0, 0, 0, 0.5);
/* Surfaces (elevation system) */
--color-surface-base: #ffffff;
--color-surface-raised: #ffffff;
--color-surface-overlay: #ffffff;
/* Borders */
--color-border-default: var(--color-gray-200);
--color-border-muted: var(--color-gray-100);
--color-border-emphasis: var(--color-gray-300);
/* Interactive */
--color-interactive-primary: var(--color-ocean-600);
--color-interactive-hover: var(--color-ocean-700);
--color-interactive-focus: var(--color-ocean-500);
/* Elevation (shadows work in light mode) */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15);
}
/* Dark Mode */
:root.theme-dark {
/* Text - slightly off-white to reduce strain */
--color-text-primary: var(--color-gray-50); /* 15.3:1 on dark */
--color-text-secondary: var(--color-gray-300); /* 9.3:1 on dark */
--color-text-muted: var(--color-gray-400); /* 5.5:1 on dark */
--color-text-inverse: var(--color-gray-900);
/* Backgrounds - NOT pure black (#000) */
--color-bg-primary: var(--color-twilight-deep); /* #0c1222 */
--color-bg-secondary: var(--color-twilight-mid); /* #151b2e */
--color-bg-elevated: var(--color-twilight-surface); /* #1a1f3a */
--color-bg-overlay: rgba(0, 0, 0, 0.7);
/* Surfaces - LIGHTER for elevation (key dark mode principle) */
--color-surface-base: var(--color-twilight-deep);
--color-surface-raised: var(--color-twilight-mid);
--color-surface-overlay: var(--color-twilight-surface);
/* Borders - more visible in dark mode */
--color-border-default: rgba(255, 255, 255, 0.1);
--color-border-muted: rgba(255, 255, 255, 0.05);
--color-border-emphasis: rgba(255, 255, 255, 0.2);
/* Interactive - brighter for visibility */
--color-interactive-primary: var(--color-ocean-400);
--color-interactive-hover: var(--color-ocean-300);
--color-interactive-focus: var(--color-ocean-500);
/* Elevation - GLOW replaces shadows in dark mode */
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);
--shadow-md: 0 4px 8px rgba(0, 0, 0, 0.5);
--shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.5);
--shadow-xl: 0 12px 24px rgba(0, 0, 0, 0.6);
/* Glow effects (unique to dark mode) */
--glow-sm: 0 0 8px rgba(56, 189, 248, 0.2);
--glow-md: 0 0 16px rgba(56, 189, 248, 0.3);
--glow-lg: 0 0 32px rgba(56, 189, 248, 0.4);
}
/* ══════════════════════════════════════════════════════════════════
TIER 3: COMPONENT - Specific usage, consuming semantic tokens
══════════════════════════════════════════════════════════════════ */
:root {
/* Buttons */
--button-bg: var(--color-interactive-primary);
--button-text: var(--color-text-inverse);
--button-border: transparent;
--button-shadow: var(--shadow-sm);
/* Cards */
--card-bg: var(--color-surface-raised);
--card-border: var(--color-border-default);
--card-shadow: var(--shadow-md);
/* Inputs */
--input-bg: var(--color-bg-primary);
--input-border: var(--color-border-default);
--input-focus-ring: var(--color-interactive-focus);
}Elevation in Dark Mode: The Critical Difference
深色模式的层级设计:关键差异
Why Shadows Fail in Dark Mode
为何阴影在深色模式中失效
In light mode, shadows create depth by simulating light from above. In dark mode:
- Shadows become invisible against dark backgrounds
- Pure black shadows look like "holes"
- The illusion breaks completely
在浅色模式中,阴影通过模拟上方光源来营造深度感。但在深色模式中:
- 阴影在深色背景下几乎不可见
- 纯黑色阴影看起来像“空洞”
- 深度错觉完全被打破
Material Design 3 Solution: Tonal Elevation
Material Design 3解决方案:色调层级
Instead of shadows, use lighter surface colors for elevated elements:
css
/* Light Mode: Shadows create elevation */
.card-light {
background: #ffffff;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
/* Dark Mode: Surface color creates elevation */
.card-dark {
background: #1e1e1e; /* Elevated from #121212 base */
box-shadow: none; /* Or very subtle */
}用更浅的表面颜色替代阴影来表现层级:
css
/* Light Mode: Shadows create elevation */
.card-light {
background: #ffffff;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
/* Dark Mode: Surface color creates elevation */
.card-dark {
background: #1e1e1e; /* Elevated from #121212 base */
box-shadow: none; /* Or very subtle */
}Elevation Scale (Material Design 3)
层级刻度(Material Design 3)
| Level | Light Mode | Dark Mode Surface | Overlay % |
|---|---|---|---|
| 0 (base) | #ffffff | #121212 | 0% |
| 1 | shadow-sm | #1e1e1e | 5% white |
| 2 | shadow-md | #232323 | 7% white |
| 3 | shadow-lg | #282828 | 8% white |
| 4 | shadow-xl | #2d2d2d | 9% white |
| 5 | shadow-2xl | #323232 | 11% white |
| 层级 | 浅色模式 | 深色模式表面色 | 白色叠加占比 |
|---|---|---|---|
| 0 (基础层) | #ffffff | #121212 | 0% |
| 1 | shadow-sm | #1e1e1e | 5% |
| 2 | shadow-md | #232323 | 7% |
| 3 | shadow-lg | #282828 | 8% |
| 4 | shadow-xl | #2d2d2d | 9% |
| 5 | shadow-2xl | #323232 | 11% |
Implementation Pattern
实现模式
css
:root.theme-dark {
/* Calculate overlay colors */
--elevation-1: color-mix(in srgb, white 5%, var(--color-bg-primary));
--elevation-2: color-mix(in srgb, white 7%, var(--color-bg-primary));
--elevation-3: color-mix(in srgb, white 8%, var(--color-bg-primary));
--elevation-4: color-mix(in srgb, white 9%, var(--color-bg-primary));
--elevation-5: color-mix(in srgb, white 11%, var(--color-bg-primary));
}
.card {
background: var(--elevation-2);
}
.modal {
background: var(--elevation-4);
}
.dropdown {
background: var(--elevation-3);
}css
:root.theme-dark {
/* Calculate overlay colors */
--elevation-1: color-mix(in srgb, white 5%, var(--color-bg-primary));
--elevation-2: color-mix(in srgb, white 7%, var(--color-bg-primary));
--elevation-3: color-mix(in srgb, white 8%, var(--color-bg-primary));
--elevation-4: color-mix(in srgb, white 9%, var(--color-bg-primary));
--elevation-5: color-mix(in srgb, white 11%, var(--color-bg-primary));
}
.card {
background: var(--elevation-2);
}
.modal {
background: var(--elevation-4);
}
.dropdown {
background: var(--elevation-3);
}CSS Implementation Patterns
CSS实现模式
Modern Approach: prefers-color-scheme
+ light-dark()
prefers-color-schemelight-dark()现代方案:prefers-color-scheme
+ light-dark()
prefers-color-schemelight-dark()css
/* 1. Set color-scheme for native element styling */
:root {
color-scheme: light dark;
}
/* 2. Use light-dark() for inline theming (2024+ browsers) */
.card {
background: light-dark(#ffffff, #1e1e1e);
color: light-dark(#1f2937, #f3f4f6);
border: 1px solid light-dark(#e5e7eb, rgba(255,255,255,0.1));
}
/* 3. Respect system preference */
@media (prefers-color-scheme: dark) {
:root:not(.theme-light) {
/* Dark mode tokens */
}
}
@media (prefers-color-scheme: light) {
:root:not(.theme-dark) {
/* Light mode tokens */
}
}css
/* 1. Set color-scheme for native element styling */
:root {
color-scheme: light dark;
}
/* 2. Use light-dark() for inline theming (2024+ browsers) */
.card {
background: light-dark(#ffffff, #1e1e1e);
color: light-dark(#1f2937, #f3f4f6);
border: 1px solid light-dark(#e5e7eb, rgba(255,255,255,0.1));
}
/* 3. Respect system preference */
@media (prefers-color-scheme: dark) {
:root:not(.theme-light) {
/* Dark mode tokens */
}
}
@media (prefers-color-scheme: light) {
:root:not(.theme-dark) {
/* Light mode tokens */
}
}Theme Switching with JavaScript
JavaScript实现主题切换
typescript
// Theme manager with persistence
type Theme = 'light' | 'dark' | 'system';
function setTheme(theme: Theme) {
const root = document.documentElement;
// Remove existing theme classes
root.classList.remove('theme-light', 'theme-dark');
if (theme === 'system') {
// Let CSS media queries handle it
localStorage.removeItem('theme');
return;
}
// Apply explicit theme
root.classList.add(`theme-${theme}`);
localStorage.setItem('theme', theme);
}
function getSystemTheme(): 'light' | 'dark' {
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
}
// Initialize on page load (before render to prevent flash)
function initTheme() {
const saved = localStorage.getItem('theme') as Theme | null;
if (saved && saved !== 'system') {
document.documentElement.classList.add(`theme-${saved}`);
}
}
// Listen for system changes
window.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', (e) => {
if (!localStorage.getItem('theme')) {
// Only react if user hasn't set explicit preference
// CSS will handle via media queries
}
});typescript
// Theme manager with persistence
type Theme = 'light' | 'dark' | 'system';
function setTheme(theme: Theme) {
const root = document.documentElement;
// Remove existing theme classes
root.classList.remove('theme-light', 'theme-dark');
if (theme === 'system') {
// Let CSS media queries handle it
localStorage.removeItem('theme');
return;
}
// Apply explicit theme
root.classList.add(`theme-${theme}`);
localStorage.setItem('theme', theme);
}
function getSystemTheme(): 'light' | 'dark' {
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
}
// Initialize on page load (before render to prevent flash)
function initTheme() {
const saved = localStorage.getItem('theme') as Theme | null;
if (saved && saved !== 'system') {
document.documentElement.classList.add(`theme-${saved}`);
}
}
// Listen for system changes
window.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', (e) => {
if (!localStorage.getItem('theme')) {
// Only react if user hasn't set explicit preference
// CSS will handle via media queries
}
});Preventing Flash of Wrong Theme (FOWT)
避免主题闪烁问题(FOWT)
html
<!-- In <head>, before any CSS -->
<script>
(function() {
const theme = localStorage.getItem('theme');
if (theme === 'dark') {
document.documentElement.classList.add('theme-dark');
} else if (theme === 'light') {
document.documentElement.classList.add('theme-light');
}
})();
</script>html
<!-- In <head>, before any CSS -->
<script>
(function() {
const theme = localStorage.getItem('theme');
if (theme === 'dark') {
document.documentElement.classList.add('theme-dark');
} else if (theme === 'light') {
document.documentElement.classList.add('theme-light');
}
})();
</script>Worked Example: Weather/Sky/Ocean Atmospheric UI
实战案例:天气/天空/海洋氛围式UI
This is the flagship example—a complete token system for a weather-inspired UI that adapts to time of day and atmospheric conditions.
这是核心示例——一套完整的令牌系统,实现可根据时间和环境条件自适应的天气灵感UI。
Design Philosophy
设计理念
The ocean and sky share a visual language:
- Dawn/Dusk: Warm gradients, soft transitions
- Midday: Bright, high contrast, clear
- Night: Deep blues, subtle glows, stars
- Storm: Dramatic grays, electric highlights
- Underwater: Teal depths, bioluminescent accents
海洋与天空共享一套视觉语言:
- 黎明/黄昏:暖色调渐变、柔和过渡
- 正午:明亮、高对比度、清晰
- 夜晚:深蓝、微光、星点
- 风暴:深灰、电光高亮
- 水下:青蓝深邃、生物荧光点缀
Complete Token System
完整令牌系统
css
/* ══════════════════════════════════════════════════════════════════
OCEAN MODERN: ATMOSPHERIC UI TOKEN SYSTEM
A weather/sky/ocean-inspired design system with time-of-day awareness
══════════════════════════════════════════════════════════════════ */
:root {
/* ────────────────────────────────────────────────────────────────
PRIMITIVES: Atmospheric Color Palette
──────────────────────────────────────────────────────────────── */
/* Ocean Depths */
--ocean-surface: #38bdf8; /* Sunlit surface */
--ocean-shallow: #0ea5e9; /* Clear shallows */
--ocean-mid: #0284c7; /* Mid-depth */
--ocean-deep: #0369a1; /* Deep water */
--ocean-abyss: #075985; /* Abyssal zone */
--ocean-trench: #0c4a6e; /* Hadal zone */
/* Sky States */
--sky-dawn: #fef3c7; /* Golden hour */
--sky-morning: #bae6fd; /* Clear morning */
--sky-midday: #7dd3fc; /* Bright day */
--sky-golden: #fcd34d; /* Golden hour */
--sky-sunset: #fb923c; /* Sunset orange */
--sky-dusk: #a78bfa; /* Purple dusk */
--sky-twilight: #6366f1; /* Civil twilight */
--sky-night: #1e1b4b; /* Night sky */
/* Atmospheric Effects */
--atmosphere-haze: rgba(186, 230, 253, 0.3);
--atmosphere-fog: rgba(241, 245, 249, 0.6);
--atmosphere-mist: rgba(148, 163, 184, 0.4);
/* Storm System */
--storm-light: #9ca3af;
--storm-mid: #6b7280;
--storm-dark: #4b5563;
--storm-thunder: #374151;
--storm-lightning: #fbbf24;
/* Bioluminescence (dark mode accents) */
--bio-cyan: #22d3ee;
--bio-teal: #2dd4bf;
--bio-blue: #60a5fa;
--bio-purple: #a78bfa;
--bio-glow: rgba(34, 211, 238, 0.4);
/* Sand & Beach */
--sand-light: #fef3c7;
--sand-warm: #fde68a;
--sand-golden: #fcd34d;
--sand-wet: #d4a574;
/* Coral & Life */
--coral-pink: #fb7185;
--coral-orange: #fb923c;
--kelp-green: #22c55e;
--algae-teal: #14b8a6;
}
/* ════════════════════════════════════════════════════════════════════
SEMANTIC: Time-of-Day Themes
════════════════════════════════════════════════════════════════════ */
/* DAWN THEME (5am - 8am) - Warm, hopeful, transitional */
:root.atmosphere-dawn {
--color-text-primary: #1e293b;
--color-text-secondary: #475569;
--color-bg-primary: var(--sky-dawn);
--color-bg-secondary: #fef9e7;
--color-accent: var(--sky-golden);
--gradient-sky: linear-gradient(
180deg,
var(--sky-night) 0%,
var(--sky-dusk) 20%,
var(--sky-sunset) 40%,
var(--sky-golden) 70%,
var(--sky-dawn) 100%
);
--gradient-ocean: linear-gradient(
180deg,
var(--ocean-deep) 0%,
var(--ocean-mid) 50%,
var(--ocean-surface) 100%
);
}
/* DAYLIGHT THEME (8am - 5pm) - Bright, clear, energetic */
:root.atmosphere-day, :root.theme-light {
--color-text-primary: #0f172a;
--color-text-secondary: #334155;
--color-text-muted: #64748b;
--color-bg-primary: #ffffff;
--color-bg-secondary: #f8fafc;
--color-bg-elevated: #ffffff;
--color-accent: var(--ocean-shallow);
--color-accent-hover: var(--ocean-mid);
--gradient-sky: linear-gradient(
180deg,
var(--sky-midday) 0%,
var(--sky-morning) 50%,
#ffffff 100%
);
--gradient-ocean: linear-gradient(
180deg,
var(--ocean-abyss) 0%,
var(--ocean-deep) 30%,
var(--ocean-mid) 60%,
var(--ocean-shallow) 100%
);
/* Daylight uses shadows for elevation */
--elevation-method: shadow;
}
/* GOLDEN HOUR THEME (5pm - 7pm) - Warm, dramatic, nostalgic */
:root.atmosphere-golden {
--color-text-primary: #1c1917;
--color-text-secondary: #44403c;
--color-bg-primary: #fffbeb;
--color-bg-secondary: #fef3c7;
--color-accent: var(--sky-sunset);
--gradient-sky: linear-gradient(
180deg,
var(--sky-midday) 0%,
var(--sky-golden) 40%,
var(--sky-sunset) 70%,
var(--coral-pink) 100%
);
--gradient-ocean: linear-gradient(
180deg,
var(--ocean-deep) 0%,
#0891b2 50%,
#fcd34d 100%
);
}
/* TWILIGHT THEME (7pm - 9pm) - Transitional, mysterious */
:root.atmosphere-twilight {
--color-text-primary: #e2e8f0;
--color-text-secondary: #94a3b8;
--color-bg-primary: #0f172a;
--color-bg-secondary: #1e293b;
--color-bg-elevated: #334155;
--color-accent: var(--sky-twilight);
--gradient-sky: linear-gradient(
180deg,
var(--sky-night) 0%,
var(--sky-twilight) 30%,
var(--sky-dusk) 60%,
var(--sky-sunset) 100%
);
--gradient-ocean: linear-gradient(
180deg,
var(--ocean-trench) 0%,
var(--ocean-abyss) 50%,
var(--ocean-deep) 100%
);
}
/* NIGHT THEME (9pm - 5am) - Deep, calm, bioluminescent */
:root.atmosphere-night, :root.theme-dark {
--color-text-primary: #f1f5f9; /* 15.3:1 ✓ AAA */
--color-text-secondary: #cbd5e1; /* 9.3:1 ✓ AAA */
--color-text-muted: #94a3b8; /* 5.5:1 ✓ AA */
--color-bg-primary: #0c1222; /* Deep twilight navy */
--color-bg-secondary: #151b2e;
--color-bg-elevated: #1a1f3a;
--color-accent: var(--bio-cyan);
--color-accent-hover: var(--bio-teal);
--gradient-sky: linear-gradient(
180deg,
#020617 0%,
var(--sky-night) 50%,
#1e1b4b 100%
);
--gradient-ocean: linear-gradient(
180deg,
#020617 0%,
var(--ocean-trench) 50%,
var(--ocean-abyss) 100%
);
/* Night uses lighter surfaces for elevation */
--elevation-method: surface;
/* Bioluminescent glow effects */
--glow-accent: 0 0 20px var(--bio-glow);
--glow-subtle: 0 0 10px rgba(34, 211, 238, 0.2);
/* Surface elevation scale */
--surface-base: #0c1222;
--surface-1: #111827;
--surface-2: #1f2937;
--surface-3: #374151;
--surface-4: #4b5563;
}
/* STORM THEME - Dramatic, intense, electric */
:root.atmosphere-storm {
--color-text-primary: #f3f4f6;
--color-text-secondary: #d1d5db;
--color-bg-primary: var(--storm-thunder);
--color-bg-secondary: var(--storm-dark);
--color-bg-elevated: var(--storm-mid);
--color-accent: var(--storm-lightning);
--gradient-sky: linear-gradient(
180deg,
var(--storm-thunder) 0%,
var(--storm-dark) 30%,
var(--storm-mid) 60%,
var(--storm-light) 100%
);
--gradient-ocean: linear-gradient(
180deg,
#1f2937 0%,
#374151 40%,
#6b7280 80%,
#9ca3af 100%
);
/* Lightning flash animation */
--flash-color: rgba(251, 191, 36, 0.3);
}
/* ════════════════════════════════════════════════════════════════════
COMPONENT: Atmospheric UI Elements
════════════════════════════════════════════════════════════════════ */
/* Glass Card - works in all atmospheres */
.glass-card {
background: var(--glass-bg, rgba(255, 255, 255, 0.1));
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid var(--glass-border, rgba(255, 255, 255, 0.2));
border-radius: 16px;
}
:root.theme-light .glass-card,
:root.atmosphere-day .glass-card,
:root.atmosphere-dawn .glass-card {
--glass-bg: rgba(255, 255, 255, 0.7);
--glass-border: rgba(0, 0, 0, 0.1);
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1);
}
:root.theme-dark .glass-card,
:root.atmosphere-night .glass-card,
:root.atmosphere-twilight .glass-card {
--glass-bg: rgba(15, 23, 42, 0.6);
--glass-border: rgba(255, 255, 255, 0.1);
box-shadow: var(--glow-subtle);
}
/* Wave Animation */
.wave-layer {
position: absolute;
bottom: 0;
left: 0;
width: 200%;
height: var(--wave-height, 100px);
background: var(--wave-color);
animation: wave var(--wave-duration, 8s) ease-in-out infinite;
opacity: var(--wave-opacity, 0.6);
}
@keyframes wave {
0%, 100% { transform: translateX(0) translateY(0); }
50% { transform: translateX(-25%) translateY(-10px); }
}
/* Bioluminescent Glow (dark mode only) */
:root.theme-dark .glow-element,
:root.atmosphere-night .glow-element {
box-shadow: var(--glow-accent);
transition: box-shadow 0.3s ease;
}
:root.theme-dark .glow-element:hover,
:root.atmosphere-night .glow-element:hover {
box-shadow: 0 0 30px var(--bio-glow), 0 0 60px rgba(34, 211, 238, 0.2);
}
/* Cloud Layer */
.cloud-layer {
position: absolute;
width: 100%;
height: 100%;
background-image: var(--cloud-pattern);
opacity: var(--cloud-opacity, 0.5);
animation: drift var(--cloud-speed, 60s) linear infinite;
}
@keyframes drift {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}css
/* ══════════════════════════════════════════════════════════════════
OCEAN MODERN: ATMOSPHERIC UI TOKEN SYSTEM
A weather/sky/ocean-inspired design system with time-of-day awareness
══════════════════════════════════════════════════════════════════ */
:root {
/* ────────────────────────────────────────────────────────────────
PRIMITIVES: Atmospheric Color Palette
──────────────────────────────────────────────────────────────── */
/* Ocean Depths */
--ocean-surface: #38bdf8; /* Sunlit surface */
--ocean-shallow: #0ea5e9; /* Clear shallows */
--ocean-mid: #0284c7; /* Mid-depth */
--ocean-deep: #0369a1; /* Deep water */
--ocean-abyss: #075985; /* Abyssal zone */
--ocean-trench: #0c4a6e; /* Hadal zone */
/* Sky States */
--sky-dawn: #fef3c7; /* Golden hour */
--sky-morning: #bae6fd; /* Clear morning */
--sky-midday: #7dd3fc; /* Bright day */
--sky-golden: #fcd34d; /* Golden hour */
--sky-sunset: #fb923c; /* Sunset orange */
--sky-dusk: #a78bfa; /* Purple dusk */
--sky-twilight: #6366f1; /* Civil twilight */
--sky-night: #1e1b4b; /* Night sky */
/* Atmospheric Effects */
--atmosphere-haze: rgba(186, 230, 253, 0.3);
--atmosphere-fog: rgba(241, 245, 249, 0.6);
--atmosphere-mist: rgba(148, 163, 184, 0.4);
/* Storm System */
--storm-light: #9ca3af;
--storm-mid: #6b7280;
--storm-dark: #4b5563;
--storm-thunder: #374151;
--storm-lightning: #fbbf24;
/* Bioluminescence (dark mode accents) */
--bio-cyan: #22d3ee;
--bio-teal: #2dd4bf;
--bio-blue: #60a5fa;
--bio-purple: #a78bfa;
--bio-glow: rgba(34, 211, 238, 0.4);
/* Sand & Beach */
--sand-light: #fef3c7;
--sand-warm: #fde68a;
--sand-golden: #fcd34d;
--sand-wet: #d4a574;
/* Coral & Life */
--coral-pink: #fb7185;
--coral-orange: #fb923c;
--kelp-green: #22c55e;
--algae-teal: #14b8a6;
}
/* ════════════════════════════════════════════════════════════════════
SEMANTIC: Time-of-Day Themes
════════════════════════════════════════════════════════════════════ */
/* DAWN THEME (5am - 8am) - Warm, hopeful, transitional */
:root.atmosphere-dawn {
--color-text-primary: #1e293b;
--color-text-secondary: #475569;
--color-bg-primary: var(--sky-dawn);
--color-bg-secondary: #fef9e7;
--color-accent: var(--sky-golden);
--gradient-sky: linear-gradient(
180deg,
var(--sky-night) 0%,
var(--sky-dusk) 20%,
var(--sky-sunset) 40%,
var(--sky-golden) 70%,
var(--sky-dawn) 100%
);
--gradient-ocean: linear-gradient(
180deg,
var(--ocean-deep) 0%,
var(--ocean-mid) 50%,
var(--ocean-surface) 100%
);
}
/* DAYLIGHT THEME (8am - 5pm) - Bright, clear, energetic */
:root.atmosphere-day, :root.theme-light {
--color-text-primary: #0f172a;
--color-text-secondary: #334155;
--color-text-muted: #64748b;
--color-bg-primary: #ffffff;
--color-bg-secondary: #f8fafc;
--color-bg-elevated: #ffffff;
--color-accent: var(--ocean-shallow);
--color-accent-hover: var(--ocean-mid);
--gradient-sky: linear-gradient(
180deg,
var(--sky-midday) 0%,
var(--sky-morning) 50%,
#ffffff 100%
);
--gradient-ocean: linear-gradient(
180deg,
var(--ocean-abyss) 0%,
var(--ocean-deep) 30%,
var(--ocean-mid) 60%,
var(--ocean-shallow) 100%
);
/* Daylight uses shadows for elevation */
--elevation-method: shadow;
}
/* GOLDEN HOUR THEME (5pm - 7pm) - Warm, dramatic, nostalgic */
:root.atmosphere-golden {
--color-text-primary: #1c1917;
--color-text-secondary: #44403c;
--color-bg-primary: #fffbeb;
--color-bg-secondary: #fef3c7;
--color-accent: var(--sky-sunset);
--gradient-sky: linear-gradient(
180deg,
var(--sky-midday) 0%,
var(--sky-golden) 40%,
var(--sky-sunset) 70%,
var(--coral-pink) 100%
);
--gradient-ocean: linear-gradient(
180deg,
var(--ocean-deep) 0%,
#0891b2 50%,
#fcd34d 100%
);
}
/* TWILIGHT THEME (7pm - 9pm) - Transitional, mysterious */
:root.atmosphere-twilight {
--color-text-primary: #e2e8f0;
--color-text-secondary: #94a3b8;
--color-bg-primary: #0f172a;
--color-bg-secondary: #1e293b;
--color-bg-elevated: #334155;
--color-accent: var(--sky-twilight);
--gradient-sky: linear-gradient(
180deg,
var(--sky-night) 0%,
var(--sky-twilight) 30%,
var(--sky-dusk) 60%,
var(--sky-sunset) 100%
);
--gradient-ocean: linear-gradient(
180deg,
var(--ocean-trench) 0%,
var(--ocean-abyss) 50%,
var(--ocean-deep) 100%
);
}
/* NIGHT THEME (9pm - 5am) - Deep, calm, bioluminescent */
:root.atmosphere-night, :root.theme-dark {
--color-text-primary: #f1f5f9; /* 15.3:1 ✓ AAA */
--color-text-secondary: #cbd5e1; /* 9.3:1 ✓ AAA */
--color-text-muted: #94a3b8; /* 5.5:1 ✓ AA */
--color-bg-primary: #0c1222; /* Deep twilight navy */
--color-bg-secondary: #151b2e;
--color-bg-elevated: #1a1f3a;
--color-accent: var(--bio-cyan);
--color-accent-hover: var(--bio-teal);
--gradient-sky: linear-gradient(
180deg,
#020617 0%,
var(--sky-night) 50%,
#1e1b4b 100%
);
--gradient-ocean: linear-gradient(
180deg,
#020617 0%,
var(--ocean-trench) 50%,
var(--ocean-abyss) 100%
);
/* Night uses lighter surfaces for elevation */
--elevation-method: surface;
/* Bioluminescent glow effects */
--glow-accent: 0 0 20px var(--bio-glow);
--glow-subtle: 0 0 10px rgba(34, 211, 238, 0.2);
/* Surface elevation scale */
--surface-base: #0c1222;
--surface-1: #111827;
--surface-2: #1f2937;
--surface-3: #374151;
--surface-4: #4b5563;
}
/* STORM THEME - Dramatic, intense, electric */
:root.atmosphere-storm {
--color-text-primary: #f3f4f6;
--color-text-secondary: #d1d5db;
--color-bg-primary: var(--storm-thunder);
--color-bg-secondary: var(--storm-dark);
--color-bg-elevated: var(--storm-mid);
--color-accent: var(--storm-lightning);
--gradient-sky: linear-gradient(
180deg,
var(--storm-thunder) 0%,
var(--storm-dark) 30%,
var(--storm-mid) 60%,
var(--storm-light) 100%
);
--gradient-ocean: linear-gradient(
180deg,
#1f2937 0%,
#374151 40%,
#6b7280 80%,
#9ca3af 100%
);
/* Lightning flash animation */
--flash-color: rgba(251, 191, 36, 0.3);
}
/* ════════════════════════════════════════════════════════════════════
COMPONENT: Atmospheric UI Elements
════════════════════════════════════════════════════════════════════ */
/* Glass Card - works in all atmospheres */
.glass-card {
background: var(--glass-bg, rgba(255, 255, 255, 0.1));
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid var(--glass-border, rgba(255, 255, 255, 0.2));
border-radius: 16px;
}
:root.theme-light .glass-card,
:root.atmosphere-day .glass-card,
:root.atmosphere-dawn .glass-card {
--glass-bg: rgba(255, 255, 255, 0.7);
--glass-border: rgba(0, 0, 0, 0.1);
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1);
}
:root.theme-dark .glass-card,
:root.atmosphere-night .glass-card,
:root.atmosphere-twilight .glass-card {
--glass-bg: rgba(15, 23, 42, 0.6);
--glass-border: rgba(255, 255, 255, 0.1);
box-shadow: var(--glow-subtle);
}
/* Wave Animation */
.wave-layer {
position: absolute;
bottom: 0;
left: 0;
width: 200%;
height: var(--wave-height, 100px);
background: var(--wave-color);
animation: wave var(--wave-duration, 8s) ease-in-out infinite;
opacity: var(--wave-opacity, 0.6);
}
@keyframes wave {
0%, 100% { transform: translateX(0) translateY(0); }
50% { transform: translateX(-25%) translateY(-10px); }
}
/* Bioluminescent Glow (dark mode only) */
:root.theme-dark .glow-element,
:root.atmosphere-night .glow-element {
box-shadow: var(--glow-accent);
transition: box-shadow 0.3s ease;
}
:root.theme-dark .glow-element:hover,
:root.atmosphere-night .glow-element:hover {
box-shadow: 0 0 30px var(--bio-glow), 0 0 60px rgba(34, 211, 238, 0.2);
}
/* Cloud Layer */
.cloud-layer {
position: absolute;
width: 100%;
height: 100%;
background-image: var(--cloud-pattern);
opacity: var(--cloud-opacity, 0.5);
animation: drift var(--cloud-speed, 60s) linear infinite;
}
@keyframes drift {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}Time-Based Theme Switching
基于时间的主题切换
typescript
type Atmosphere = 'dawn' | 'day' | 'golden' | 'twilight' | 'night' | 'storm';
function getAtmosphereFromTime(hour: number): Atmosphere {
if (hour >= 5 && hour < 8) return 'dawn';
if (hour >= 8 && hour < 17) return 'day';
if (hour >= 17 && hour < 19) return 'golden';
if (hour >= 19 && hour < 21) return 'twilight';
return 'night';
}
function setAtmosphere(atmosphere: Atmosphere) {
const root = document.documentElement;
// Remove all atmosphere classes
root.classList.remove(
'atmosphere-dawn', 'atmosphere-day', 'atmosphere-golden',
'atmosphere-twilight', 'atmosphere-night', 'atmosphere-storm',
'theme-light', 'theme-dark'
);
// Add new atmosphere
root.classList.add(`atmosphere-${atmosphere}`);
// Also set base theme for compatibility
const isDark = ['twilight', 'night', 'storm'].includes(atmosphere);
root.classList.add(isDark ? 'theme-dark' : 'theme-light');
}
// Auto-update based on time
function initAtmosphericUI() {
const updateAtmosphere = () => {
const hour = new Date().getHours();
setAtmosphere(getAtmosphereFromTime(hour));
};
updateAtmosphere();
// Update every 15 minutes
setInterval(updateAtmosphere, 15 * 60 * 1000);
}typescript
type Atmosphere = 'dawn' | 'day' | 'golden' | 'twilight' | 'night' | 'storm';
function getAtmosphereFromTime(hour: number): Atmosphere {
if (hour >= 5 && hour < 8) return 'dawn';
if (hour >= 8 && hour < 17) return 'day';
if (hour >= 17 && hour < 19) return 'golden';
if (hour >= 19 && hour < 21) return 'twilight';
return 'night';
}
function setAtmosphere(atmosphere: Atmosphere) {
const root = document.documentElement;
// Remove all atmosphere classes
root.classList.remove(
'atmosphere-dawn', 'atmosphere-day', 'atmosphere-golden',
'atmosphere-twilight', 'atmosphere-night', 'atmosphere-storm',
'theme-light', 'theme-dark'
);
// Add new atmosphere
root.classList.add(`atmosphere-${atmosphere}`);
// Also set base theme for compatibility
const isDark = ['twilight', 'night', 'storm'].includes(atmosphere);
root.classList.add(isDark ? 'theme-dark' : 'theme-light');
}
// Auto-update based on time
function initAtmosphericUI() {
const updateAtmosphere = () => {
const hour = new Date().getHours();
setAtmosphere(getAtmosphereFromTime(hour));
};
updateAtmosphere();
// Update every 15 minutes
setInterval(updateAtmosphere, 15 * 60 * 1000);
}Weather API Integration
天气API集成
typescript
interface WeatherCondition {
main: 'Clear' | 'Clouds' | 'Rain' | 'Thunderstorm' | 'Snow' | 'Mist';
description: string;
}
function getAtmosphereFromWeather(
weather: WeatherCondition,
hour: number
): Atmosphere {
// Storm conditions override time
if (weather.main === 'Thunderstorm') return 'storm';
// Heavy overcast dims everything
if (weather.main === 'Clouds' && weather.description.includes('overcast')) {
return hour >= 19 || hour < 6 ? 'night' : 'twilight';
}
// Default to time-based
return getAtmosphereFromTime(hour);
}typescript
interface WeatherCondition {
main: 'Clear' | 'Clouds' | 'Rain' | 'Thunderstorm' | 'Snow' | 'Mist';
description: string;
}
function getAtmosphereFromWeather(
weather: WeatherCondition,
hour: number
): Atmosphere {
// Storm conditions override time
if (weather.main === 'Thunderstorm') return 'storm';
// Heavy overcast dims everything
if (weather.main === 'Clouds' && weather.description.includes('overcast')) {
return hour >= 19 || hour < 6 ? 'night' : 'twilight';
}
// Default to time-based
return getAtmosphereFromTime(hour);
}Anti-Patterns to Avoid
需避免的反模式
1. Pure Black Background (#000000)
1. 纯黑色背景(#000000)
Problem: Causes eye strain, harsh contrast, OLED "smearing" on scroll
Solution: Use near-black like #0c1222, #121212, or #1a1a2e
问题: 引发视疲劳、高对比度刺眼、OLED设备滚动时出现“拖影”
解决方案: 使用近黑色,如#0c1222、#121212或#1a1a2e
2. Pure White Text (#FFFFFF) on Dark
2. 深色背景配纯白色文本(#FFFFFF)
Problem: Too harsh, causes halation for astigmatism users
Solution: Use off-white like #f1f5f9, #e2e8f0
问题: 过于刺眼,散光用户会出现光晕效应
解决方案: 使用米白色,如#f1f5f9、#e2e8f0
3. Same Colors for Both Themes
3. 明暗模式使用完全相同的颜色
Problem: Teal that looks great on white becomes invisible on dark
Solution: Use brighter variants in dark mode (ocean-400 instead of ocean-600)
问题: 在白色背景上效果极佳的蓝绿色,在深色背景上可能完全不可见
解决方案: 深色模式中使用更亮的颜色变体(比如用ocean-400替代ocean-600)
4. Shadows in Dark Mode
4. 深色模式中使用阴影
Problem: Shadows disappear against dark backgrounds
Solution: Use lighter surface colors for elevation instead
问题: 阴影在深色背景下几乎不可见
解决方案: 改用更浅的表面颜色来表现层级
5. Inverted Light Mode Colors
5. 直接反转浅色模式颜色
Problem: Simply inverting creates jarring, unnatural results
Solution: Design dark mode as its own coherent system
问题: 简单反转会产生不协调、不自然的效果
解决方案: 将深色模式作为独立的完整系统来设计
6. Ignoring System Preference
6. 忽略系统配色偏好
Problem: Forcing dark mode ignores user's system-wide preference
Solution: Default to , allow override
prefers-color-scheme问题: 强制使用深色模式会忽略用户的系统全局偏好
解决方案: 默认遵循,同时允许用户手动覆盖
prefers-color-scheme7. Flash of Wrong Theme
7. 主题闪烁问题
Problem: Page loads light, then flashes to dark
Solution: Inline script in before CSS loads
<head>问题: 页面先加载浅色模式,再闪变为深色模式
解决方案: 在中、CSS加载前插入内联脚本
<head>Testing Checklist
测试检查清单
Visual Testing
视觉测试
- Primary text readable on all backgrounds (4.5:1+)
- Secondary text readable (4.5:1+)
- Muted text acceptable (3:1+ for large, 4.5:1+ for normal)
- Interactive elements distinguishable
- Focus states clearly visible
- Disabled states identifiable (but not required contrast)
- Elevation hierarchy clear without shadows
- No harsh white/black combinations
- 所有背景上的主文本都可读(对比度≥4.5:1)
- 次要文本可读(对比度≥4.5:1)
- 弱化文本符合要求(大文本≥3:1,常规文本≥4.5:1)
- 交互元素可清晰区分
- 焦点状态清晰可见
- 禁用元素可识别(无强制对比度要求)
- 无需阴影即可清晰区分层级
- 无刺眼的白/黑组合
Functional Testing
功能测试
- Theme toggle works correctly
- System preference respected on first load
- Theme persists across page reloads
- No flash of wrong theme
- Images adapt appropriately
- Code blocks readable
- Charts/graphs remain legible
- Form elements properly styled
- 主题切换功能正常
- 首次加载时遵循系统偏好
- 主题在页面刷新后可保留
- 无主题闪烁问题
- 图片可自适应主题
- 代码块可读
- 图表保持清晰
- 表单元素样式正确
Device Testing
设备测试
- OLED screens (check for smearing on scroll)
- LCD screens (check for backlight bleed visibility)
- High brightness outdoor use
- Low brightness night use
- Color blindness simulation
- OLED屏幕(检查滚动拖影)
- LCD屏幕(检查背光漏光可见度)
- 高亮度户外场景
- 低亮度夜间场景
- 色盲模拟测试
Industry References
行业参考
Material Design 3
Material Design 3
- Base dark surface: #121212
- Tonal elevation with white overlay (5-11%)
- Primary colors at 80% lightness for dark mode
- 15.8:1 target contrast for elevated surfaces
- 深色基础表面:#121212
- 白色叠加(5-11%)实现色调层级
- 深色模式中主色亮度为80%
- 提升表面的目标对比度为15.8:1
Apple Human Interface Guidelines
Apple人机界面指南
- Respect system appearance setting
- Semantic colors that adapt automatically
- Increased vibrancy in dark mode
- Base system background: dynamic (not static black)
- 尊重系统外观设置
- 自动适配的语义化颜色
- 深色模式中增强活力感
- 基础系统背景为动态色(非静态黑色)
Figma
Figma
- Background: #2c2c2c (base), #383838 (elevated)
- Text: #ffffff (primary), #b3b3b3 (secondary)
- Accent: #0d99ff (brand blue, brightened for dark)
- 背景:#2c2c2c(基础)、#383838(提升层)
- 文本:#ffffff(主文本)、#b3b3b3(次要文本)
- 强调色:#0d99ff(品牌蓝,深色模式中提亮)
Discord
Discord
- Background: #36393f (main), #2f3136 (sidebar)
- Text: #dcddde (primary), #72767d (muted)
- Accent: #5865f2 (blurple, same in both modes)
- 背景:#36393f(主背景)、#2f3136(侧边栏)
- 文本:#dcddde(主文本)、#72767d(弱化文本)
- 强调色:#5865f2(蓝紫色,明暗模式保持一致)
Slack
Slack
- Background: #1a1d21 (base), #222529 (elevated)
- Uses colored sidebars in dark mode
- Maintains brand identity while adapting
- 背景:#1a1d21(基础)、#222529(提升层)
- 深色模式中使用彩色侧边栏
- 在保持品牌辨识度的同时实现适配
Companion Skills
配套技能
| Skill | Handoff Point |
|---|---|
| After designing tokens, audit specific pairs |
| Integrate dark mode into broader design system |
| Overall visual direction and brand alignment |
| Generating initial color palettes |
Remember: Dark mode isn't the absence of light—it's the careful orchestration of luminance to guide attention, reduce strain, and create atmosphere.
| 技能 | 交接场景 |
|---|---|
| 设计令牌后,审核特定颜色对的对比度 |
| 将深色模式整合到更完整的设计系统中 |
| 整体视觉方向与品牌对齐 |
| 生成初始调色板 |
记住:深色模式并非只是去除光线——它是对亮度的精心编排,用于引导注意力、减少疲劳并营造氛围。