news 2026/9/10 21:39:28

vue-vben-admin 主题定制完全指南:CSS 变量体系、内置主题与品牌色配置

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
vue-vben-admin 主题定制完全指南:CSS 变量体系、内置主题与品牌色配置

vue-vben-admin 主题定制完全指南:CSS 变量体系、内置主题与品牌色配置

【免费下载链接】vue-vben-adminA modern vue admin panel built with Vue3, Shadcn UI, Vite, TypeScript, and Monorepo. It's fast!项目地址: https://gitcode.com/GitHub_Trending/vu/vue-vben-admin

导读

本文基于 vue-vben-admin 官方文档的《主题》章节,系统讲解这套基于 shadcn-vue 中的真实实现代码做进一步验证。

主题系统总览:CSS 变量 + Tailwind 实用类

框架的主题能力建立在 shadcn-vue 与 Tailwind CSS 之上,二者的结合点是「语义化的 CSS 变量」:所有颜色、圆角、字体大小都以--xxx形式的 CSS 变量定义,Tailwind 实用类再通过hsl(var(--xxx))引用这些变量。因此你可以选择直接使用 CSS 变量,也可以使用 Tailwind 实用程序类进行主题设置,二者最终都落到同一组变量上。

颜色遵循一套简单而统一的命名约定:background变量用于组件的背景颜色,foreground变量用于文本颜色。例如:

<div class="bg-background text-foreground" />

这行代码中的bg-backgroundtext-foreground会分别解析为hsl(var(--background))hsl(var(--foreground))

以此类推,大多数组件都按照「xxx+xxx-foreground」成对出现的方式组织变量。例如 Button 组件的backgroundhsl(var(--primary)),其foregroundhsl(var(--primary-foreground))

注意:CSS 变量内的颜色必须使用hsl格式,如0 0% 100%,不需要加hsl()包裹,也不能带逗号分隔。

默认主题完整 CSS 变量列表

了解所有可用的变量,是自定义主题的前提。下面分别给出默认主题(浅色)与黑暗模式下的完整变量清单。

默认主题(浅色)变量

:root { --font-family: -apple-system, blinkmacsystemfont, 'Segoe UI', roboto, 'Helvetica Neue', arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; /* Default background color of <body />...etc */ --background: 0 0% 100%; /* 主体区域背景色 */ --background-deep: 216 20.11% 95.47%; --foreground: 210 6% 21%; /* Background color for <Card /> */ --card: 0 0% 100%; --card-foreground: 222.2 84% 4.9%; /* Background color for popovers such as <DropdownMenu />, <HoverCard />, <Popover /> */ --popover: 0 0% 100%; --popover-foreground: 222.2 84% 4.9%; /* Muted backgrounds such as <TabsList />, <Skeleton /> and <Switch /> */ --muted: 210 40% 96.1%; --muted-foreground: 215.4 16.3% 46.9%; /* 主题颜色 */ --primary: 212 100% 45%; --primary-foreground: 0 0% 98%; /* Used for destructive actions such as <Button variant="destructive"> */ --destructive: 0 78% 68%; --destructive-foreground: 0 0% 98%; /* Used for success actions such as <message> */ --success: 144 57% 58%; --success-foreground: 0 0% 98%; /* Used for warning actions such as <message> */ --warning: 42 84% 61%; --warning-foreground: 0 0% 98%; /* Secondary colors for <Button /> */ --secondary: 240 5% 96%; --secondary-foreground: 240 6% 10%; /* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */ --accent: 240 5% 96%; --accent-hover: 200deg 10% 90%; --accent-foreground: 240 6% 10%; /* Darker color */ --heavy: 192deg 9.43% 89.61%; --heavy-foreground: var(--accent-foreground); /* Default border color */ --border: 240 5.9% 90%; /* Border color for inputs such as <Input />, <Select />, <Textarea /> */ --input: 240deg 5.88% 90%; --input-placeholder: 217 10.6% 65%; --input-background: 0 0% 100%; /* Used for focus ring */ --ring: 222.2 84% 4.9%; /* Border radius for card, input and buttons */ --radius: 0.5rem; /* ============= custom ============= */ /* 遮罩颜色 */ --overlay: 0deg 0% 0% / 30%; /* 基本文字大小 */ --font-size-base: 16px; /* =============component & UI============= */ /* menu */ --sidebar: 0 0% 100%; --sidebar-deep: 216 20.11% 95.47%; --menu: var(--sidebar); /* header */ --header: 0 0% 100%; accent-color: var(--primary); color-scheme: light; }

默认主题黑暗模式变量

.dark, .dark[data-theme='custom'], .dark[data-theme='default'] { /* Default background color of <body />...etc */ --background: 222.34deg 10.43% 12.27%; /* 主体区域背景色 */ --background-deep: 220deg 13.06% 9%; --foreground: 0 0% 95%; /* Background color for <Card /> */ --card: 222.34deg 10.43% 12.27%; /* --card: 222.2 84% 4.9%; */ --card-foreground: 210 40% 98%; /* Background color for popovers such as <DropdownMenu />, <HoverCard />, <Popover /> */ --popover: 222.82deg 8.43% 12.27%; --popover-foreground: 210 40% 98%; /* Muted backgrounds such as <TabsList />, <Skeleton /> and <Switch /> */ --muted: 220deg 6.82% 17.25%; --muted-foreground: 215 20.2% 65.1%; /* 主题颜色 */ /* --primary: 245 82% 67%; */ --primary-foreground: 0 0% 98%; /* Used for destructive actions such as <Button variant="destructive"> */ --destructive: 0 78% 68%; --destructive-foreground: 0 0% 98%; /* Used for success actions such as <message> */ --success: 144 57% 58%; --success-foreground: 0 0% 98%; /* Used for warning actions such as <message> */ --warning: 42 84% 61%; --warning-foreground: 0 0% 98%; /* 颜色次要 */ --secondary: 240 5% 17%; --secondary-foreground: 0 0% 98%; /* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */ --accent: 0deg 0% 100% / 8%; --accent-hover: 0deg 0% 100% / 12%; --accent-foreground: 0 0% 98%; /* Darker color */ --heavy: 0deg 0% 100% / 12%; --heavy-foreground: var(--accent-foreground); /* Default border color */ --border: 240 3.7% 15.9%; /* Border color for inputs such as <Input />, <Select />, <Textarea /> */ --input: 0deg 0% 100% / 10%; --input-placeholder: 218deg 11% 65%; --input-background: 0deg 0% 100% / 5%; /* Used for focus ring */ --ring: 222.2 84% 4.9%; /* 基本圆角大小 */ --radius: 0.5rem; /* ============= Custom ============= */ /* 遮罩颜色 */ --overlay: 0deg 0% 0% / 40%; /* 基本文字大小 */ --font-size-base: 16px; /* =============component & UI============= */ --sidebar: 222.34deg 10.43% 12.27%; --sidebar-deep: 220deg 13.06% 9%; --menu: var(--sidebar); --header: 222.34deg 10.43% 12.27%; color-scheme: dark; }

从上面两段可以看出几个关键设计点:黑暗模式的选择器统一为.dark类(可叠加[data-theme='custom']/[data-theme='default']),通过color-scheme告知浏览器原生控件(滚动条、表单控件)采用对应的明暗配色,并用accent-color: var(--primary)让复选框、单选钮等原生控件跟随主题色。

覆盖默认的 CSS 变量

你只需要在自己的 CSS 文件中覆盖想要修改的变量即可,无需改动框架源码。例如要把 Card 的默认背景色改为深灰色:

默认主题下

:root { /* Background color for <Card /> */ --card: 0 0% 30%; }

黑暗模式下

.dark, .dark[data-theme='custom'], .dark[data-theme='default'] { /* Background color for <Card /> */ --card: 222.34deg 10.43% 12.27%; }

覆盖时注意三点:一是变量必须使用hsl格式(不带hsl()与逗号);二是「默认主题下」写在:root,而「黑暗模式下」需要同时命中.dark相关选择器以保证优先级;三是--card这类带-foreground成对的变量,通常需要成对覆盖才能获得一致的对比度。

更改品牌主色

品牌主色(Primary)是影响观感最直接的变量。框架要求以hsl格式配置,你可以在应用目录下的preferences.ts中通过defineOverridesPreferences自定义主色:

import { defineOverridesPreferences } from '@vben/preferences'; export const overridesPreferences = defineOverridesPreferences({ // overrides theme: { // 错误色 colorDestructive: 'hsl(348 100% 61%)', // 主题色 colorPrimary: 'hsl(212 100% 45%)', // 成功色 colorSuccess: 'hsl(144 57% 58%)', // 警告色 colorWarning: 'hsl(42 84% 61%)', }, });

提示:

  • 颜色必须使用hsl格式(可借助第三方在线工具进行颜色格式转换);
  • 修改后需要清空浏览器缓存方可生效——这一点在源码中也有明确注释,见各应用 preferences.ts 文件头部的「更改配置后请清空缓存,否则可能不生效」。

需要说明的是,文档中给出的colorPrimary等默认值(hsl(212 100% 45%)等)与 默认偏好配置 中theme一节的取值完全一致,说明这份示例就是框架的出厂默认值,你只需替换其中的色值即可完成品牌换肤。

源码视角:品牌色如何生效

品牌色的四个配置项之所以能以「一个色值驱动整站」,是因为 update-css-variables.ts 中的updateMainColorVariables会调用generatorColorVariables,把用户配置的单一色值展开成整套色阶变量,再映射到语义变量上:

const colorMappings = { '--green-500': '--success', '--primary-500': '--primary', '--red-500': '--destructive', '--yellow-500': '--warning', };

也就是说,你配置的colorPrimary会被生成一组--primary-50--primary-950的完整色阶(经由 theme-colors 的getColors展开,并统一转为hsl格式),其中500号色阶作为主色写入--primary。这也是为什么仅仅给出一个主色值,就能让按钮、选中态、焦点环、链接等所有引用--primary的地方一起变色。

内置主题

框架内置了多种主题,你可以在preferences.ts中通过builtinType进行配置:

import { defineOverridesPreferences } from '@vben/preferences'; export const overridesPreferences = defineOverridesPreferences({ // overrides theme: { builtinType: 'default', }, });

内置主题类型列表

框架内置了 16 种主题,且支持自定义主题,理论上可以无限扩展。完整的类型定义如下:

type BuiltinThemeType = | 'custom' | 'deep-blue' | 'deep-green' | 'default' | 'gray' | 'green' | 'neutral' | 'orange' | 'pink' | 'red' | 'rose' | 'sky-blue' | 'slate' | 'stone' | 'violet' | 'yellow' | 'zinc' | (Record<never, never> & string);

这段联合类型末尾的(Record<never, never> & string)是一个巧妙的类型技巧:它让BuiltinThemeType在保留既有字面量提示的同时,也能接收任意字符串,从而支持你自定义主题名(如'my-theme')而不报类型错误。该类型定义可在 app.d.ts 中查看。

内置主题浅色变量

每个内置主题都是通过[data-theme='xxx']选择器覆盖一组变量实现的。浅色模式下,所有主题共享:root中的基础变量(如--background: 0 0% 100%),再各自覆盖--primary相关色阶、--ring--secondary--muted等变量。以几个代表主题为例:

:root { --font-family: -apple-system, blinkmacsystemfont, 'Segoe UI', roboto, 'Helvetica Neue', arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; /* Default background color of <body />...etc */ --background: 0 0% 100%; /* 主体区域背景色 */ --background-deep: 216 20.11% 95.47%; --foreground: 222 84% 5%; /* Background color for <Card /> */ --card: 0 0% 100%; --card-foreground: 222.2 84% 4.9%; /* Background color for popovers such as <DropdownMenu />, <HoverCard />, <Popover /> */ --popover: 0 0% 100%; --popover-foreground: 222.2 84% 4.9%; /* Muted backgrounds such as <TabsList />, <Skeleton /> and <Switch /> */ --muted: 240 4.8% 95.9%; --muted-foreground: 240 3.8% 46.1%; /* 主题颜色 */ --primary: 212 100% 45%; --primary-foreground: 0 0% 98%; /* Used for destructive actions such as <Button variant="destructive"> */ --destructive: 0 78% 68%; --destructive-foreground: 0 0% 98%; /* Used for success actions such as <message> */ --success: 144 57% 58%; --success-foreground: 0 0% 98%; /* Used for warning actions such as <message> */ --warning: 42 84% 61%; --warning-foreground: 0 0% 98%; /* Secondary colors for <Button /> */ --secondary: 240 5% 96%; --secondary-foreground: 240 6% 10%; /* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */ --accent: 240 5% 96%; --accent-hover: 200deg 10% 90%; --accent-foreground: 240 6% 10%; /* Darker color */ --heavy: 192deg 9.43% 89.61%; --heavy-foreground: var(--accent-foreground); /* Default border color */ --border: 240 5.9% 90%; /* Border color for inputs such as <Input />, <Select />, <Textarea /> */ --input: 240deg 5.88% 90%; --input-placeholder: 217 10.6% 65%; --input-background: 0 0% 100%; /* Used for focus ring */ --ring: 222.2 84% 4.9%; /* Border radius for card, input and buttons */ --radius: 0.5rem; /* ============= custom ============= */ /* 遮罩颜色 */ --overlay: 0deg 0% 0% / 30%; /* 基本文字大小 */ --font-size-base: 16px; /* =============component & UI============= */ /* menu */ --sidebar: 0 0% 100%; --sidebar-deep: 0 0% 100%; --menu: var(--sidebar); /* header */ --header: 0 0% 100%; accent-color: var(--primary); color-scheme: light; } [data-theme='violet'] { /* --background: 0 0% 100%; */ --foreground: 224 71.4% 4.1%; --card: 0 0% 100%; --card-foreground: 224 71.4% 4.1%; --popover: 0 0% 100%; --popover-foreground: 224 71.4% 4.1%; --primary-foreground: 210 20% 98%; --secondary: 220 14.3% 95.9%; --secondary-foreground: 220.9 39.3% 11%; --muted: 220 14.3% 95.9%; --muted-foreground: 220 8.9% 46.1%; --accent: 220 14.3% 95.9%; --accent-foreground: 220.9 39.3% 11%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 210 20% 98%; --border: 220 13% 91%; --input: 220 13% 91%; --ring: 262.1 83.3% 57.8%; } [data-theme='pink'] { /* --background: 0 0% 100%; */ --foreground: 240 10% 3.9%; --card: 0 0% 100%; --card-foreground: 240 10% 3.9%; --popover: 0 0% 100%; --popover-foreground: 240 10% 3.9%; --primary-foreground: 355.7 100% 97.3%; --secondary: 240 4.8% 95.9%; --secondary-foreground: 240 5.9% 10%; --muted: 240 4.8% 95.9%; --muted-foreground: 240 3.8% 46.1%; --accent: 240 4.8% 95.9%; --accent-foreground: 240 5.9% 10%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 0 0% 98%; --border: 240 5.9% 90%; --input: 240 5.9% 90%; --ring: 346.8 77.2% 49.8%; } [data-theme='sky-blue'] { /* --background: 0 0% 100%; */ --foreground: 222.2 84% 4.9%; --card: 0 0% 100%; --card-foreground: 222.2 84% 4.9%; --popover: 0 0% 100%; --popover-foreground: 222.2 84% 4.9%; --primary-foreground: 210 40% 98%; --secondary: 210 40% 96.1%; --secondary-foreground: 222.2 47.4% 11.2%; --muted: 210 40% 96.1%; --muted-foreground: 215.4 16.3% 46.9%; --accent: 210 40% 96.1%; --accent-foreground: 222.2 47.4% 11.2%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 210 40% 98%; --border: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%; --ring: 221.2 83.2% 53.3%; } [data-theme='green'] { /* --background: 0 0% 100%; */ --foreground: 240 10% 3.9%; --card: 0 0% 100%; --card-foreground: 240 10% 3.9%; --popover: 0 0% 100%; --popover-foreground: 240 10% 3.9%; --primary-foreground: 355.7 100% 97.3%; --secondary: 240 4.8% 95.9%; --secondary-foreground: 240 5.9% 10%; --muted: 240 4.8% 95.9%; --muted-foreground: 240 3.8% 46.1%; --accent: 240 4.8% 95.9%; --accent-foreground: 240 5.9% 10%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 0 0% 98%; --border: 240 5.9% 90%; --input: 240 5.9% 90%; --ring: 142.1 76.2% 36.3%; } [data-theme='zinc'] { /* --background: 0 0% 100%; */ --foreground: 240 10% 3.9%; --card: 0 0% 100%; --card-foreground: 240 10% 3.9%; --popover: 0 0% 100%; --popover-foreground: 240 10% 3.9%; --primary-foreground: 0 0% 98%; --secondary: 240 4.8% 95.9%; --secondary-foreground: 240 5.9% 10%; --muted: 240 4.8% 95.9%; --muted-foreground: 240 3.8% 46.1%; --accent: 240 4.8% 95.9%; --accent-foreground: 240 5.9% 10%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 0 0% 98%; --border: 240 5.9% 90%; --input: 240 5.9% 90%; --ring: 240 5.9% 10%; }

其余内置主题(rosedeep-bluedeep-greenorangeyellowneutralslategray等)遵循完全相同的模式:均以[data-theme='xxx']覆盖--foreground--secondary--muted--accent--destructive--border--input--ring等成组变量,各主题间的差异主要体现在--ring(焦点环色,实际由主题色派生)与中性色系(zinc/neutral/slate/gray分别对应不同色相的中性灰)上。

内置主题黑暗模式变量

黑暗模式下,选择器变为.dark[data-theme='xxx'][data-theme='xxx'] .dark的复合写法,且每个主题都会额外覆盖--sidebar--sidebar-deep--header等布局区域变量。以violetzinc为例:

.dark, .dark[data-theme='custom'], .dark[data-theme='default'] { /* Default background color of <body />...etc */ --background: 222.34deg 10.43% 12.27%; /* 主体区域背景色 */ --background-deep: 220deg 13.06% 9%; --foreground: 0 0% 95%; /* Background color for <Card /> */ --card: 222.34deg 10.43% 12.27%; --card-foreground: 210 40% 98%; /* Background color for popovers such as <DropdownMenu />, <HoverCard />, <Popover /> */ --popover: 222.82deg 8.43% 12.27%; --popover-foreground: 210 40% 98%; /* Muted backgrounds such as <TabsList />, <Skeleton /> and <Switch /> */ --muted: 240 3.7% 15.9%; --muted-foreground: 240 5% 64.9%; /* 主题颜色 */ --primary-foreground: 0 0% 98%; /* Used for destructive actions such as <Button variant="destructive"> */ --destructive: 0 78% 68%; --destructive-foreground: 0 0% 98%; /* Used for success actions such as <message> */ --success: 144 57% 58%; --success-foreground: 0 0% 98%; /* Used for warning actions such as <message> */ --warning: 42 84% 61%; --warning-foreground: 0 0% 98%; /* 颜色次要 */ --secondary: 240 5% 17%; --secondary-foreground: 0 0% 98%; /* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */ --accent: 216 5% 19%; --accent-hover: 216 5% 24%; --accent-foreground: 0 0% 98%; /* Darker color */ --heavy: 216 5% 24%; --heavy-foreground: var(--accent-foreground); /* Default border color */ --border: 240 3.7% 22%; /* Border color for inputs such as <Input />, <Select />, <Textarea /> */ --input: 0deg 0% 100% / 10%; --input-placeholder: 218deg 11% 65%; --input-background: 0deg 0% 100% / 5%; /* Used for focus ring */ --ring: 222.2 84% 4.9%; /* 基本圆角大小 */ --radius: 0.5rem; /* ============= Custom ============= */ /* 遮罩颜色 */ --overlay: 0deg 0% 0% / 40%; /* 基本文字大小 */ --font-size-base: 16px; /* =============component & UI============= */ --sidebar: 222.34deg 10.43% 12.27%; --sidebar-deep: 220deg 13.06% 9%; --menu: var(--sidebar); /* header */ --header: 222.34deg 10.43% 12.27%; color-scheme: dark; } .dark[data-theme='violet'], [data-theme='violet'] .dark { --background: 224 71.4% 4.1%; --background-deep: var(--background); --foreground: 210 20% 98%; --card: 224 71.4% 4.1%; --card-foreground: 210 20% 98%; --popover: 224 71.4% 4.1%; --popover-foreground: 210 20% 98%; --primary-foreground: 210 20% 98%; --secondary: 215 27.9% 16.9%; --secondary-foreground: 210 20% 98%; --muted: 215 27.9% 16.9%; --muted-foreground: 217.9 10.6% 64.9%; --accent: 215 27.9% 16.9%; --accent-foreground: 210 20% 98%; --destructive: 0 62.8% 30.6%; --destructive-foreground: 210 20% 98%; --border: 215 27.9% 16.9%; --input: 215 27.9% 16.9%; --ring: 263.4 70% 50.4%; --sidebar: 224 71.4% 4.1%; --sidebar-deep: 224 71.4% 4.1%; --header: 224 71.4% 4.1%; } .dark[data-theme='zinc'], [data-theme='zinc'] .dark { --background: 240 10% 3.9%; --background-deep: var(--background); --foreground: 0 0% 98%; --card: 240 10% 3.9%; --card-foreground: 0 0% 98%; --popover: 240 10% 3.9%; --popover-foreground: 0 0% 98%; --primary-foreground: 240 5.9% 10%; --secondary: 240 3.7% 15.9%; --secondary-foreground: 0 0% 98%; --muted: 240 3.7% 15.9%; --muted-foreground: 240 5% 64.9%; --accent: 240 3.7% 15.9%; --accent-foreground: 0 0% 98%; --destructive: 0 62.8% 30.6%; --destructive-foreground: 0 0% 98%; --border: 240 3.7% 15.9%; --input: 240 3.7% 15.9%; --ring: 240 4.9% 83.9%; --sidebar: 240 10% 3.9%; --sidebar-deep: 240 10% 3.9%; --header: 240 4.9% 83.9%; }

注意.dark[data-theme='xxx'][data-theme='xxx'] .dark同时出现的原因:框架同时支持「把dark类挂在<html>上」与「把dark类挂在某个子容器上」两种黑暗模式作用域方式,两种写法保证了主题变量在两种场景下都能正确命中。

内置主题的预设主色(源码证据)

除了 CSS 变量,内置主题还各自带有一个「预设主色」,保存在 constants.ts 的BUILT_IN_THEME_PRESETS中。例如default预设主色为hsl(212 100% 45%)violethsl(245 82% 67%)pinkhsl(347 77% 60%)sky-bluehsl(231 98% 65%)greenhsl(161 90% 43%)等。特别地,zincneutralslategray这类中性色主题在浅色/深色模式下使用了不同的主色(如zinc浅色主色为hsl(240 5.9% 10%),深色主色为hsl(0 0% 98%)),以保证明暗两种模式下文字与主题色的对比度。当选择某个内置主题时,update-css-variables.ts 会按当前明暗模式取对应的预设主色并写回--primary变量——这就是「切换内置主题后整站主色随之变化」的实现机制。

新增自定义主题

想要扩展一个全新的主题,只需两步:

  1. 在应用的src/preferences.ts内新增一个主题配置,把builtinType设为你的主题名:
import { defineOverridesPreferences } from '@vben/preferences'; export const overridesPreferences = defineOverridesPreferences({ // overrides theme: { builtinType: 'my-theme', }, });
  1. 在你的 CSS 文件中,为新增主题补充 CSS 变量(浅色与深色各一套):
/* light */ [data-theme='my-theme'] { --foreground: 224 71.4% 4.1%; --card: 0 0% 100%; --card-foreground: 224 71.4% 4.1%; --popover: 0 0% 100%; --popover-foreground: 224 71.4% 4.1%; --primary-foreground: 210 20% 98%; --secondary: 220 14.3% 95.9%; --secondary-foreground: 220.9 39.3% 11%; --muted: 220 14.3% 95.9%; --muted-foreground: 220 8.9% 46.1%; --accent: 220 14.3% 95.9%; --accent-foreground: 220.9 39.3% 11%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 210 20% 98%; --border: 220 13% 91%; --input: 220 13% 91%; --ring: 262.1 83.3% 57.8%; } /* dark */ .dark[data-theme='my-theme'], [data-theme='my-theme'] .dark { --background: 224 71.4% 4.1%; --background-deep: var(--background); --foreground: 210 20% 98%; --card: 224 71.4% 4.1%; --card-foreground: 210 20% 98%; --popover: 224 71.4% 4.1%; --popover-foreground: 210 20% 98%; --primary-foreground: 210 20% 98%; --secondary: 215 27.9% 16.9%; --secondary-foreground: 210 20% 98%; --muted: 215 27.9% 16.9%; --muted-foreground: 217.9 10.6% 64.9%; --accent: 215 27.9% 16.9%; --accent-foreground: 210 20% 98%; --destructive: 0 62.8% 30.6%; --destructive-foreground: 210 20% 98%; --border: 215 27.9% 16.9%; --input: 215 27.9% 16.9%; --ring: 263.4 70% 50.4%; --sidebar: 224 71.4% 4.1%; --sidebar-deep: 224 71.4% 4.1%; }

新增主题名之所以能够直接通过builtinType传入,正是得益于BuiltinThemeType末尾的(Record<never, never> & string)开放类型;而 update-css-variables.ts 会把builtinType写入<html>data-theme属性,从而触发你自定义的[data-theme='my-theme']选择器生效。

黑暗模式

黑暗模式同样通过 CSS 变量驱动,在preferences.ts中配置theme.mode即可:

import { defineOverridesPreferences } from '@vben/preferences'; export const overridesPreferences = defineOverridesPreferences({ // overrides theme: { mode: 'dark', }, });

mode的取值类型为ThemeModeType = 'auto' | 'dark' | 'light'(见 app.d.ts),即支持「自动跟随系统 / 强制深色 / 强制浅色」三种模式。源码层面,update-css-variables.ts 中的isDarkTheme会在auto模式下通过window.matchMedia('(prefers-color-scheme: dark)')检测系统偏好,然后将dark类添加或移除到<html>根元素上;配合上文.dark选择器下的整套深色变量,完成全局明暗切换。

自定义侧边栏颜色

侧边栏颜色通过--sidebar变量配置(菜单色--menu默认引用var(--sidebar)):

默认主题下

:root { --sidebar: 0 0% 100%; }

黑暗模式下

.dark, .dark[data-theme='custom'], .dark[data-theme='default'] { --sidebar: 222.34deg 10.43% 12.27%; }

自定义顶栏颜色

顶栏(header)颜色通过--header变量配置:

默认主题下

:root { --header: 0 0% 100%; }

黑暗模式下

.dark, .dark[data-theme='custom'], .dark[data-theme='default'] { --header: 222.34deg 10.43% 12.27%; }

从默认变量表可以看到,浅色主题下--sidebar--header--background均为0 0% 100%(纯白),而--sidebar-deep(侧边栏更深一层的背景)为216 20.11% 95.47%,与--background-deep一致——这套「基础白 + 深一阶灰」的层级关系保证了页面不同区域的视觉分区。

色弱模式

色弱模式一般用于特殊场景(如视觉障碍友好的产品场景)。在preferences.ts中配置:

import { defineOverridesPreferences } from '@vben/preferences'; export const overridesPreferences = defineOverridesPreferences({ // overrides app: { colorWeakMode: true, }, });

灰色模式

灰色模式一般用于特殊场景(如哀悼日、系统维护期将整站置灰)。同样在preferences.tsapp一节中配置:

import { defineOverridesPreferences } from '@vben/preferences'; export const overridesPreferences = defineOverridesPreferences({ // overrides app: { colorGrayMode: true, }, });

colorWeakModecolorGrayMode的默认值均为false,其类型定义与默认值可分别在 types.ts 与 config.ts 中确认。二者均属于app分组,与主题分组(theme)是平级关系,配置时注意层级不要写错。

主题配置的运行时更新机制

了解以上所有配置项的运行时行为,有助于排查「改了配置不生效」的问题。核心逻辑集中在 update-css-variables.ts:

  1. 明暗模式:检测到theme.mode变化后,通过root.classList.toggle('dark', dark)控制<html>上的dark类;
  2. 内置主题:检测到theme.builtinType变化后,通过root.dataset.theme = builtinType同步data-theme属性;
  3. 主题色:根据内置主题预设主色或用户自定义的colorPrimary/colorDestructive/colorSuccess/colorWarning,调用updateMainColorVariables把整套色阶转换为 HSL 变量并写入根元素;
  4. 圆角--radius${radius}rem写入,配合radius: '0.5'这样的偏好值;
  5. 字体大小--font-size-base${fontSize}px写入,同时按calc(${fontSize}px * 0.875)联动更新--menu-font-size,保证菜单字号与全局字号等比缩放。

该更新逻辑在偏好设置被初始化或通过偏好设置面板修改时触发,因此你在偏好设置面板中切换主题、明暗模式,本质上也走的是同一套 CSS 变量更新路径——这解释了为何所有主题能力都「收敛」到 CSS 变量这一单一事实来源(Single Source of Truth),也让开发者可以放心地在任意组件中通过hsl(var(--xxx))引用主题能力。

【免费下载链接】vue-vben-adminA modern vue admin panel built with Vue3, Shadcn UI, Vite, TypeScript, and Monorepo. It's fast!项目地址: https://gitcode.com/GitHub_Trending/vu/vue-vben-admin

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 21:39:01

FlatBuffers Python 使用指南:库结构、读写与 NumPy 向量加速

FlatBuffers Python 使用指南&#xff1a;库结构、读写与 NumPy 向量加速 【免费下载链接】flatbuffers FlatBuffers: Memory Efficient Serialization Library 项目地址: https://gitcode.com/GitHub_Trending/fl/flatbuffers 本篇指南基于 FlatBuffers 官方文档中 Pyt…

作者头像 李华
网站建设 2026/9/10 21:38:26

CANN/ge TimeBatch时间批次功能

&#xfeff;# TimeBatch 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、T…

作者头像 李华
网站建设 2026/9/10 21:35:14

CANN/ge C++融合Pass开发指南

C Fusion Pass Development Guide 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 Py…

作者头像 李华