Bulma Color Palettes:从 7 个主题色到 21 级色阶的 CSS 变量体系与自动对比度算法
本文基于 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)的调色板。例如当 primary 为 hsl(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-palette(css-variables.scss#L191-L487)则串联起整个调色板生成流程。浅色主题的入口在 light.scss:它对 $colors map 中每个颜色调用 generate-color-palette,再调用 generate-on-scheme-colors 计算 on-scheme 颜色;其中 white、black、light、dark 四个"无调色板"颜色只走简化的 generate-basic-palette(light.scss#L99-L133)。
二、Soft 与 Bold:为深色模式设计的语义色
Bulma 引入 Dark Mode 后带来一个新概念:soft 与 bold 颜色。
- 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-l、bold-l: $dark-l(浅色模式下 soft 亮、bold 暗),而 dark.scss 中反向注册 soft-l: $dark-l、bold-l: $light-l(dark.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-invert、primary-light-invert、primary-dark-invert,以及每个数字色阶(00–100)都各有一个 -invert 伙伴色。
对比度算法(源码级原理)
这些 invert 颜色不是简单取黑/白,而是由 css-variables.scss 中 generate-color-palette 的 STEP 3 计算得出:
- 对每个背景色阶,用
bulmaColorLuminance(functions.scss#L102-L134)计算 WCAG 相对亮度,遍历所有前景色阶候选,计算对比度(L1 + 0.05) / (L2 + 0.05)(参考 W3C G17 公式); - 只保留对比度大于 7(即 WCAG AAA 级别)的组合,浅色背景优先选浅色前景(更柔和),否则持续替换为对比度更高的前景色阶;
- 若某背景找不到对比度 > 7 的前景,则回退:亮背景配
00号(最暗)前景,暗背景配100号(最亮)前景; - 最终把结果写成
--bulma-{color}-{digits}-invert-l: var(--bulma-{color}-{fg}-l)形式(见 css-variables.scss#L351-L373),即"每个色阶的 invert 都指向某个色阶的 lightness 变量",保证变量之间可追溯。
on-scheme 颜色采用类似但更宽松的机制:generate-on-scheme-colors(css-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-75、has-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.scss 中 buildHslaString 函数(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-00 … has-text-primary-100 |
has-background-primary-00 … has-background-primary-100 |
| 色阶反转 | has-text-primary-00-invert … has-text-primary-100-invert |
has-background-primary-00-invert … has-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 个主题色(text、link、info、success、warning、danger)遵循完全相同的命名规则,只需替换前缀。
这些类全部由 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.scss 的 mergeColorMaps 合并进 $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
)
);
mergeColorMaps(functions.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 展示了完整结构:
- 变量宿主(默认
:root)直接输出浅色主题全套变量 + 计算后的setup-theme(链接、阴影、代码色等派生变量,见 themes/setup.scss); @media (prefers-color-scheme: light|dark)下按系统偏好重注册变量;[data-theme="light"]/.theme-light/[data-theme="dark"]/.theme-dark四个显式切换选择器同样会重新注入对应主题的变量(css-variables.scss 中的bulma-thememixin,L489-L494)。
这就是文档中"切到深色模式 soft/bold 自动互换、-on-scheme 颜色随之重算"的实现路径:深色主题通过 dark-theme mixin 重注册 soft-l/bold-l 与全部 *-on-scheme 变量,而颜色类本身(hue/saturation 部分)保持不变。
小结
- Bulma 调色板以 7 个主题色为种子,通过 css-variables.scss 的
generate-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.scss 与 dark.scss 间互换,实现深色模式零改动适配; - helpers/color.scss 把整组变量映射为
has-text-*/has-background-*辅助类,并附带交互态加深与bulma-palette-*局部变量; - 需要新颜色时,通过
$custom-colors/$custom-shades注入即可获得与内置颜色完全一致的完整调色板能力。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00