很多前端开发者在实际项目开发中,常常会遇到一个棘手问题:在暗色模式(深色模式)下,页面文字看起来总是发灰发暗,无论怎么调整颜色都感觉不对。这其实不仅仅是颜色取值的问题,而是整个视觉系统的亮度权重在暗色背景下被重新分配了。下面直接分享干货,带您系统性地解决这个难题。

暗色背景下文字发灰,先查LCH亮度值再调色
直接去修改rgb()或者hsl()的数值,往往需要反复试错才能找到合适的颜色,因为人眼对明暗的感知并不是线性的。正确的做法是:利用LCH色空间先定位背景的真实亮度(即L值),然后按照"背景L + 55"这个原则,推算出文字的最低L值。举个例子,背景色设为#2d2d2d,实测下来L值大约等于20,那么文字的L值至少需要达到75——对应到rgb(220, 220, 220)这类去色后的灰白色。这样调出来的颜色,比直接硬写rgb(255, 255, 255)更加耐看舒适,在OLED屏幕上边缘也更干净清晰。
只调hsl()的L值不够,S和H必须同步微调
将深灰文字从hsl(0, 0%, 30%)提亮到hsl(0, 0%, 42%)后,经常会出现"发闷"或者"泛黄"的观感。这是因为单独提升L值,会放大原有饱和度上的缺陷。这里有几个关键点需要注意:
- 深色文字的L值升到40%以上后,如果S值仍然保持0%,视觉重量会显得不够饱满,建议把S值稍微提高到12%–18%,这样文字的辨识度会明显增强
- 如果浅色文字搭配的是偏蓝或偏紫的背景,H值容易接近补色区间(比如背景H=240,文字H=60),就会产生视觉颤动感。此时将文字H值微调±5°,就能有效避开颜色冲突
- 所有调整完成后,务必用DevTools的灰度模式验证一下:如果文字在灰度模式下依然清晰可辨,那就说明亮度差真的达标了
::selection在暗色模式下失效,是因为没显式声明color和background-color
系统切换到暗色模式后,::selection不会继承父元素的color属性,浏览器默认使用黑字叠加深蓝背景,结果就是选中文字完全看不清。必须同时显式声明这两个属性:
button::selection {
color: #e2e8f0; /* 至少L=90,不要用#94a3b8 */
background-color: #4a5568; /* 避免使用rgba(),Safari渲染不稳定 */
}
::-moz-selection {
color: #e2e8f0;
background-color: #4a5568;
}
注意:button和input默认带有user-select: none属性,写了也无效;局部覆盖时作用域要精确,比如pre code::selection才影响代码块内的文字,而pre::selection会连行号一起高亮显示。
背景色太暗不是颜色问题,是饱和度和对比关系崩了
深色按钮看不清,往往不是因为背景不够黑,而是饱和度太高,或者周围视觉分量的权重没有重新评估。具体来说:
- 深色背景的饱和度如果超过15%,尤其是带有红或橙倾向时,会抢走文本的视觉注意力,降到5%–10%会更稳妥
- 暗色模式下,
gap、border-color、box-shadow的明度权重全变了,不能沿用亮色模式的值——比如亮色时border-color: #e2e8f0,暗色下就得换成hsl(210, 8%, 30%),否则边框会"消失"不见 - 必须在
:root加上color-scheme: light dark,否则@media (prefers-color-scheme: dark)只覆盖了表层样式,原生控件(比如滚动条、input光标)仍然会按系统默认的逻辑渲染
真正卡住人的地方,从来不是"怎么调颜色",而是忘了暗色模式下整个视觉系统的权重都重新分配了——文字、边框、阴影,甚至透明度叠加,全都得按新的上下文重新校准才能获得最佳显示效果。
