首页
/ Bulma Color Palettes:从 7 个主题色到 21 级色阶的 CSS 变量体系与自动对比度算法

Bulma Color Palettes:从 7 个主题色到 21 级色阶的 CSS 变量体系与自动对比度算法

2026-09-06 11:04:29作者:彭桢灵Jeremy

本文基于 Bulma 官方文档 Color Palettes in Bulma 展开,系统讲解 Bulma 调色板(Color Palettes)的完整机制:7 个主题色如何自动派生出一整套 CSS 变量、soft/bold 语义色在深色模式下为何会自动互换、每色 21 级色阶(shade)及其 -invert 对比色是如何按 WCAG 对比度算法计算出来的。读完后你可以直接复用 Bulma 的 --bulma-* 变量编写自己的 CSS,并理解其底层 Sass 实现,从而正确自定义或扩展配色。

一、7 个主题色与调色板变量

Bulma 内置 7 个主题色(primary colors):

颜色名 说明
text 正文文字色
link 链接色
primary 主色
info 信息提示色
success 成功提示色
warning 警告提示色
danger 危险/错误色

这些默认值定义在 derived-variables.scss 中,底层对应 initial-variables.scss 的 HSL 常量:

// sass/utilities/derived-variables.scss
$primary: iv.$turquoise !default;
$info: iv.$cyan !default;
$success: iv.$green !default;
$warning: iv.$yellow !default;
$danger: iv.$red !default;
$link: iv.$blue !default;

Bulma 会为每个主题色自动生成一组 CSS 变量,这组变量构成一个可玩弄同一颜色不同深浅(shade)的调色板。例如当 primaryhsl(171, 100%, 41%)(即默认的 $turquoise)时,会生成以下变量:

变量 含义
--bulma-primary 基础色
--bulma-primary-rgb RGB 三元组,用于自定义 RGBA,如 rgba(var(--bulma-primary-rgb), 0.5)
--bulma-primary-h 色相(hue)
--bulma-primary-s 饱和度(saturation)
--bulma-primary-l 明度(lightness)
--bulma-primary-base 等于 --bulma-primary,仅作引用
--bulma-primary-invert 放在 primary 上视觉效果合适的对比色(前景/背景组合)
--bulma-primary-light 明度 90% 的 primary
--bulma-primary-light-invert -light 版本上表现良好的颜色
--bulma-primary-dark 明度 20%(源码中取 -10-l 对应的色阶)的 primary
--bulma-primary-dark-invert -dark 版本上表现良好的颜色
--bulma-primary-soft 浅色模式下取 light 色、深色模式下取 dark
--bulma-primary-bold 浅色模式下取 dark 色、深色模式下取 light
--bulma-primary-soft-invert bold 版本相同
--bulma-primary-bold-invert soft 版本相同
--bulma-primary-on-scheme scheme-main(默认白色,即页面背景色)上表现良好的颜色

此外还有文档中「21 shades」一节列出的 --bulma-primary-00--bulma-primary-100 全部 21 级色阶变量及各自的 -invert 变量(后文详述)。

源码中的变量注册流程

从源码结构看,上述变量并非手工枚举,而是由 css-variables.scss 中的 register-hsl / register-rgb 等 mixin 统一注册:register-hsl 把一个颜色拆成 -h-s-l 三个变量并取整,register-rgb 拆出 -rgb 三元组;generate-color-palettecss-variables.scss#L191-L487)则串联起整个调色板生成流程。浅色主题的入口在 light.scss:它对 $colors map 中每个颜色调用 generate-color-palette,再调用 generate-on-scheme-colors 计算 on-scheme 颜色;其中 whiteblacklightdark 四个"无调色板"颜色只走简化的 generate-basic-palettelight.scss#L99-L133)。

二、Soft 与 Bold:为深色模式设计的语义色

Bulma 引入 Dark Mode 后带来一个新概念:softbold 颜色。

  • soft 是与背景对比度很低的柔和浅淡色。在浅色模式下它是浅色,最适合做背景
<span class="has-background-primary-soft">The quick brown fox jumps over the lazy dog</span>
  • bold 是与背景形成强烈对比的醒目深色。在浅色模式下它是深色,最适合做文字颜色
<span class="has-text-primary-bold">The quick brown fox jumps over the lazy dog</span>

最佳实践是把两者组合使用:soft 作背景、bold 作前景:

<span class="has-text-primary-bold has-background-primary-soft">
  The quick brown fox jumps over the lazy dog
</span>

深色模式下的自动互换

在 light/dark 之间切换时,soft 和 bold 的取值会互换,这样无需改动任何 CSS 类就能保持设计观感不变。源码层面的依据是:light.scss 注册 soft-l: $light-lbold-l: $dark-l(浅色模式下 soft 亮、bold 暗),而 dark.scss 中反向注册 soft-l: $dark-lbold-l: $light-ldark.scss#L36-L39)。由于 soft/bold 的颜色最终由「当前主题的 soft-l / bold-l 明度」加该色自己的 hue/saturation 合成(见 css-variables.scss#L471-L485 中的 buildHslaString),主题一变,输出自然互换。

三、Invert 颜色:为每种背景自动挑选前景色

-invert 系列变量的用途是与对应的基础色搭配:比如用 primary-light 作背景色时,就用 primary-light-invert 作前景色。同理还有 primary-invertprimary-light-invertprimary-dark-invert,以及每个数字色阶(00100)都各有一个 -invert 伙伴色。

对比度算法(源码级原理)

这些 invert 颜色不是简单取黑/白,而是由 css-variables.scssgenerate-color-palette 的 STEP 3 计算得出:

  1. 对每个背景色阶,用 bulmaColorLuminancefunctions.scss#L102-L134)计算 WCAG 相对亮度,遍历所有前景色阶候选,计算对比度 (L1 + 0.05) / (L2 + 0.05)(参考 W3C G17 公式);
  2. 只保留对比度大于 7(即 WCAG AAA 级别)的组合,浅色背景优先选浅色前景(更柔和),否则持续替换为对比度更高的前景色阶;
  3. 若某背景找不到对比度 > 7 的前景,则回退:亮背景配 00 号(最暗)前景,暗背景配 100 号(最亮)前景;
  4. 最终把结果写成 --bulma-{color}-{digits}-invert-l: var(--bulma-{color}-{fg}-l) 形式(见 css-variables.scss#L351-L373),即"每个色阶的 invert 都指向某个色阶的 lightness 变量",保证变量之间可追溯。

on-scheme 颜色采用类似但更宽松的机制:generate-on-scheme-colorscss-variables.scss#L82-L134)以页面主背景 scheme-main(默认白色)为参照,从基础色出发每次调整 5% 明度,最多 20 步,直到与背景达到对比度 > 5 为止。

四、每个颜色的 21 级色阶

Bulma 会自动为每个颜色生成 21 级色阶:从「大约 0%」开始,以 5% 为步长递增直到 100%。之所以说"大约 0%",是因为最后一位数字由基础色自身明度决定——若基础色明度是 42%,那么 --bulma-primary-00 实际是 2% 而不是 0%。色阶编号为:

00 05 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100

源码中该列表与 helpers/color.scss 顶部的 $digits 列表完全一致(color.scss#L5-L27)。每个色阶都带一个 -invert 伙伴色,例如:

/* 概念示意:light/dark 主题中都会为每个 digits 注册成对变量 */
--bulma-primary-75:         hsla(var(--bulma-primary-h), var(--bulma-primary-s), var(--bulma-primary-75-l), 1);
--bulma-primary-75-invert:  hsla(var(--bulma-primary-h), var(--bulma-primary-s), var(--bulma-primary-75-invert-l), 1);

对应地,has-text-primary-75 / has-background-primary-75has-text-primary-75-invert / has-background-primary-75-invert 等辅助类也全部可用(生成逻辑见 color.scss#L219-L251)。

Lightness CSS 变量:手写 CSS 时单独取明度

如果写自定义 CSS 想直接复用某个色阶,可以通过带 -l 后缀的专用明度变量单独访问其 lightness 值。例如 --bulma-primary-75 的定义形如:

:root {
  --bulma-primary-75: hsla(
    var(--bulma-primary-h),
    var(--bulma-primary-s),
    var(--bulma-primary-75-l),
    1
  );
}

其中 --bulma-primary-75-l 等于一个百分比值(例如默认主色下约为 76%),可以直接用 var(--bulma-primary-75-l) 取值参与自己的 hsl() 计算。这正是 css-variables.scssbuildHslaString 函数(L13-L19)的产物:每个色阶颜色都不写死完整 HSL,而是引用共享的 -h/-s 与自身的 -l 变量。

五、CSS 辅助类(helper classes)

除了直接写 color: var(--bulma-primary),Bulma 还为每个颜色提供成对的 has-text-*(前景)has-background-*(背景) 辅助类。对 primary 来说,可用名称覆盖以下所有取值:

颜色 color background
基础色 has-text-primary has-background-primary
反转色 has-text-primary-invert has-background-primary-invert
浅色 has-text-primary-light has-background-primary-light
浅色反转 has-text-primary-light-invert has-background-primary-light-invert
深色 has-text-primary-dark has-background-primary-dark
深色反转 has-text-primary-dark-invert has-background-primary-dark-invert
页面背景适配色 has-text-primary-on-scheme has-background-primary-on-scheme
21 级色阶 has-text-primary-00has-text-primary-100 has-background-primary-00has-background-primary-100
色阶反转 has-text-primary-00-inverthas-text-primary-100-invert has-background-primary-00-inverthas-background-primary-100-invert
柔和/醒目 has-text-primary-soft / has-text-primary-bold has-background-primary-soft / has-background-primary-bold
柔和/醒目反转 has-text-primary-soft-invert / has-text-primary-bold-invert has-background-primary-soft-invert / has-background-primary-bold-invert

其余 6 个主题色(textlinkinfosuccesswarningdanger)遵循完全相同的命名规则,只需替换前缀。

这些类全部由 helpers/color.scss 通过循环生成,并带有额外细节:

  • 交互态:作用于 <a><button> 或加了 is-hoverable 的元素时,hover/focus-visible 与 active 状态会按主题变量 hover-color-l-delta / active-color-l-delta(浅色主题分别为 -5%、-10%,见 light.scss#L31-L41)自动加深(color.scss#L253-L304);
  • 局部调色板bulma-palette-primary 类会在元素作用域内把该色暴露为 --h--s--l--color 以及 --color-00--color-100 局部变量,方便在自定义组件里直接引用整组色阶(color.scss#L306-L317)。

六、扩展调色板:自定义颜色

上述机制对自定义颜色同样生效。derived-variables.scss 提供 $custom-colors 钩子,通过 functions.scssmergeColorMaps 合并进 $colors

// 在你的自定义 _variables.scss 中
$custom-colors: (
  "brand": (
    hsl(200, 80%, 45%),            // base
    hsl(200, 80%, 95%),           // 可选 invert
    hsl(200, 80%, 92%),           // 可选 light
    hsl(200, 80%, 25%)            // 可选 dark
  )
);

mergeColorMapsfunctions.scss#L8-L84)支持 1~4 个分量的列表:只给 base 时,invert/light/dark 由 bulmaFindColorInvert 等函数自动推算。合并后 brand 会像 7 个内置主题色一样,在浅色/深色主题中获得完整调色板、21 级色阶、has-text-brand-* / has-background-brand-* 辅助类。类似地,$custom-shades 可扩展灰阶 shades($shades map 同样经由 mergeColorMaps 合并)。

七、主题切换入口:变量在哪些选择器下生效

理解调色板变量"何时、在哪一层"被定义,对调试自定义主题很有帮助。themes/_index.scss 展示了完整结构:

  1. 变量宿主(默认 :root)直接输出浅色主题全套变量 + 计算后的 setup-theme(链接、阴影、代码色等派生变量,见 themes/setup.scss);
  2. @media (prefers-color-scheme: light|dark) 下按系统偏好重注册变量;
  3. [data-theme="light"] / .theme-light / [data-theme="dark"] / .theme-dark 四个显式切换选择器同样会重新注入对应主题的变量(css-variables.scss 中的 bulma-theme mixin,L489-L494)。

这就是文档中"切到深色模式 soft/bold 自动互换、-on-scheme 颜色随之重算"的实现路径:深色主题通过 dark-theme mixin 重注册 soft-l/bold-l 与全部 *-on-scheme 变量,而颜色类本身(hue/saturation 部分)保持不变。

小结

  • Bulma 调色板以 7 个主题色为种子,通过 css-variables.scssgenerate-color-palette 自动生成基础变量、-rgb、H/S/L 分量、light/dark/soft/bold/on-scheme 系列以及 21 级数字色阶和全部 -invert 变量;
  • invert 色阶按 WCAG 相对亮度、以对比度 > 7(AAA)为目标自动挑选,on-scheme 以对比度 > 5 为目标逐步逼近,全部逻辑在编译期完成,运行时只是纯 CSS 变量引用;
  • soft/bold 通过主题级 soft-l/bold-l 明度变量在 light.scssdark.scss 间互换,实现深色模式零改动适配;
  • helpers/color.scss 把整组变量映射为 has-text-* / has-background-* 辅助类,并附带交互态加深与 bulma-palette-* 局部变量;
  • 需要新颜色时,通过 $custom-colors / $custom-shades 注入即可获得与内置颜色完全一致的完整调色板能力。
登录后查看全文
热门项目推荐
相关项目推荐