应使用 wcag 2.1 的相对亮度与对比度比值(≥4.5)判断文字色,而非 lightness() 或 darken();因 hsl 的 lightness 不反映人眼感知,如纯红与纯绿 lightness 同为 50% 但实际亮度差异大,故须用 relative-luminance 计算对比度。

SCSS里怎么判断一个颜色够不够深,来决定文字用白还是黑
直接结论:不能靠人眼估,也不能只看 lightness() 值——它不反映人眼感知的明暗对比。必须用 WCAG 2.1 定义的相对亮度(relative luminance)和对比度比值(contrast ratio)来算,最低阈值设为 4.5 才算可读。
为什么 lightness() 和 darken()/lighten() 不可靠
lightness() 是 HSL 模型里的“亮度”,但 HSL 的 L 并不等价于人眼对明暗的感知。比如 #ff0000(纯红)和 #00ff00(纯绿)在 HSL 中 lightness() 都是 50%,但人眼觉得绿色更亮,实际相对亮度分别是 0.2126 和 0.7152。
-
darken($color, 20%)只是线性减饱和+降亮度,不保证结果满足对比度要求 - 用
lightness($color) > 50%切黑白字色,会在大量黄、青、浅紫上出错(比如#e6f7fflightness ≈ 95%,但背景太浅,黑字仍可读;而#ffcc00lightness ≈ 60%,黑字反而发灰、对比不足) - 真正该比的是:背景色相对亮度 vs 文字色相对亮度 → 算对比度比值 → 判断是否 ≥ 4.5
手写 contrast-ratio() 和 is-dark-enough-for-white-text() 函数
SCSS 不支持浮点运算精度控制,所以得把 RGB 转相对亮度的公式拆成整数友好形式,并预设常用文字色(#000 和 #fff)做固定对比。以下函数经实测兼容 Dart Sass 1.33+(LibSass 已淘汰,不建议再用):
@function relative-luminance($color) {
$r: red($color) / 255;
$g: green($color) / 255;
$b: blue($color) / 255;
$r: if($r @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;
}</p>
使用时直接:color: if(is-dark-enough-for-white-text($brand-bg), #fff, #000);
- 这个
is-dark-enough-for-white-text()返回true表示背景够暗,能放心用白字 - 注意:它只判断「白字是否可读」,不自动选黑字——黑字默认可用,除非背景是极浅灰(如
#f8f9fa),此时你得额外加一层判断,或统一 fallback 到#333 - 函数在编译期运行,不会增加运行时开销,但每次调用都重算 luminance,避免在循环中高频调用
实际项目里怎么安全落地,又不卡编译
别在每个 class 里都现场算。把颜色决策收口到设计系统层,生成确定的文本色变量:
$primary-bg: #2563eb;
$primary-fg: if(is-dark-enough-for-white-text($primary-bg), #fff, #1e293b);
<p>.card {
background-color: $primary-bg;
color: $primary-fg;
}</p>
- 所有主题色、状态色(success / warning / danger)、中性色(gray-50 ~ gray-900)都提前算好对应的
$*-fg变量 - 禁用
!important强覆盖字体色——那会绕过对比度逻辑,让 a11y 检查失败 - 如果用了 CSS 变量做主题切换(比如
--bg动态变),这套 SCSS 函数就无能为力了,得换 JavaScript 运行时检测(例如用getComputedStyle+chroma.js)
最易被忽略的一点:按钮禁用态(opacity: 0.5)会让原本合格的对比度跌破 4.5。函数无法感知 opacity,必须单独为 :disabled 或 .is-disabled 提供降级色板。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











