深色模式¶
Django Admin Dashboards 提供全面的深色模式支持,可无缝集成到 Django Admin 的内置主题系统中。本指南涵盖从基础使用到高级自定义的所有内容。
目录¶
概述¶
什么是深色模式?¶
深色模式是一种在深色背景上使用浅色文本和图标的配色方案。优点包括:
- 减少眼睛疲劳:在低光环境下
- 提高电池寿命:在 OLED/AMOLED 显示屏上
- 更好的可读性:对部分用户而言
- 现代外观:符合当代设计趋势
Django Admin Dashboards 中的深色模式工作原理¶
- 主题检测:系统通过
prefers-color-schemeCSS 媒体查询检测用户主题偏好 - Django Admin 集成:利用 Django Admin 的内置主题切换系统
- URL 参数控制:允许通过 URL 参数强制指定主题
- CSS 自定义属性:使用 CSS 变量实现跨组件的一致主题
- 组件感知:所有组件自动适应深色模式
与 Django Admin 的集成¶
Django Admin Dashboards 与 Django Admin 的主题系统集成:
- 在
<html>元素上使用相同的data-theme属性 - 尊重 Django Admin 的主题切换按钮(当
_dark_mode_on=auto时) - 与 Django Admin 的调色板保持一致
- 支持所有三种主题模式:
light、dark和auto
快速开始¶
通过 URL 启用深色模式¶
控制深色模式最简单的方法是通过 URL 参数:
# 强制深色模式
/admin/?_dark_mode_on=true
# 强制浅色模式
/admin/?_dark_mode_on=false
# 跟随 Django Admin 主题(默认)
/admin/?_dark_mode_on=auto
数据看板配置¶
在数据看板中启用深色模式支持:
from django_admin_dashboards.base import Dashboard
class MyDashboard(Dashboard):
# 默认启用深色模式
supports_dark_mode = True
def get_context_data(self, request):
context = super().get_context_data(request)
# 深色模式上下文变量
context['is_dark_mode'] = self.is_dark_mode(request)
context['current_theme'] = self.get_current_theme(request)
return context
在模板中检查当前主题¶
URL 参数¶
_dark_mode_on 参数¶
使用 _dark_mode_on URL 参数控制深色模式行为:
| 值 | 描述 | 示例 |
|---|---|---|
true |
强制深色模式 | /admin/?_dark_mode_on=true |
false |
强制浅色模式 | /admin/?_dark_mode_on=false |
auto |
跟随 Django Admin 主题 | /admin/?_dark_mode_on=auto |
1, yes |
替代真值 | /admin/?_dark_mode_on=1 |
0, no |
替代假值 | /admin/?_dark_mode_on=0 |
参数持久性¶
深色模式偏好保存在 localStorage 中:
// 当使用 _dark_mode_on=true 时:
localStorage.setItem('theme', 'dark');
// 当使用 _dark_mode_on=false 时:
localStorage.setItem('theme', 'light');
// 当使用 _dark_mode_on=auto 时:
localStorage.setItem('theme', 'auto');
清除深色模式覆盖¶
要返回系统默认:
与其他参数组合¶
深色模式参数可以与其他数据看板控制参数组合:
# 深色模式 + 全屏
/admin/?_dark_mode_on=true&_hide_others_in_fullscreen=true
# 浅色模式 + 强制隐藏
/admin/?_dark_mode_on=false&_force_hide_others=true
# 自动主题 + 自定义视图
/admin/?_dark_mode_on=auto&_view=detailed
数据看板配置¶
启用/禁用深色模式支持¶
在数据看板级别控制深色模式:
class MyDashboard(Dashboard):
# 启用深色模式支持(默认:True)
supports_dark_mode = True
# 自定义深色模式 CSS 类
dark_mode_css_class = 'custom-dark-mode'
# 主题特定设置
dark_mode_settings = {
'primary_color': '#3b82f6',
'background_color': '#1f2937',
'text_color': '#f9fafb',
}
light_mode_settings = {
'primary_color': '#2563eb',
'background_color': '#ffffff',
'text_color': '#111827',
}
主题检测方法¶
class ThemeAwareDashboard(Dashboard):
def get_current_theme(self, request):
"""基于多种因素确定当前主题"""
# 1. 检查 URL 参数(最高优先级)
url_theme = request.GET.get('_dark_mode_on')
if url_theme in ['true', 'false', 'auto']:
return self._normalize_theme(url_theme)
# 2. 通过 JavaScript 上下文检查 localStorage
js_theme = getattr(request, 'theme_from_js', None)
if js_theme:
return js_theme
# 3. 检查 Django Admin 的主题
admin_theme = get_admin_theme(request)
if admin_theme:
return admin_theme
# 4. 默认为 auto(跟随系统)
return 'auto'
def _normalize_theme(self, theme):
"""规范化主题值"""
if theme in ['true', '1', 'yes']:
return 'dark'
elif theme in ['false', '0', 'no']:
return 'light'
else:
return theme # 'auto' 或未更改
上下文变量¶
将主题信息添加到模板上下文:
class ContextRichDashboard(Dashboard):
def get_context_data(self, request):
context = super().get_context_data(request)
# 主题信息
context['theme_info'] = {
'current': self.get_current_theme(request),
'is_dark': self.is_dark_mode(request),
'is_light': self.is_light_mode(request),
'is_auto': self.is_auto_theme(request),
'supports_dark': self.supports_dark_mode,
}
# 主题特定设置
if context['theme_info']['is_dark']:
context.update(self.dark_mode_settings)
else:
context.update(self.light_mode_settings)
return context
CSS 实现¶
CSS 自定义属性(变量)¶
Django Admin Dashboards 使用 CSS 自定义属性进行主题化:
:root {
/* 浅色主题(默认) */
--dashboard-bg-color: #ffffff;
--dashboard-text-color: #111827;
--dashboard-primary-color: #2563eb;
--dashboard-border-color: #e5e7eb;
--dashboard-card-bg: #f9fafb;
}
[data-theme="dark"] {
/* 深色主题覆盖 */
--dashboard-bg-color: #1f2937;
--dashboard-text-color: #f9fafb;
--dashboard-primary-color: #3b82f6;
--dashboard-border-color: #374151;
--dashboard-card-bg: #111827;
}
组件样式¶
组件使用 CSS 变量自动适应深色模式:
/* 基础组件样式 */
.dashboard-component {
background-color: var(--dashboard-card-bg);
color: var(--dashboard-text-color);
border: 1px solid var(--dashboard-border-color);
}
/* 深色模式特定调整 */
[data-theme="dark"] .dashboard-component {
/* 额外的深色模式调整 */
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
}
/* 组件部分 */
.dashboard-component-header {
border-bottom: 1px solid var(--dashboard-border-color);
color: var(--dashboard-primary-color);
}
.dashboard-component-body {
padding: 1rem;
}
高对比度支持¶
/* 高对比度模式支持 */
@media (prefers-contrast: high) {
[data-theme="dark"] {
--dashboard-border-color: #ffffff;
--dashboard-text-color: #ffffff;
}
[data-theme="light"] {
--dashboard-border-color: #000000;
--dashboard-text-color: #000000;
}
}
/* 减少运动支持 */
@media (prefers-reduced-motion: reduce)
}
CSS 文件结构¶
深色模式样式在 dashboard.css 中组织:
/* dashboard.css - 深色模式部分 */
/* 1. CSS 变量 */
:root { /* 浅色主题变量 */ }
[data-theme="dark"] { /* 深色主题变量 */ }
/* 2. 基础元素 */
body { /* 基础 body 样式 */ }
[data-theme="dark"] body { /* 深色 body 样式 */ }
/* 3. 数据看板容器 */
.dashboard { /* 数据看板容器 */ }
[data-theme="dark"] .dashboard { /* 深色数据看板 */ }
/* 4. 组件 */
.dashboard .card { /* 卡片组件 */ }
[data-theme="dark"] .dashboard .card { /* 深色卡片 */ }
/* 5. 表格 */
.dashboard table { /* 表格样式 */ }
[data-theme="dark"] .dashboard table { /* 深色表格 */ }
/* 6. 表单和输入 */
.dashboard input, .dashboard select { /* 表单元素 */ }
[data-theme="dark"] .dashboard input { /* 深色表单元素 */ }
/* 7. 工具类 */
.dashboard-fullscreen-mode { /* 全屏模式 */ }
[data-theme="dark"].dashboard-fullscreen-mode { /* 深色全屏 */ }
JavaScript 集成¶
主题检测和切换¶
// 主题检测和切换
class DashboardThemeManager {
constructor() {
this.theme = localStorage.getItem('theme') || 'auto';
this.initialize();
}
initialize() {
// 首先检查 URL 参数
const urlTheme = this.getUrlParameter('_dark_mode_on');
if (urlTheme) {
this.applyTheme(urlTheme);
return;
}
// 应用当前主题
this.applyTheme(this.theme);
// 监听 Django Admin 的主题变化
this.listenToAdminThemeChanges();
}
getUrlParameter(name) {
// URL 参数解析逻辑
const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
const results = regex.exec(window.location.href);
return results ? decodeURIComponent(results[2].replace(/\+/g, ' ')) : null;
}
applyTheme(theme) {
// 规范化主题值
theme = this.normalizeTheme(theme);
// 更新 localStorage
localStorage.setItem('theme', theme);
// 更新 DOM
if (theme === 'auto') {
// 让 Django Admin 处理自动检测
document.documentElement.removeAttribute('data-theme');
} else {
document.documentElement.setAttribute('data-theme', theme);
}
// 分发事件
this.dispatchThemeChangeEvent(theme);
}
normalizeTheme(theme) {
if (theme === 'true' || theme === '1' || theme === 'yes') {
return 'dark';
} else if (theme === 'false' || theme === '0' || theme === 'no') {
return 'light';
}
return theme; // 'auto', 'dark', 或 'light'
}
listenToAdminThemeChanges() {
// 监听 Django Admin 主题变化
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'data-theme') {
const theme = document.documentElement.getAttribute('data-theme');
if (theme && this.theme === 'auto') {
// Django Admin 更改了主题
this.dispatchThemeChangeEvent(theme);
}
}
});
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme']
});
}
dispatchThemeChangeEvent(theme) {
window.dispatchEvent(new CustomEvent('dashboardthemechange', {
detail: {
theme: theme,
isDark: theme === 'dark',
isLight: theme === 'light',
isAuto: theme === 'auto'
}
}));
}
}
// 初始化主题管理器
document.addEventListener('DOMContentLoaded', () => {
window.dashboardThemeManager = new DashboardThemeManager();
});
主题变更事件¶
在 JavaScript 中监听主题变化:
// 监听主题变化
window.addEventListener('dashboardthemechange', (event) => {
const { theme, isDark, isLight, isAuto } = event.detail;
console.log(`主题更改为:${theme}`);
// 基于主题更新 UI
if (isDark) {
document.body.classList.add('theme-dark');
document.body.classList.remove('theme-light');
} else if (isLight) {
document.body.classList.add('theme-light');
document.body.classList.remove('theme-dark');
}
// 更新图表和可视化
updateChartsForTheme(theme);
});
// 手动主题切换
function switchTheme(theme) {
// 更新 URL 参数
const url = new URL(window.location);
url.searchParams.set('_dark_mode_on', theme);
window.history.replaceState({}, '', url);
// 应用主题
if (window.dashboardThemeManager) {
window.dashboardThemeManager.applyTheme(theme);
}
}
与 Django Admin 的 theme.js 集成¶
// 与 Django Admin 的主题系统协作
function integrateWithAdminTheme() {
// 检查 Django Admin 的 theme.js 是否已加载
if (typeof window.theme !== 'undefined') {
// 使用 Django Admin 的主题对象
const adminTheme = window.theme;
// 与数据看板主题管理器同步
adminTheme.addEventListener('change', (theme) => {
if (window.dashboardThemeManager.theme === 'auto') {
// 数据看板正在跟随 Django Admin 主题
window.dashboardThemeManager.dispatchThemeChangeEvent(theme);
}
});
// 初始同步
if (window.dashboardThemeManager.theme === 'auto') {
window.dashboardThemeManager.dispatchThemeChangeEvent(adminTheme.current);
}
}
}
自定义主题¶
创建自定义配色方案¶
使用自定义配色方案扩展默认深色模式:
# dashboard.py
class CustomThemedDashboard(Dashboard):
def get_theme_settings(self, request):
"""返回主题特定设置"""
base_settings = super().get_theme_settings(request)
# 添加自定义主题设置
if self.is_dark_mode(request):
base_settings.update({
'accent_color': '#8b5cf6',
'success_color': '#10b981',
'warning_color': '#f59e0b',
'danger_color': '#ef4444',
'info_color': '#06b6d4',
})
return base_settings
/* custom-theme.css */
[data-theme="dark"] {
/* 自定义深色主题 */
--dashboard-accent-color: #8b5cf6;
--dashboard-success-color: #10b981;
--dashboard-warning-color: #f59e0b;
--dashboard-danger-color: #ef4444;
--dashboard-info-color: #06b6d4;
/* 组件特定自定义 */
--dashboard-card-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3);
--dashboard-border-radius: 12px;
}
/* 将自定义主题应用于特定数据看板 */
.dashboard-custom-theme[data-theme="dark"] {
/* 额外的自定义 */
background: linear-gradient(135deg, #1f2937 0%, #111827 100%);
}
主题变体¶
# 多个主题变体
class MultiThemeDashboard(Dashboard):
THEME_VARIANTS = {
'default': {
'dark': {
'primary': '#3b82f6',
'background': '#1f2937',
},
'light': {
'primary': '#2563eb',
'background': '#ffffff',
}
},
'blue': {
'dark': {
'primary': '#60a5fa',
'background': '#1e3a8a',
},
'light': {
'primary': '#1d4ed8',
'background': '#dbeafe',
}
},
'green': {
'dark': {
'primary': '#34d399',
'background': '#064e3b',
},
'light': {
'primary': '#059669',
'background': '#d1fae5',
}
}
}
def get_theme_variant(self, request):
"""从 URL 或用户偏好获取当前主题变体"""
return request.GET.get('_theme_variant', 'default')
def get_context_data(self, request):
context = super().get_context_data(request)
variant = self.get_theme_variant(request)
theme_type = 'dark' if self.is_dark_mode(request) else 'light'
# 添加变体特定的 CSS 类
context['dashboard_css_class'] = f'dashboard-variant-{variant}'
# 将变体颜色添加到上下文
if variant in self.THEME_VARIANTS:
context.update(self.THEME_VARIANTS[variant][theme_type])
return context
动态主题切换¶
// 带有平滑过渡的动态主题切换
class DynamicThemeSwitcher {
constructor() {
this.transitionDuration = 300;
this.init();
}
init() {
// 添加过渡样式
this.addTransitionStyles();
// 监听主题变化
window.addEventListener('dashboardthemechange', (event) => {
this.handleThemeChange(event.detail);
});
}
addTransitionStyles() {
const style = document.createElement('style');
style.textContent = `
.dashboard,
.dashboard * {
transition: background-color ${this.transitionDuration}ms ease,
color ${this.transitionDuration}ms ease,
border-color ${this.transitionDuration}ms ease,
box-shadow ${this.transitionDuration}ms ease;
}
/* 在初始加载期间禁用过渡 */
.dashboard-preload * {
transition: none !important;
}
`;
document.head.appendChild(style);
// 初始加载后移除 preload 类
setTimeout(() => {
document.body.classList.remove('dashboard-preload');
}, 100);
}
handleThemeChange(themeDetail) {
const { theme, isDark } = themeDetail;
// 添加过渡类
document.body.classList.add('dashboard-theme-transitioning');
// 更新主题
if (theme === 'auto') {
document.documentElement.removeAttribute('data-theme');
} else {
document.documentElement.setAttribute('data-theme', theme);
}
// 过渡后移除过渡类
setTimeout(() => {
document.body.classList.remove('dashboard-theme-transitioning');
}, this.transitionDuration);
}
}
组件深色模式¶
组件特定的深色模式样式¶
组件可以具有主题特定样式:
# components.py
class DarkModeAwareComponent(Component):
def get_context_data(self, request):
context = super().get_context_data(request)
# 添加主题信息
context['is_dark_mode'] = self.dashboard.is_dark_mode(request)
context['theme_class'] = 'dark-mode' if context['is_dark_mode'] else 'light-mode'
# 主题特定数据
if context['is_dark_mode']:
context['icon_color'] = '#f9fafb'
context['background_color'] = '#374151'
else:
context['icon_color'] = '#111827'
context['background_color'] = '#f3f4f6'
return context
<div class="dashboard-component {{ theme_class }}"
style="background-color: {{ background_color }};">
<div class="component-header">
<i class="ri-icon" style="color: {{ icon_color }};"></i>
<h3>{{ title }}</h3>
</div>
<div class="component-body">
{% if is_dark_mode %}
<p class="dark-mode-note">
正在深色模式下查看
</p>
{% endif %}
{{ content }}
</div>
</div>
图表组件主题化¶
# chart_component.py
class ThemedChartComponent(ChartComponent):
def get_chart_options(self, request):
options = super().get_chart_options(request)
if self.dashboard.is_dark_mode(request):
# 深色模式图表选项
options.update({
'backgroundColor': '#1f2937',
'color': '#f9fafb',
'borderColor': '#374151',
'plugins': {
'legend': {
'labels': {
'color': '#f9fafb'
}
}
},
'scales': {
'x': {
'grid': {
'color': '#374151'
},
'ticks': {
'color': '#9ca3af'
}
},
'y': {
'grid': {
'color': '#374151'
},
'ticks': {
'color': '#9ca3af'
}
}
}
})
return options
表格组件主题化¶
/* 表格深色模式样式 */
[data-theme="dark"] .dashboard table {
background-color: #111827;
color: #f9fafb;
border-color: #374151;
}
[data-theme="dark"] .dashboard table thead {
background-color: #1f2937;
border-bottom: 2px solid #374151;
}
[data-theme="dark"] .dashboard table th {
color: #d1d5db;
border-color: #374151;
}
[data-theme="dark"] .dashboard table td {
border-color: #374151;
}
[data-theme="dark"] .dashboard table tr:hover {
background-color: #1f2937;
}
[data-theme="dark"] .dashboard table tr:nth-child(even) {
background-color: rgba(31, 41, 55, 0.5);
}
测试深色模式¶
手动测试清单¶
使用此清单测试深色模式功能:
-
URL 参数测试
-
/?_dark_mode_on=true→ 应强制深色模式 -
/?_dark_mode_on=false→ 应强制浅色模式 -
/?_dark_mode_on=auto→ 应跟随 Django Admin 主题 -
/?_dark_mode_on=invalid→ 应默认为 auto -
主题持久性
-
刷新页面 → 主题应持久
-
打开新标签页 → 主题应持久
-
清除 localStorage → 应返回默认
-
Django Admin 集成
-
主题切换按钮应在
_dark_mode_on=auto时工作 -
应检测系统主题变化
-
主题应与 Django Admin 同步
-
组件测试
-
所有组件应适应深色模式
-
两种主题下文本应可读
-
颜色应有足够的对比度
-
图像和图标应可见
-
浏览器兼容性
-
Chrome、Firefox、Safari、Edge
-
移动浏览器
-
不同屏幕尺寸
自动化测试¶
# tests.py
from django.test import TestCase
from django.test.client import RequestFactory
class DarkModeTests(TestCase):
def setUp(self):
self.factory = RequestFactory()
self.dashboard = AuthAppDashboard()
def test_dark_mode_url_parameter(self):
"""测试深色模式 URL 参数解析"""
# 测试真值
request = self.factory.get('/admin/?_dark_mode_on=true')
self.assertTrue(self.dashboard.is_dark_mode(request))
request = self.factory.get('/admin/?_dark_mode_on=1')
self.assertTrue(self.dashboard.is_dark_mode(request))
request = self.factory.get('/admin/?_dark_mode_on=yes')
self.assertTrue(self.dashboard.is_dark_mode(request))
# 测试假值
request = self.factory.get('/admin/?_dark_mode_on=false')
self.assertFalse(self.dashboard.is_dark_mode(request))
request = self.factory.get('/admin/?_dark_mode_on=0')
self.assertFalse(self.dashboard.is_dark_mode(request))
request = self.factory.get('/admin/?_dark_mode_on=no')
self.assertFalse(self.dashboard.is_dark_mode(request))
# 测试 auto
request = self.factory.get('/admin/?_dark_mode_on=auto')
self.assertEqual(self.dashboard.get_current_theme(request), 'auto')
def test_dark_mode_context(self):
"""测试深色模式上下文变量"""
# 深色模式请求
request = self.factory.get('/admin/?_dark_mode_on=true')
context = self.dashboard.get_context_data(request)
self.assertIn('is_dark_mode', context)
self.assertTrue(context['is_dark_mode'])
self.assertIn('current_theme', context)
self.assertEqual(context['current_theme'], 'dark')
# 浅色模式请求
request = self.factory.get('/admin/?_dark_mode_on=false')
context = self.dashboard.get_context_data(request)
self.assertIn('is_dark_mode', context)
self.assertFalse(context['is_dark_mode'])
self.assertIn('current_theme', context)
self.assertEqual(context['current_theme'], 'light')
def test_dark_mode_css_classes(self):
"""测试渲染 HTML 中的深色模式 CSS 类"""
from django.test import Client
from django.urls import reverse
client = Client()
# 测试深色模式渲染带有深色类
response = client.get(reverse('admin:index') + '?_dark_mode_on=true')
self.assertContains(response, 'data-theme="dark"')
self.assertContains(response, 'dashboard-dark-mode')
# 测试浅色模式渲染不带深色类
response = client.get(reverse('admin:index') + '?_dark_mode_on=false')
self.assertContains(response, 'data-theme="light"')
self.assertNotContains(response, 'dashboard-dark-mode')
可访问性测试¶
# accessibility_tests.py
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
@pytest.mark.accessibility
class TestDarkModeAccessibility:
@pytest.fixture
def driver(self):
driver = webdriver.Chrome()
yield driver
driver.quit()
def test_contrast_ratio(self, driver):
"""测试深色模式下的对比度比率"""
# 加载深色模式页面
driver.get('http://localhost:8000/admin/?_dark_mode_on=true')
# 测试文本对比度
text_elements = driver.find_elements(By.CSS_SELECTOR, '.dashboard p, .dashboard h1, .dashboard h2, .dashboard h3')
for element in text_elements:
color = element.value_of_css_property('color')
bg_color = element.value_of_css_property('background-color')
# 计算对比度比率(简化版)
# 在实际测试中,使用适当的对比度比率计算
assert color != bg_color, f"文本颜色与背景匹配:{element.text}"
def test_focus_indicators(self, driver):
"""测试深色模式下焦点指示器是否可见"""
driver.get('http://localhost:8000/admin/?_dark_mode_on=true')
# 查找可聚焦元素
focusable = driver.find_elements(By.CSS_SELECTOR, 'button, [href], input, select, textarea, [tabindex]')
for element in focusable:
# 聚焦元素
driver.execute_script("arguments[0].focus();", element)
# 检查焦点样式
outline = element.value_of_css_property('outline')
box_shadow = element.value_of_css_property('box-shadow')
# 焦点应可见
assert outline != 'none' or box_shadow != 'none', \
f"元素上焦点不可见:{element.tag_name}"
故障排除¶
常见问题和解决方案¶
1. 深色模式未应用¶
症状:URL 参数无效,主题不改变。
解决方案:
- 检查 JavaScript 控制台是否有错误 -
验证数据看板是否设置了
supports_dark_mode = True- 清除浏览器缓存和 localStorage - 检查与!important规则的 CSS 冲突
// 调试深色模式
console.log('当前主题:', localStorage.getItem('theme'));
console.log('URL 参数:', new URLSearchParams(window.location.search).get('_dark_mode_on'));
console.log('DOM data-theme:', document.documentElement.getAttribute('data-theme'));
2. 主题切换按钮不工作¶
症状:Django Admin 主题切换按钮不改变数据看板主题。
解决方案:
- 确保设置了
_dark_mode_on=auto(或没有_dark_mode_on参数) - 检查数据看板 JavaScript 是否干扰了 Django Admin 的 theme.js - 验证数据看板是否正在监听主题变更事件
// 检查 theme.js 是否已加载
if (typeof window.theme !== 'undefined') {
console.log('Django Admin theme.js 已加载:', window.theme.current);
}
3. 错误主题闪烁 (FOWT)¶
症状:在深色模式应用之前短暂闪烁浅色主题。
解决方案:
- 在 HTML head 中添加初始主题检测 - 使用不依赖 JavaScript
进行初始渲染的 CSS - 在服务器端添加
data-theme属性
<script>
// 立即设置主题以防止闪烁
(function() {
const urlParams = new URLSearchParams(window.location.search);
const darkModeParam = urlParams.get('_dark_mode_on');
if (darkModeParam === 'true' || darkModeParam === '1' || darkModeParam === 'yes') {
document.documentElement.setAttribute('data-theme', 'dark');
} else if (darkModeParam === 'false' || darkModeParam === '0' || darkModeParam === 'no') {
document.documentElement.setAttribute('data-theme', 'light');
}
// 如果是 'auto' 或未指定,让 Django Admin 处理
})();
</script>
4. CSS 变量不工作¶
症状:颜色不随主题改变。
解决方案:
- 检查 CSS 变量定义是否在正确的作用域内 - 验证 CSS 在主题设置后加载 - 使用更简单的 CSS 进行测试以隔离问题
/* 测试 CSS 变量是否工作 */
.test-var {
color: var(--dashboard-text-color, red); /* 回退到红色 */
}
/* 在浏览器开发者工具中强制检查 */
[data-theme="dark"] {
--dashboard-text-color: #f9fafb !important;
}
5. 性能问题¶
症状:主题切换缓慢,页面卡顿。
解决方案:
- 减少 CSS 选择器的复杂性 - 最小化使用
!important- 谨慎使用 CSS 过渡 - 防抖快速主题更改
// 防抖主题更改
let themeChangeTimeout;
function debouncedThemeChange(theme) {
clearTimeout(themeChangeTimeout);
themeChangeTimeout = setTimeout(() => {
applyTheme(theme);
}, 100); // 100ms 防抖
}
调试工具¶
浏览器开发者工具¶
- 元素面板:检查
<html>元素上的data-theme属性 - 控制台:检查 JavaScript 错误
- 网络面板:验证 CSS 文件是否正在加载
- 应用程序面板:检查 localStorage 值
调试 CSS¶
/* 添加调试样式 */
[data-theme="dark"] * {
outline: 1px solid rgba(255, 0, 0, 0.1) !important;
}
/* 高亮主题特定元素 */
[data-theme="dark"]::before {
content: '深色模式已激活';
position: fixed;
top: 0;
right: 0;
background: red;
color: white;
padding: 5px;
z-index: 9999;
font-size: 12px;
}
JavaScript 调试助手¶
// 添加到浏览器控制台进行调试
window.debugDarkMode = function() {
console.group('深色模式调试');
console.log('URL:', window.location.href);
console.log('参数:', new URLSearchParams(window.location.search).toString());
console.log('localStorage 主题:', localStorage.getItem('theme'));
console.log('DOM data-theme:', document.documentElement.getAttribute('data-theme'));
console.log('CSS 变量:');
console.log(' --dashboard-bg-color:', getComputedStyle(document.documentElement)
.getPropertyValue('--dashboard-bg-color').trim());
console.groupEnd();
};
获取帮助¶
如果仍有问题:
- 检查文档:查看本指南和其他文档
- 搜索问题:检查 GitHub 仓库是否有类似问题
- 创建最小示例:在最小测试用例中重现问题
- 报告问题:包括浏览器版本、Django 版本和错误消息