首页
/ Bulma 0.8.0 颜色系统演进:Light/Dark 双色机制、4 值颜色映射与 Dark Mode 的 Scheme 变量

Bulma 0.8.0 颜色系统演进:Light/Dark 双色机制、4 值颜色映射与 Dark Mode 的 Scheme 变量

2026-09-05 13:11:31作者:魏侃纯Zoe

本文围绕 Bulma 0.8.0 版本发布日志中关于颜色系统的六项核心更新展开:每个主色新增 *-light / *-dark 双色版本、面板全色支持、表单控件加大、$colors 映射升级为 4 值结构,以及为"Dark Mode"铺路的 $scheme-* 变量体系。读完后,你将掌握这些颜色函数与映射机制在 Bulma 源码中的真实实现(如 mergeColorMaps()bulmaFindLightColor()),并能将 0.8.0 的设计思想延续到当前仓库的 CSS 变量主题系统(sass/themes/dark.scss)。

0.8.0 版本更新总览

Bulma 0.8.0 是一次以"更多颜色与更大灵活性"为主题的大版本更新,包含多项 bug 修复与以下新功能(原文记录于 2019-10-15-light-dark-colors.md):

  1. Light/Dark 双色:每个主色(primaryinfosuccesswarningdanger)各增加 *-light*-dark 版本;
  2. 视觉观感改进:调整部分基础色与 box / card 的阴影;
  3. Panel 组件全色支持:面板可以呈现任意主题色;
  4. 更大的表单控件:控件与按钮高度提升到 2.5em
  5. 4 值颜色映射$colors 映射的每个值可接受最多 4 个分量的嵌套映射,由 mergeColorMaps() 统一处理;
  6. 面向 Dark Mode 的 Scheme 变量:6 个新的 $scheme-* 派生变量取代代码库中散落的 $white / $black 引用。

与本文档同步的完整变更明细见 CHANGELOG.md 中的 0.8.0 小节,其中"Initial variables"与"Derived variables"两节逐条列出了该版本新增的派生变量清单(如 $primary-invert: findColorInvert($primary)$primary-light: findLightColor($primary) 等),可作为升级排查时的对照清单。

Light/Dark 双色:两个颜色函数如何工作

0.8.0 起,每个主色都派生出一对 light/dark 版本,二者由两个新增的 Sass 颜色函数计算:

  • findLightColor():找到一个颜色的浅色版本;
  • findDarkColor():找到一个颜色的深色版本。

这两个颜色的用途分工明确:light 色被 button 元素使用(浅色背景 + 深色文字的"幽灵按钮"效果);light 与 dark 色共同被 message 组件使用(浅色标题/正文区 + 深色强调区),生成明显更好看的彩色消息块。

源码实现:从亮度算法到色相保持

当前仓库中这两个函数已演进为带 bulma 前缀的版本,定义在 sass/utilities/functions.scss,其核心逻辑值得拆开看:

// sass/utilities/functions.scss
@function bulmaFindLightColor($color, $l: 96%) {
  @if meta.type-of($color) == "color" {
    $l: 96%;

    @if color.channel($color, "lightness", $space: hsl) > 96% {
      $l: color.channel($color, "lightness", $space: hsl);
    }

    @return color.change($color, $lightness: $l);
  }

  @return $background;
}

@function bulmaFindDarkColor($color, $base-l: 29%) {
  @if meta.type-of($color) == "color" {
    $luminance: bulmaColorLuminance($color);
    $luminance-delta: 0.53 - $luminance;
    $target-l: math.round($base-l + $luminance-delta * 53);

    @return color.change($color, $lightness: max($base-l, $target-l));
  }

  @return $text-strong;
}

从源码结构可以看出三点设计意图:

  1. 浅色版只改亮度、保留色相bulmaFindLightColor() 把 HSL 亮度强制拉到 96%(极浅),但保留原始色相与饱和度,因此 is-info 按钮的浅色背景仍带有该色系"淡淡的蓝味",而非纯灰;
  2. 深色版按相对亮度动态补偿bulmaFindDarkColor() 先通过 bulmaColorLuminance() 计算颜色的感知亮度(加权 0.2126R + 0.7152G + 0.0722B 的 sRGB 相对亮度公式),再以基准亮度 29% 为底、按亮度差做补偿——原本越亮的颜色,压暗后的目标亮度越高,保证深色版文字/边框在不同色系上对比度均衡;
  3. 对非颜色输入有兜底。两个函数在参数不是合法颜色时分别回退到 $background$text-strong,配合 bulmaRgba() 等防御性写法,使得自定义颜色映射传入意外值时不会直接编译失败。

与之配套的 bulmaFindColorInvert() 则用同一套亮度计算决定"反色文字":亮度大于 0.55 时返回 rgba(#000, 0.7),否则返回白色——这是主色上文字颜色的自动选择逻辑。

视觉观感改进:基础调色板与阴影

0.8.0 同时微调了三个基础色的 HSL 取值("Before / After"对比,数据来自原文与 CHANGELOG.md 0.8.0 节的"Initial variables"):

颜色变量 调整前 调整后
$green hsl(141, 71%, 48%) hsl(141, 53%, 53%)
$cyan hsl(204, 86%, 53%) hsl(204, 71%, 53%)
$red hsl(348, 100%, 61%) hsl(348, 86%, 61%)

可以看出调整思路是普遍降低饱和度、略微提升亮度:原取值饱和度过高(71%~100%),在按钮、消息块等大面积使用场景下显得刺眼,降饱和后整体更沉稳。这些值统一定义在初始变量层 sass/utilities/initial-variables.scss(当前仓库中该文件已随 v1 再次调参,例如 $green: hsl(153, 53%, 53%)),遵循"initial 层只放原始取值、derived 层再做语义映射"的分层原则。

除颜色外,boxcard阴影也做了改进,使卡片浮起感更柔和自然。阴影取值由派生变量 $shadow-color(见 sass/utilities/derived-variables.scss)驱动,后续主题化时也随之成为 CSS 变量。

Panel 组件的全色支持

panel 组件在 0.8.0 起支持所有主题色,即 is-primaryis-infois-success 等修饰类均可用于面板。这类修饰类的生成依赖于 $colors 映射的完整性——只要某个颜色名存在于映射中,颜色修饰类循环(@each $name, $value in $colors)就会为全部组件批量产出对应的 is-<name> 规则。这正是"4 值颜色映射"(下一节)在组件层面的直接红利:颜色系统对组件是数据驱动的,新增颜色只需改映射,无需逐个组件修改。

更大的表单控件与回退配置

0.8.0 将表单控件与按钮的高度提升到 2.5em(原为 2.25em),改善了触控与视觉比例。若需要恢复旧尺寸,按原文给出的方式覆盖以下变量即可:

$control-height: 2.25em
$control-padding-vertical: calc(0.375em - #{$control-border-width})
$control-padding-horizontal: calc(0.625em - #{$control-border-width})

$button-padding-vertical: calc(0.375em - #{$button-border-width})
$button-padding-horizontal: 0.75em

注意这些变量内部相互引用 $control-border-width / $button-border-width,因此覆盖时应原样保留 calc(... - #{$...-border-width}) 结构,保证边框加在内侧、总高度不变。这段回退配置同时收录在 CHANGELOG.md 的 0.8.0 "Larger form controls"小节中。

4 值颜色映射:$colors 的 1~4 值嵌套结构与 mergeColorMaps()

0.8.0 起,$colors Sass 映射的每个值都可以是最多 4 个分量的嵌套映射,顺序固定为:

(基础色, 反色, 浅色, 深色)   // 例如 "info" -> ($info, $info-invert, $info-light, $info-dark)

当用户提供 $custom-colors 自定义映射时,每个值可以给出 1、2、3 或 4 个分量;缺省的分量由 Bulma 自动计算。原文给出的完整示例如下:

$custom-colors: (
  "lime": (
    lime,
  ),
  "tomato": (
    tomato,
    white,
  ),
  "orange": (
    $orange,
    $orange-invert,
    $orange-light,
  ),
  "lavender": (
    $lavender,
    $lavender-invert,
    $lavender-light,
    $lavender-dark,
  ),
);

源码实现:mergeColorMaps() 的分量补齐逻辑

上述"缺多少算多少"的行为由 sass/utilities/functions.scss 中的 mergeColorMaps($bulma-colors, $custom-colors) 实现,其核心分支为:

@if meta.type-of($components) == "color" {
  // 只给 1 个值:invert/light/dark 全部自动计算
  $color-base: $components;
  $color-invert: bulmaFindColorInvert($color-base);
  $color-light: bulmaFindLightColor($color-base);
  $color-dark: bulmaFindDarkColor($color-base);
} @else if meta.type-of($components) == "list" {
  $color-base: list.nth($components, 1);

  @if list.length($components) > 3 {
    // 给了 4 个值:原样采用
  } @else if list.length($components) > 2 {
    // 给了 3 个值:dark 用 bulmaFindDarkColor() 补齐
  } @else {
    // 给了 2 个值:light、dark 均自动计算
  }
}

对照源码可以得到一张"用户输入 -> 自动补齐"的完整规则表:

提供的分量 base invert light dark
1 个(单个颜色) 采用 bulmaFindColorInvert() 计算 bulmaFindLightColor() 计算 bulmaFindDarkColor() 计算
2 个(base, invert) 采用 采用 计算 计算
3 个(base, invert, light) 采用 采用 采用 计算
4 个(全部) 采用 采用 采用 采用

此外,函数内部还包含一层输入校验:颜色名必须是字符串、分量必须是合法颜色或长度不小于 1 的列表、且 base 必须是真实颜色值(meta.type-of($color-base) == "color")才会执行 map.merge() 合并——这意味着同名键(如 "info")可以被自定义映射整体覆盖而非报错,这也是"自定义颜色可以覆盖内置颜色"的实现基础。

该映射在派生变量层的挂载点见 sass/utilities/derived-variables.scss:内置 $colors(white / black / light / dark / text / primary / link / info / success / warning / danger)与用户 $custom-colors 在此通过 fn.mergeColorMaps() 合并,$shades 同理合并 $custom-shades

Scheme 变量:面向 Dark Mode 的重构

0.8.0 引入 6 个新的 $scheme 派生变量:

$scheme-main        $scheme-main-bis    $scheme-main-ter
$scheme-invert     $scheme-invert-bis  $scheme-invert-ter

它们的作用是取代代码库中所有 $white$black 的直接引用。其默认值在 sass/utilities/derived-variables.scss 中定义:

// sass/utilities/derived-variables.scss
$scheme-main: iv.$white !default;
$scheme-main-bis: iv.$white-bis !default;
$scheme-main-ter: iv.$white-ter !default;
$scheme-invert: iv.$black !default;
$scheme-invert-bis: iv.$black-bis !default;
$scheme-invert-ter: iv.$black-ter !default;

于是创建"Dark Mode"只需交换取值,无需改动任何组件源码:

$scheme-main: $black
$scheme-invert: $white
// 其余 scheme 变量同理

原文还点出了与之配套的关键决策:0.8.0 起大部分代码库引用的是派生变量$text$background$border 等),而非初始变量($grey$grey-lighter$grey-darker 等)。在 sass/utilities/derived-variables.scss 中可以看到这一分层的落地形态:

$text: iv.$grey-dark !default;
$text-weak: iv.$grey !default;
$text-strong: iv.$grey-darker !default;
$background: iv.$white-ter !default;
$border: iv.$grey-lighter !default;
$border-weak: iv.$grey-lightest !default;

派生变量是"语义层",初始变量是"原子层";组件只依赖语义层,因此更新派生变量会直接影响所有元素和组件——这正是 Dark Mode 能够"一处交换、全局生效"的原因。

后续演进:从 Sass 变量交换到 CSS 变量主题

这套 scheme 思想在当前仓库中进一步演进为基于 CSS 变量的运行时主题系统:官方深色主题 sass/themes/dark.scss 通过 register-vars() mixin 注册 --bulma-scheme-main-l: 11% 等亮度变量,并用 generate-on-scheme-colors() 为每个主色重新生成"on-scheme"色阶。触发方式同时支持 @media (prefers-color-scheme: dark)system-theme() mixin)与 [data-theme=dark] / .theme-dark 选择器(bulma-theme() mixin),完整说明见 docs/documentation/features/dark-mode.md。对于只消费预编译 CSS(css/bulma.css)的使用者,0.8.0 的 scheme 变量交换属于构建期定制;而对于当前版本,则可以直接通过 HTML 属性或 CSS 类在页面内切换主题,两种方式在原理上一脉相承——都是把"背景/文字/边框"收敛到一组可整体翻转的变量上。

版本兼容性说明

原文结尾明确承诺:0.8.0 版本与任何既有 Bulma 配置完全兼容,升级遇到问题建议向仓库提交 issue 反馈。结合 CHANGELOG.md 0.8.0 小节可以看到,该版本的新增项(light/dark 色、scheme 变量、4 值映射)均以"扩展 + 默认值兜底"方式落地——$custom-colors 默认为 nullmergeColorMaps() 对未提供的分量自动补齐,因此既有配置在升级后行为不变。需要注意的适用前提是:本文所述的"控件加大可回退""颜色映射 4 值结构"等结论均针对 0.8.0 版本;当前仓库源码(v1 线)中的函数已统一加 bulma 前缀(bulmaFindLightColor() 等),若你的项目基于当前版本定制颜色函数,应以 sass/utilities/functions.scss 中的实际签名和 docs/documentation/features/dark-mode.md 的 CSS 变量主题方案为准。

登录后查看全文
热门项目推荐
相关项目推荐