应使用 WCAG 2.1 的 relative-luminance 和 contrast-ratio 判断文字颜色,因 HSL 的 lightness() 不符合人眼感知亮度;正确做法是 contrast-ratio($bg, #fff) >= 4.5,而非 lightness($color) > 50%。

SCSS 里不能“实时生成”无障碍颜色,但能预计算出满足 WCAG AA(≥4.5:1)的文字色选择逻辑——核心是用 relative-luminance 和 contrast-ratio 替代 lightness(),否则在黄、青、浅紫等色相上必翻车。
为什么不能用 lightness() 判断黑白字色
它不是人眼感知亮度:#ffcc00(亮黄)lightness() ≈ 60%,但实际相对亮度高达 ~0.72,黑字发灰;而 #e6f7ff(极浅蓝)lightness() ≈ 95%,却仍能清晰显示黑字。HSL 的 L 值不加权、无 gamma 校正,完全无法替代 WCAG 公式。
- 所有依赖
lightness($color) > 50%的判断都应立即替换 -
darken()/lighten()是线性调整,不保证对比度达标 - 纯 CSS 变量(如
--bg)在 SCSS 编译期不可见,函数无法读取
relative-luminance() 函数必须手动实现
Dart Sass 不支持浮点精度控制,且 pow() 在旧版中行为不一致,需用 sass:math 并显式处理分支。以下为实测兼容 Dart Sass 1.33+ 的写法:
@use "sass:math";
<p>@function relative-luminance($color) {
$r: red($color) / 255;
$g: green($color) / 255;
$b: blue($color) / 255;</p><p>$r: if($r < 0.03928, $r / 12.92, math.pow(($r + 0.055) / 1.055, 2.4));
$g: if($g < 0.03928, $g / 12.92, math.pow(($g + 0.055) / 1.055, 2.4));
$b: if($b < 0.03928, $b / 12.92, math.pow(($b + 0.055) / 1.055, 2.4));</p><p>@return 0.2126 <em> $r + 0.7152 </em> $g + 0.0722 * $b;
}- 别用 LibSass —— 它已淘汰,
pow()行为错误且不支持sass:math - 阈值
0.03928来自 sRGB gamma 切换点,不能四舍五入或简化 - 权重系数
0.2126/0.7152/0.0722是 WCAG 强制要求,不可改
is-dark-enough-for-white-text() 是唯一可靠判断入口
它封装了真实对比度计算,只回答一个问题:“白字在该背景上是否 ≥4.5:1?”返回布尔值,供 @if 直接消费:
立即学习“前端免费学习笔记(深入)”;
@function contrast-ratio($bg, $text) {
$l1: relative-luminance($bg);
$l2: relative-luminance($text);
$l-max: max($l1, $l2);
$l-min: min($l1, $l2);
@return ($l-max + 0.05) / ($l-min + 0.05);
}
<p>@function is-dark-enough-for-white-text($bg) {
@return contrast-ratio($bg, #fff) >= 4.5;
}- 永远只比
#fff和#000—— 其他候选色(如灰阶)需额外验证,SCSS 无法动态求解最优解 - 别传透明色(
rgba())进函数:SCSS 无法解析 alpha 混合后的最终色,结果无意义 - 主题切换时,每个背景色必须单独调用该函数,不能靠“深色模式开关”一刀切
真正容易被忽略的点:这个函数只在编译期生效,它解决的是“设计阶段预设色板的合规性”,而非运行时动态背景(比如 JS 改变 background-color)。后者必须用 JS 获取 computed style 后重新校验——SCSS 没有 runtime 能力。


















