bootstrap 5.3+ 通过 data-bs-theme="dark" 或 "light" 属性驱动深色/浅色模式切换,需操作 document.documentelement 设置,css 响应 prefers-color-scheme,自定义变量需配合 [data-bs-theme="dark"] 单独定义。

如何用 data-bs-theme 手动切换深色/浅色模式
Bootstrap 5.3+ 原生支持深色模式,核心是给 或 添加 data-bs-theme="dark" 或 data-bs-theme="light"。它不是靠 CSS 类切换,而是靠属性驱动整套变量重计算。
手动切换只需一行 JS:
document.documentElement.setAttribute('data-bs-theme', 'dark')
注意:必须操作 document.documentElement(即 ),改 无效;且该属性值区分大小写,只能是 light 或 dark,不能写成 Dark 或 Dark Mode。
- 首次加载时建议在
标签上直接写死初始值,比如,避免闪屏 - 不要依赖
prefers-color-scheme自动设值后再 JS 覆盖——CSS 变量会先按系统偏好渲染一帧,再被 JS 覆盖,可能造成白/黑屏闪烁 - 切换后无需重初始化组件,所有
btn、card、form-control等都会自动响应
为什么 prefers-color-scheme 检测要放在 CSS 里而不是 JS 里
因为 Bootstrap 的深色模式底层靠 CSS @media 查询 + :root 变量覆盖实现。JS 读取 window.matchMedia('(prefers-color-scheme: dark)') 只能拿到当前快照,无法响应系统主题动态切换(比如用户在设置里改了夜间模式,页面不会自动更新)。
真正可靠的方案是把检测逻辑交给 CSS:
@media (prefers-color-scheme: dark) {
:root {
data-bs-theme: dark;
}
}
但注意:这个写法不合法。正确做法是——Bootstrap 已内置处理,你只需确保 HTML 根元素没硬编码 data-bs-theme,然后在 CSS 中这样写:
@media (prefers-color-scheme: dark) {
:root:not([data-bs-theme="light"]) {
color-scheme: dark;
}
}
实际生效靠的是 Bootstrap 的 SCSS 变量注入机制,所以更稳妥的做法是:保留 (虽然 auto 不是有效值),然后靠 JS 监听 change 事件并同步设置属性。
- 监听代码必须用
matchMedia().addEventListener(),不能只调一次matches - 监听对象是
window.matchMedia('(prefers-color-scheme: dark)'),不是'(prefers-color-scheme)' - 切换时仍要调用
setAttribute('data-bs-theme', ...),否则 Bootstrap 不感知
data-bs-theme 和自定义 CSS 变量的冲突怎么避
如果你在 :root 里写了 --bs-primary: #333 这类变量,又开了深色模式,这些变量不会自动变——Bootstrap 的深色模式只管它自己那套 --bs-* 变量(如 --bs-body-bg、--bs-emphasis-color),不接管你的自定义变量。
所以常见错误是:按钮颜色正常,但你自己写的 .my-card 背景还是白的,看起来割裂。
- 解决方案一:用
[data-bs-theme="dark"] .my-card单独写样式,别依赖变量 - 解决方案二:在
:root[data-bs-theme="dark"]下重定义你的变量,例如::root[data-bs-theme="dark"] { --my-card-bg: #212529; } - 千万别在
:root顶层直接覆盖 Bootstrap 的--bs-*变量,否则深色模式的变量注入会被你写死的值挡住
切换按钮点击没反应?检查这三处
最常卡住的地方不是逻辑,而是 DOM 时机和属性优先级。
- JS 执行太早,
document.documentElement还没 ready,加DOMContentLoaded包一层 - 用了
class="btn btn-dark"但按钮本身在深色模式下文字看不清——Bootstrap 的btn-dark在data-bs-theme="dark"下背景会变浅,文字反而变灰,建议统一用btn-outline-secondary或手动加text-light - 用了第三方库(比如 Chart.js)或自定义组件,它们没监听
data-bs-theme变化,需要你手动触发重绘或调用其主题方法
深色模式不是“开关”,它是属性驱动的响应式变量体系。真正麻烦的从来不是切,而是切完之后,那些没走 Bootstrap 变量路径的代码还按老样子跑。











