Bootstrap: 暗色模式
最后更新:2026-08-26
1. 本课导读
(1) 前置知识
- Bootstrap Sass 定制流程(Lesson 20)
- CSS 自定义属性(CSS 变量)基础
- JavaScript DOM 操作基础
(2) 🎯 你将学到
- 使用
data-bs-theme属性启用暗色模式 - 实现主题切换按钮 + localStorage 持久化
- 自动跟随系统
prefers-color-scheme偏好 - 自定义暗色模式 CSS 变量
- 掌握图片、组件在暗色模式下的适配技巧
(3) 痛点
Charlie 的管理后台需要添加"日间/夜间模式"切换按钮——传统实现需要为每个组件写两套样式,维护成本极高,且切换时容易出现样式遗漏。
(4) 解法
Bootstrap 5.3 引入了原生暗色模式支持。通过 data-bs-theme="dark" 属性,整个页面或单个组件可以在亮色/暗色之间切换,所有组件样式自动适配。只需一行属性的变化即可实现全局主题切换。
理解方式: 暗色模式 = 主题"滤镜"——Bootstrap 5.3 中所有组件都有
light和dark两套 CSS 变量,data-bs-theme决定使用哪一套。
(5) 收益
Charlie 只需添加一个切换按钮,通过一个 data-bs-theme 属性变化即可实现全局主题切换,所有组件样式自动适配暗色模式,无需手写任何暗色覆盖样式。
2. 基础暗色模式
▶ 示例:基础暗色模式演示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dark Mode Demo</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container py-4">
<!-- Light mode section -->
<h5>Light Mode (default)</h5>
<div class="card p-3 mb-3">
<p>This card uses the default light theme.</p>
<button class="btn btn-primary">Button</button>
</div>
<!-- Dark mode section -->
<div data-bs-theme="dark">
<h5>Dark Mode</h5>
<div class="card p-3 mb-3">
<p>This card automatically adapts to dark theme.</p>
<button class="btn btn-primary">Button</button>
<div class="alert alert-warning mt-2">Alert also adapts.</div>
</div>
</div>
</div>
</body>
</html>
输出: Bootstrap 5.3 样式生效的组件效果(如按钮、卡片、轮播、折叠等),页面默认采用 Bootstrap 默认主题(亮色)和响应式网格。
3. 三种作用域
| 作用域 | 写法 | 说明 |
|---|---|---|
| 全局 | <html data-bs-theme="dark"> |
整个页面变成暗色模式 |
| 区域 | <div data-bs-theme="dark"> |
指定容器内的组件变为暗色 |
| 组件 | <div class="card" data-bs-theme="dark"> |
单个卡片暗色 |
<!-- Global dark mode -->
<html data-bs-theme="dark">
<!-- all components use dark theme -->
</html>
输出: Bootstrap 5.3 样式生效的组件效果(如按钮、卡片、轮播、折叠等),页面默认采用 Bootstrap 默认主题(亮色)和响应式网格。
4. 主题切换按钮
<button id="themeToggle" class="btn btn-outline-primary">
<i class="bi bi-moon"></i> Dark Mode
</button>
<script>
const toggle = document.getElementById('themeToggle')
toggle.addEventListener('click', () => {
const html = document.documentElement
const current = html.getAttribute('data-bs-theme')
const next = current === 'dark' ? 'light' : 'dark'
html.setAttribute('data-bs-theme', next)
toggle.innerHTML = next === 'dark'
? '<i class="bi bi-sun"></i> Light Mode'
: '<i class="bi bi-moon"></i> Dark Mode'
})
</script>
输出: Bootstrap 5.3 样式生效的组件效果(如按钮、卡片、轮播、折叠等),页面默认采用 Bootstrap 默认主题(亮色)和响应式网格。
(1) 记住用户偏好
<script>
// Check saved preference on load
const saved = localStorage.getItem('theme')
if (saved) {
document.documentElement.setAttribute('data-bs-theme', saved)
}
document.getElementById('themeToggle').addEventListener('click', () => {
const html = document.documentElement
const current = html.getAttribute('data-bs-theme') || 'light'
const next = current === 'dark' ? 'light' : 'dark'
html.setAttribute('data-bs-theme', next)
localStorage.setItem('theme', next)
})
</script>
输出: Bootstrap 5.3 样式生效的组件效果(如按钮、卡片、轮播、折叠等),页面默认采用 Bootstrap 默认主题(亮色)和响应式网格。
5. 自动跟随系统偏好
<script>
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)')
const saved = localStorage.getItem('theme')
if (saved) {
document.documentElement.setAttribute('data-bs-theme', saved)
} else if (prefersDark.matches) {
document.documentElement.setAttribute('data-bs-theme', 'dark')
}
// Listen for system theme change
prefersDark.addEventListener('change', (e) => {
if (!localStorage.getItem('theme')) {
document.documentElement.setAttribute('data-bs-theme', e.matches ? 'dark' : 'light')
}
})
</script>
输出: Bootstrap 5.3 样式生效的组件效果(如按钮、卡片、轮播、折叠等),页面默认采用 Bootstrap 默认主题(亮色)和响应式网格。
6. 深色模式的 CSS 变量
Bootstrap 5.3 使用 CSS 自定义属性实现主题切换。你可以直接在 CSS 中覆盖暗色变量:
/* Custom dark mode colors */
[data-bs-theme="dark"] {
--bs-body-bg: #1a1a2e;
--bs-body-color: #e0e0e0;
--bs-primary: #bb86fc;
--bs-primary-rgb: 187, 134, 252;
--bs-card-bg: #16213e;
--bs-border-color: #2a2a4a;
}
输出: Bootstrap 5.3 样式生效的组件效果(如按钮、卡片、轮播、折叠等),页面默认采用 Bootstrap 默认主题(亮色)和响应式网格。
(1) Sass 方案
// scss/custom.scss
$enable-dark-mode: true;
// Override dark mode colors
$dark-bg: #1a1a2e;
$dark-card-bg: #16213e;
$primary-dark: #bb86fc;
// Import Bootstrap
@import "../node_modules/bootstrap/scss/bootstrap";
输出: Bootstrap 5.3 样式生效的组件效果(如按钮、卡片、轮播、折叠等),页面默认采用 Bootstrap 默认主题(亮色)和响应式网格。
7. 图片暗色自适应
<style>
.dark-show { display: none; }
[data-bs-theme="dark"] .dark-show { display: block; }
[data-bs-theme="dark"] .dark-hide { display: none; }
</style>
<img src="logo-light.png" class="dark-hide" alt="Logo light">
<img src="logo-dark.png" class="dark-show" alt="Logo dark">
输出: Bootstrap 5.3 样式生效的组件效果(如按钮、卡片、轮播、折叠等),页面默认采用 Bootstrap 默认主题(亮色)和响应式网格。 通过自定义 CSS 配合
[data-bs-theme="dark"]选择器来控制暗色模式下的显示/隐藏,例如.dark-show和.dark-hide。
8. 综合示例
▶ 示例:完整暗色模式切换页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dark Mode Toggle Demo</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
</head>
<body>
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container">
<a class="navbar-brand" href="#"><i class="bi bi-moon-stars me-2"></i>Theme Demo</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#nav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="nav">
<ul class="navbar-nav ms-auto align-items-center">
<li class="nav-item"><a class="nav-link active" href="#">Home</a></li>
<li class="nav-item"><a class="nav-link" href="#">Features</a></li>
<li class="nav-item"><a class="nav-link" href="#">Pricing</a></li>
<li class="nav-item ms-2">
<button id="themeToggle" class="btn btn-outline-primary btn-sm rounded-pill">
<i class="bi bi-moon"></i> <span id="themeLabel">Dark</span>
</button>
</li>
</ul>
</div>
</div>
</nav>
<div class="container py-5">
<h1>Dark Mode Demo</h1>
<p class="lead">Click the toggle button to switch between light and dark themes.</p>
<div class="row g-4 mb-5">
<div class="col-md-4">
<div class="card p-4">
<h5>Card Component</h5>
<p>Automatically adapts to theme.</p>
<button class="btn btn-primary">Primary</button>
</div>
</div>
<div class="col-md-4">
<div class="card p-4">
<h5>Alert Example</h5>
<div class="alert alert-success mt-2">Success alert</div>
<div class="alert alert-danger">Danger alert</div>
<div class="alert alert-info">Info alert</div>
</div>
</div>
<div class="col-md-4">
<div class="card p-4">
<h5>Form Elements</h5>
<input type="text" class="form-control mb-2" placeholder="Input field">
<select class="form-select mb-2"><option>Option 1</option></select>
<div class="form-check"><input class="form-check-input" type="checkbox" id="ck"><label class="form-check-label" for="ck">Check me</label></div>
</div>
</div>
</div>
<div class="card p-4">
<h5>Table</h5>
<table class="table">
<thead><tr><th>Name</th><th>Role</th><th>Status</th></tr></thead>
<tbody>
<tr><td>Alice</td><td>Admin</td><td><span class="badge bg-success">Active</span></td></tr>
<tr><td>Bob</td><td>Editor</td><td><span class="badge bg-warning">Pending</span></td></tr>
<tr><td>Charlie</td><td>Viewer</td><td><span class="badge bg-secondary">Inactive</span></td></tr>
</tbody>
</table>
</div>
</div>
<script>
const toggle = document.getElementById('themeToggle');
const label = document.getElementById('themeLabel');
const icon = toggle.querySelector('i');
function setTheme(theme) {
document.documentElement.setAttribute('data-bs-theme', theme);
localStorage.setItem('theme', theme);
const isDark = theme === 'dark';
label.textContent = isDark ? 'Dark' : 'Light';
icon.className = isDark ? 'bi bi-moon' : 'bi bi-sun';
}
const saved = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
setTheme(saved || (prefersDark.matches ? 'dark' : 'light'));
toggle.addEventListener('click', () => {
const current = document.documentElement.getAttribute('data-bs-theme');
setTheme(current === 'dark' ? 'light' : 'dark');
});
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
输出: Bootstrap 5.3 样式生效的组件效果(如按钮、卡片、轮播、折叠等),页面默认采用 Bootstrap 默认主题(亮色)和响应式网格。
▶ 示例:Sass 暗色模式 + CSS 变量
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dark Mode + Sass</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
:root {
--brand-primary: #6f42c1;
--brand-accent: #e83e8c;
}
[data-bs-theme="dark"] {
--brand-primary: #bb86fc;
--brand-accent: #ff4081;
--custom-bg: #1a1a2e;
--custom-card-bg: #16213e;
}
.custom-section {
background: var(--custom-bg, #f8f9fa);
padding: 2rem;
border-radius: 0.5rem;
}
.custom-card-css {
background: var(--custom-card-bg, #ffffff);
border: 1px solid var(--bs-border-color, #dee2e6);
border-radius: 0.5rem;
padding: 1.5rem;
}
.brand-icon { color: var(--brand-primary); font-size: 2rem; }
</style>
</head>
<body>
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h5>Sass + CSS Variables</h5>
<button class="btn btn-outline-primary btn-sm" onclick="document.documentElement.setAttribute('data-bs-theme', document.documentElement.getAttribute('data-bs-theme') === 'dark' ? 'light' : 'dark')">
<i class="bi bi-arrow-repeat"></i> Toggle Theme
</button>
</div>
<div class="custom-section">
<h5>Custom Section</h5>
<p>Background uses <code>--custom-bg</code> CSS variable, changes with theme.</p>
</div>
<div class="row g-3 mt-3">
<div class="col-md-4">
<div class="custom-card-css">
<i class="bi bi-palette brand-icon d-block mb-2"></i>
<h6>Brand Color</h6>
<p class="text-body-secondary small">Primary: <code>--brand-primary</code> changes per theme.</p>
</div>
</div>
<div class="col-md-4">
<div class="card p-4">
<h5>Bootstrap Card</h5>
<p class="text-body-secondary">Native dark mode support.</p>
<button class="btn btn-primary">Button</button>
</div>
</div>
<div class="col-md-4">
<div class="custom-card-css">
<i class="bi bi-heart brand-icon d-block mb-2"></i>
<h6>Accent Color</h6>
<p class="text-body-secondary small">Accent: <code>--brand-accent</code> for highlights.</p>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
输出: Bootstrap 5.3 样式生效的组件效果(如按钮、卡片、轮播、折叠等),页面默认采用 Bootstrap 默认主题(亮色)和响应式网格。
▶ 示例:暗色模式图片切换
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dark Mode Image Switch</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
</head>
<body>
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h5>Image Switch for Dark Mode</h5>
<button class="btn btn-outline-primary btn-sm rounded-pill" onclick="toggleTheme()">
<i class="bi bi-moon"></i> Toggle
</button>
</div>
<div class="row g-4">
<div class="col-md-6">
<div class="card p-4">
<h5>方法1:自定义CSS主题可见性</h5>
<p class="text-body-secondary">Shows different image per theme.</p>
<div class="text-center p-4 bg-light rounded-3">
<i class="bi bi-sun-fill fs-1 text-warning dark-hide"></i>
<i class="bi bi-moon-fill fs-1 text-primary dark-show"></i>
<p class="mt-2 mb-0"><span class="dark-hide">☀️ Light Mode Image</span><span class="dark-show">🌙 Dark Mode Image</span></p>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card p-4">
<h5>Method 2: CSS Filter</h5>
<p class="text-body-secondary">Invert colors for dark mode.</p>
<div class="text-center p-4 bg-light rounded-3">
<img src="https://via.placeholder.com/200x100/0d6efd/fff?text=Logo" class="img-fluid dark-invert" alt="Logo" style="max-width:200px;">
</div>
<style>
[data-bs-theme="dark"] .dark-invert {
filter: invert(0.85) hue-rotate(180deg);
}
</style>
</div>
</div>
</div>
<div class="card p-4 mt-3">
<h5>Method 3: picture element</h5>
<div class="text-center">
<picture>
<source srcset="https://via.placeholder.com/400x100/1a1a2e/bb86fc?text=Dark+Logo" media="(prefers-color-scheme: dark)" id="darkSource">
<img src="https://via.placeholder.com/400x100/0d6efd/fff?text=Light+Logo" class="img-fluid rounded-3" alt="Responsive Logo" style="max-width:400px;">
</picture>
</div>
</div>
</div>
<script>
function toggleTheme() {
const html = document.documentElement;
const next = html.getAttribute('data-bs-theme') === 'dark' ? 'light' : 'dark';
html.setAttribute('data-bs-theme', next);
}
</script>
</body>
</html>
输出: Bootstrap 5.3 样式生效的组件效果(如按钮、卡片、轮播、折叠等),页面默认采用 Bootstrap 默认主题(亮色)和响应式网格。
▶ 示例:局部暗色模式页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Partial Dark Mode</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container py-4">
<h5>区域暗色模式演示</h5>
<p class="text-body-secondary">同一个页面中不同区域使用不同主题。</p>
<!-- Light card -->
<div class="row g-4">
<div class="col-md-6">
<div data-bs-theme="light">
<div class="card p-4 border-0 shadow-sm">
<h5>☀️ Light Section</h5>
<p>This section stays light regardless of global theme.</p>
<div class="d-flex gap-2">
<button class="btn btn-primary">Primary</button>
<button class="btn btn-outline-secondary">Secondary</button>
</div>
<div class="alert alert-info mt-3">Light mode alert</div>
<table class="table table-striped mt-3">
<thead><tr><th>Item</th><th>Value</th></tr></thead>
<tbody><tr><td>Alpha</td><td>100</td></tr><tr><td>Beta</td><td>200</td></tr></tbody>
</table>
</div>
</div>
</div>
<!-- Dark card -->
<div class="col-md-6">
<div data-bs-theme="dark">
<div class="card p-4 border-0 shadow-sm">
<h5>🌙 Dark Section</h5>
<p>This section stays dark — useful for dashboards.</p>
<div class="d-flex gap-2">
<button class="btn btn-primary">Primary</button>
<button class="btn btn-outline-secondary">Secondary</button>
</div>
<div class="alert alert-info mt-3">Dark mode alert</div>
<table class="table table-striped mt-3">
<thead><tr><th>Item</th><th>Value</th></tr></thead>
<tbody><tr><td>Alpha</td><td>100</td></tr><tr><td>Beta</td><td>200</td></tr></tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Mixed card: components with individual themes -->
<h5 class="mt-5">组件级主题</h5>
<div class="d-flex gap-3 flex-wrap">
<div class="card p-3" data-bs-theme="light" style="width:200px;">
<h6>Light Card</h6>
<button class="btn btn-primary btn-sm">Button</button>
</div>
<div class="card p-3" data-bs-theme="dark" style="width:200px;">
<h6>Dark Card</h6>
<button class="btn btn-primary btn-sm">Button</button>
</div>
<div class="card p-3" style="width:200px;">
<h6>📕 Follows Global</h6>
<button class="btn btn-primary btn-sm">Button</button>
</div>
</div>
</div>
</body>
</html>
输出: Bootstrap 5.3 样式生效的组件效果(如按钮、卡片、轮播、折叠等),页面默认采用 Bootstrap 默认主题(亮色)和响应式网格。
(1) 暗色模式系统架构
graph TD
subgraph "Theme Sources"
A1[用户点击切换按钮]
A2[系统偏好 matchMedia]
A3[localStorage 存储]
end
subgraph "Theme Application"
B[data-bs-theme attribute]
C[CSS 变量切换]
D[组件样式重绘]
end
subgraph "Theme Detection"
E[window.matchMedia<br/>prefers-color-scheme]
F[getAttribute<br/>data-bs-theme]
G[localStorage.getItem]
end
A1 --> B
A2 --> B
A3 --> B
B --> C
C --> D
E --> A2
F --> A1
G --> A3
输出: Bootstrap 5.3 样式生效的组件效果(如按钮、卡片、轮播、折叠等),页面默认采用 Bootstrap 默认主题(亮色)和响应式网格。
(2) 暗色模式 CSS 变量表
| 变量名 | Light 默认值 | Dark 默认值 | 作用 |
|---|---|---|---|
--bs-body-bg |
#fff |
#212529 |
页面背景色 |
--bs-body-color |
#212529 |
#dee2e6 |
正文字体颜色 |
--bs-card-bg |
#fff |
#2b3035 |
卡片背景色 |
--bs-border-color |
#dee2e6 |
#495057 |
边框颜色 |
--bs-primary |
#0d6efd |
#6ea8fe |
主题主色 |
--bs-secondary |
#6c757d |
#adb5bd |
次要颜色 |
--bs-navbar-color |
rgba(0,0,0,0.55) |
rgba(255,255,255,0.55) |
导航栏文字 |
(3) 主题检测方法对比表
| 方法 | 实现方式 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|---|
| 用户手动切换 | JS + localStorage |
用户控制、偏好持久化 | 需要额外 UI 元素 | 所有应用 |
| 系统自动跟随 | matchMedia('prefers-color-scheme') |
无感适配、现代浏览器支持 | 无法离线切换 | 新闻站、博客 |
| 时间自动切换 | JS 判断当前时间 | 夜间自动暗色 | 可能不符合用户喜好 | 阅读类应用 |
| URL 参数 + Cookie | 服务端读取参数 | 首屏无闪烁 | 实现复杂 | SEO 重要页面 |
(4) CSS color-scheme 属性表
| 属性值 | 效果 | 说明 |
|---|---|---|
color-scheme: light |
浏览器使用亮色滚动条和表单控件 | 默认值 |
color-scheme: dark |
浏览器使用暗色滚动条和表单控件 | 配合暗色模式使用 |
color-scheme: light dark |
浏览器根据系统偏好自动选择 | 推荐全局设置 |
color-scheme: normal |
不设定任何方案 | 浏览器自行决定 |
<meta name="color-scheme" content="light dark"> |
在 HTML head 中声明 | 快速全局配置,无需 CSS |
❓ 常见问题
[data-bs-theme="dark"] 选择器中覆盖你的自定义变量,或使用 var(--bs-body-bg) 引用 Bootstrap 的主题变量。data-bs-theme="dark" 可以作用于任何 HTML 元素,只影响该元素内部的内容。测试发现区域暗色 + 全局亮色可以混合使用。localStorage 保存偏好是同步操作。唯一注意:用 matchMedia 监听系统主题变化时,回调函数应避免复杂计算,保持轻量。[data-bs-theme="dark"] 中覆盖其 CSS 变量;(2) 使用其自带主题 API(如 Flatpickr 的 theme: "dark");(3) 以 Bootstrap 的暗色 CSS 变量作为参考手动编写暗色覆盖样式。📖 小节
data-bs-theme="dark"启用暗色模式,作用于html、容器或组件- 主题切换:JS 修改
data-bs-theme属性 +localStorage保存偏好 - 系统偏好:
matchMedia('prefers-color-scheme: dark')自动跟随 - CSS 变量覆盖:
[data-bs-theme="dark"] { --bs-body-bg: ... } - 图片:自定义CSS配合
[data-bs-theme]选择器控制不同主题下显示
📝 作业
- ⭐ 给第 19 课的企业站添加暗色模式切换按钮,用户点击后切换全局主题,偏好存储在
localStorage。 - ⭐⭐ 自定义一套品牌暗色变量:背景
#0d1117(GitHub 风格)、卡片背景#161b22、主色#58a6ff。 - ⭐⭐⭐ 在页面中同时展示亮色和暗色两个卡片(
data-bs-theme分别设置),对比字体颜色、背景、边框和按钮的差异。