☰
Element Plus 暗黑模式(Dark Mode)完全指南:从 CSS 变量到源码级定制
2026/9/27 23:58:23 网站建设 项目流程

Element Plus 暗黑模式(Dark Mode)完全指南:从 CSS 变量到源码级定制

【免费下载链接】element-plus🎉 A Vue.js 3 UI Library made by Element team项目地址: https://gitcode.com/GitHub_Trending/el/element-plus

Element Plus 自 2.2.0 版本起正式支持暗黑模式(Dark Mode),其核心思路是:将整个主题体系中所有必要的样式变量统一抽取并映射为 CSS 变量,通过给<html>元素切换darkclass 即可一键切换明暗主题。本文以官方文档 docs/en-US/guide/dark-mode.md 为主线,结合仓库内 packages/theme-chalk/src/dark/ 目录下的真实 SCSS 源码与构建脚本,完整讲解启用暗黑模式、CSS / SCSS 两种变量定制方式,以及底层变量的生成原理。读完本文,你将能独立为 Element Plus 项目接入暗黑模式,并实现任意粒度的主题变量定制。

暗黑模式的实现基础:CSS 变量体系

在了解如何启用之前,先理解它为什么"一行代码就能生效"。

Element Plus 的样式系统建立在两层变量之上:

  • SCSS 变量层:所有组件样式在编写时使用 SCSS 变量,统一维护在 packages/theme-chalk/src/common/var.scss;
  • CSS 变量层:通过 SCSS 的编译能力,将 SCSS 变量自动展开为--el-*形式的 CSS 自定义属性(CSS Variables),组件最终读取的是 CSS 变量。

暗黑模式正是利用了这个机制:它不需要为每个组件单独写一套深色样式,而是在html.dark作用域下重新定义同一批 CSS 变量。组件因为始终读取--el-bg-color、--el-text-color、--el-border-color等变量,所以变量的值一变,整个界面就自动切换为深色。

从源码看,这一作用域定义在 packages/theme-chalk/src/dark/css-vars.scss:

html.dark { color-scheme: dark; // hex colors @each $type in (primary, success, warning, danger, error, info) { @include set-css-color-type($colors, $type); } // --el-box-shadow-#{$type} @include set-component-css-var('box-shadow', $box-shadow); // Background --el-bg-color-#{$type} @include set-component-css-var('bg-color', $bg-color); // --el-text-color-#{$type} @include set-component-css-var('text-color', $text-color); // --el-border-color-#{$type} @include set-component-css-var('border-color', $border-color); // Fill --el-fill-color-#{$type} @include set-component-css-var('fill-color', $fill-color); @include set-component-css-var('mask-color', $mask-color); }

其中set-component-css-var等 mixin 定义在 packages/theme-chalk/src/mixins/_var.scss,作用是把 SCSS map 中的每个键值对展开为--el-{name}-{attribute}形式的 CSS 变量,例如把$bg-color中的'page'展开为--el-bg-color-page。

这里还有两个值得注意的细节:

  • color-scheme: dark会同时通知浏览器使用深色 UA 样式,保证原生滚动条、表单控件、<input>等也呈现深色外观,避免"页面变暗但控件刺眼"的问题;
  • 组件级暗黑样式通过 packages/theme-chalk/src/mixins/mixins.scss 中的@mixin dark($block)生成,例如@include dark(button) { ... }会编译为html.dark .el-button { ... },用于覆盖少量无法用全局 CSS 变量表达的组件特殊状态。

如何启用暗黑模式

启用过程只有两个步骤:给<html>加上darkclass,然后引入暗黑模式的 CSS 变量文件。

第一步:添加 dark class

最简单的形式是直接在 HTML 上写死darkclass:

<html class="dark"> <head></head> <body></body> </html>

如果只需要固定的暗黑模式,到这里 class 部分就完成了。

如果你需要提供明暗切换开关,官方文档推荐使用 VueUse 的useDark(useDark核心实现会自动读写document.documentElement的darkclass,并可选地结合prefers-color-scheme媒体查询与 localStorage 持久化)。其使用方式大致为:

import { useDark, useToggle } from '@vueuse/core' const isDark = useDark() const toggleDark = useToggle(isDark)

把toggleDark绑定到任意开关组件上,即可在明暗之间切换,darkclass 会由useDark自动维护。

第二步:引入暗黑 CSS 变量文件

在项目入口文件中,用一行 import 引入 Element Plus 预编译好的暗黑变量文件:

// if you just want to import css import 'element-plus/theme-chalk/dark/css-vars.css'

这行代码引入的正是 packages/theme-chalk/src/dark/css-vars.scss 编译压缩后的产物。从仓库的构建脚本 packages/theme-chalk/buildfile.ts 可以看到,buildDarkCssVars()专门将src/dark/css-vars.scss编译并压缩到dist/dark/css-vars.css,随包发布为element-plus/theme-chalk/dark/css-vars.css。

引入之后,只要<html>上存在darkclass,这些暗黑变量就会覆盖默认的浅色变量,暗黑模式即刻生效;去掉 class 则恢复浅色——整个过程零 JS 逻辑、零组件改动。

定制暗黑模式变量:两种方式

官方文档提供了两条定制路径:CSS 变量覆盖(运行时、无需编译)与SCSS 变量覆盖(编译期、更彻底)。

方式一:通过 CSS 覆盖变量

暗黑模式的所有变量都定义在html.dark作用域下,因此你只需在 Element Plus 样式之后再引入自己的样式文件,用同样的选择器权重覆盖即可。

例如新建文件styles/dark/css-vars.css:

html.dark { /* custom dark bg color */ --el-bg-color: #626aef; }

然后在入口中把它放在 Element Plus 暗黑变量之后引入:

import 'element-plus/theme-chalk/dark/css-vars.css' import './styles/dark/css-vars.css'

得益于 CSS 层叠规则,后引入的同名变量会覆盖先引入的,因此无需!important也能生效。这种方式适合:只想微调某几个颜色、希望改动即时生效(浏览器开发者工具里即可验证)、或需要运行时动态换肤的场景。

关于 CSS 变量更完整的用法(如:root全局覆盖、按组件覆盖--el-tag-bg-color、通过getComputedStyle读取/写入变量等),可参考主题定制文档 docs/en-US/guide/theming.md。

方式二:通过 SCSS 覆盖变量

如果你的项目本身使用 SCSS 编译 Element Plus 样式,则可以在编译期直接覆盖暗黑变量 map。官方推荐的做法是新建styles/element/index.scss,通过@forward ... with (...)传入自定义值:

/*just override what you need*/ @forward 'element-plus/theme-chalk/src/dark/var.scss' with ( $bg-color: ( 'page': #0a0a0a, '': #626aef, 'overlay': #1d1e1f, ) );
import './styles/element/index.scss' // or just want to import scss? // import 'element-plus/theme-chalk/src/dark/css-vars.scss'

这里传入的$bg-color是暗黑模式下背景色体系的 SCSS map,包含三个键(默认值可在 packages/theme-chalk/src/dark/var.scss 中查到):

键默认值含义
'page'#0a0a0a页面级背景色,对应浅色主题下common/var.scss的#f2f3f5
''(空字符串)#141414组件默认背景色(如卡片、输入框等主体背景),对应浅色主题的#ffffff
'overlay'#1d1e1f浮层背景色(弹窗、抽屉、下拉等),对应浅色主题的#ffffff

对比浅色主题的默认值(packages/theme-chalk/src/common/var.scss)可以更直观地理解这套层级关系:浅色是"页面灰、组件白",暗色则是逐层加深的暗灰。

需要说明的是,@forward ... with (...)的覆盖能力不限于$bg-color。同一文件 packages/theme-chalk/src/dark/var.scss 中声明了完整的暗黑变量 map,包括:

  • $border-color:六档边框色(darker/dark/ 默认 /light/lighter/extra-light),基于#f5f8ff的不同透明度并与背景色混合(见该文件mix-overlay-color的使用),避免半透明边框叠在深色背景上出现脏色;
  • $box-shadow:四档阴影(默认 /light/lighter/dark),暗色下阴影更浓重,用于营造纵深;
  • $fill-color:七档填充色(darker~extra-light及blank),同样与背景色做了混合处理;
  • $text-color:五档文字色(primary/regular/secondary/placeholder/disabled),基于#f0f5ff的透明度分级;
  • $mask-color:遮罩层颜色(默认与extra-light两档);
  • 组件级 map:$button(如disabled-text-color)、$card(bg-color引用--el-bg-color-overlay)、$empty(空状态插画的整套填充色)等。

这些变量在 packages/theme-chalk/src/dark/css-vars.scss 中被逐一声明到html.dark作用域下。

SCSS 方式的一个使用前提

选择 SCSS 方式意味着你的样式链路中必须包含 SCSS 编译(例如 Vite 下通过scss.additionalData注入变量文件,或借助unplugin-element-plus的useSource: true按需加载源码样式),而不是直接使用预编译的 CSS。完整的多方案对比与 Vite / Webpack 配置示例见 docs/en-US/guide/theming.md。

原理纵深:暗黑变量是如何生成的

如果你好奇为什么dark/var.scss里能"凭空"出现那么多颜色档位,关键在于两段生成逻辑:

  1. 色阶自动生成:set-color-mix-levelmixin(packages/theme-chalk/src/dark/var.scss)对每种主题色按比例混入背景色,生成light-1~light-9九个浅色档位,再混入白色生成dark-2深色档位。暗色模式下主题色会整体偏亮,正是因为这些色阶是和深色背景混合出来的;
  2. 变量展开:html.dark { ... }块内通过set-css-color-type/set-component-css-var等 mixin(定义于 packages/theme-chalk/src/mixins/_var.scss)把上述 SCSS map 展开为--el-color-primary-light-3、--el-bg-color-page、--el-text-color-regular等一整套 CSS 变量。

因此,无论你使用 CSS 覆盖还是 SCSS 覆盖,最终影响到的都是同一批--el-*变量,只是介入的时机不同:CSS 方式在运行时覆盖,SCSS 方式在编译期重写。

常见问题与最佳实践

  • 浅色模式下引入暗黑变量文件有影响吗?没有。css-vars.css的所有变量都限定在html.dark作用域内,浅色(无darkclass)时整份文件不生效,可以放心始终引入。
  • 切换闪烁问题:如果使用useDark且希望刷新后立即应用上次的主题,注意在页面渲染前(如内联脚本)恢复darkclass,避免先亮后暗的闪烁。
  • 深色背景下的组件细节:个别组件(如 Button 禁用态、Card、Empty 插画)存在无法仅靠全局变量表达的细节,仓库通过@include dark(button) { ... }这类组件级覆盖处理(见 packages/theme-chalk/src/dark/css-vars.scss)。若你自行定制组件变量,优先覆盖--el-*变量,而非直接改写组件样式。
  • 覆盖文件顺序:使用 CSS 方式定制时,务必保证自定义文件在 Element Plus 暗黑变量文件之后引入,否则会被同权重的内置规则覆盖。

小结

Element Plus 的暗黑模式本质上是一套"变量换肤"方案:html.dark作用域下重定义全部--el-*变量,组件零改动自动切换。启用只需两步——加darkclass、引入element-plus/theme-chalk/dark/css-vars.css;定制则有 CSS 覆盖(运行时)与 SCSS 覆盖(编译期)两条路径。理解 packages/theme-chalk/src/dark/var.scss 与 packages/theme-chalk/src/dark/css-vars.scss 的生成逻辑后,你就能像定制浅色主题一样,精准掌控暗黑模式的每一个细节。

【免费下载链接】element-plus🎉 A Vue.js 3 UI Library made by Element team项目地址: https://gitcode.com/GitHub_Trending/el/element-plus

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询