微信号:QunarTL

介绍:Qunar技术沙龙是去哪儿网工程师小伙伴以及业界小伙伴们的学习交流平台.我们会分享Qunar和业界最前沿的热门技术趋势和话题;为中高端技术同学提供一个自由的技术交流和学习分享平台.

提高网页设计里文本的易读性

2018-10-11 08:00 董文博

来呀!来呀!关注我吧!!

董文博


个人介绍:董文博,2017 年加入去哪儿网技术团队。目前在平台事业部/大前端技术中心,负责小程序工程化;YApi、YDoc 等开源项目设计与开发,负责团队各产品设计工作,在追求用户体验的道路上持续探索。


网页设计中,文本是最常用的元素之一,文本易读性非常重要,我们都希望页面更加清晰易读,而颜色在文本易读性中起到了至关重要的作用。

文本和背景颜色有一个“对比度”,了解并能正确调整这个对比度,将会让你的页面更加清晰易读,进而提升阅读效率和阅读体验。

问题的由来:拯救你惨不忍睹的页面设计

在缺乏专业设计的页面中,我们经常见到下面这样的画面:

这是公司的某内部系统:

公司某内部工具:

有没有觉得上面的文本特别刺眼,难以识别?

1 标准

上面的两个案例,本质上都是文本色与背景色的“对比度”不足导致的,足够的色彩“对比度”可让文字和图片更易于阅读和理解。

“对比度”是指显示屏上两种相邻颜色之间的亮度或发出光线的强度的差异计算值。这个比值的范围在 1 到 21 之间(通常写为 1:1 到 21:1);该值越大,则对比度越高。

实际上,W3C 的 Web 无障碍推进组织制定了 Web 内容无障碍指南(WCAG)(https://www.w3.org/Translations/WCAG20-zh/),这个指南涉及了一些建议,这些建议可使 Web 内容更容易访问。遵循这些原则,能够让内容更易为广大残障人士所接受,包括失明和低视力、失聪和重听、学习障碍、认知障碍、行动不便、言语残疾、光过敏患者和这些病症的复合患者。遵循这些原则也可让普通用户更容易访问 Web 内容。

简单总结一下这个标准:

对比度等级 普通文本 大型文本
AA 4.5:1 3:1
AAA 7:1 4.5:1

对比度等级:

  • AA 级:符合要求的最小对比度

  • AAA 级:增强版的对比度,文字更清晰

字体大小的分界线:

  • 普通文本:小于 18 磅的常规字体或 14 磅的加粗字体

  • 大型文本:于 18 磅的常规字体或 14 磅的加粗字体

那么如何实现符合 WCAG 规范的对比度呢?如何在项目中渐变快捷地进行调整呢?

2 使用工具调整对比度

有很多工具可以调整对比度,这里列举一些我使用过的工具:

2.1 色彩对比度计算器

色彩对比度计算器:https://contrast-ratio.com/

一个在线的,计算任意两种有效 CSS 颜色之间对比度的工具。

两侧分别为文本颜色与背景色,中间一个醒目的圆盘,显示二者的对比度,鼠标移动到这个圆盘上可以查看该对比度符合 AA/AAA 对比度标准。

2.2 Chrome 浏览器控制台

对于开发的同学来说,这种方式也很快捷方便。新版的 Chrome 浏览器增强了 CSS 的调色功能:

打开控制台,选中一个 文本元素 然后在 Styles 中找到 color 属性,点击 颜色值,可以看到一个调色板。


后点击调色板的 Contrast radio 选项,展开对比度计算工具,可以看到上方的调色板出现一条白线,这条白线就是符合 AA 级对比度标准的临界值。调色板中的圆圈就是当前选中的色值,拖动这个色值即可调整文本颜色,这时下方的对比度计算工具进行了实时计算:

 

2.3 使用 JS 进行精准计算

通过构建工具即可在构建时进行对比度的验证,从而保证 UI 中的文本易读性符合标准。

有一些开源的 JS 库可以进行对比度的计算,这里我找到了一个很好用的库: TinyColor(https://github.com/bgrins/TinyColor#readability),通过 readability 函数计算两个颜色的对比度:

 
           
  1. tinycolor.readability("#000", "#000"); // 1

  2. tinycolor.readability("#000", "#111"); // 1.1121078324840545

  3. tinycolor.readability("#000", "#fff"); // 21

通过 isReadable 函数判断是否符合 AA/AAA 级对比度标准:

 
           
  1. tinycolor.isReadable("#000", "#111", {}); // false

  2. tinycolor.isReadable("#ff0088", "#5c1a72",{level:"AA",size:"small"}); //false

  3. tinycolor.isReadable("#ff0088", "#5c1a72",{level:"AA",size:"large"}), //true

通过 JS 进行对比度的计算,举个栗子,一个最常见的场景:手机顶部状态栏中文本非常显眼,背景色多变,但文本色的正确搭配往往被忽视,下图是我们 APP 中某个页面的状态栏:

可以看到状态栏的文本是黑色,而 header 中的文本颜色又是白色,一般来说状态栏的颜色设置为黑色或白色(iOS 中只能设置这两种颜色),这时状态栏的颜色最好根据 JS 来计算,人为设置黑色或白色容易搭配不当或者遗漏配置。

3 一些技巧

3.1 文本使用带有半透明度的黑色或白色

在彩色背景上使用灰色文本会降低对比度:

最早是在 Material Design 的设计规范中看到的这个技巧,使用透明的黑白色文本和彩色背景的时候,文本颜色会混合成相应的深色,例如上图中的深粉色。这样做的好处是背景颜色变化的时候文本颜色会自动混合成对应的深色,不必改变文本的颜色值。

3.2 告别五彩斑斓的文本

页面中色彩不宜过多,谨慎使用彩色文本,应该把彩色留给按钮、链接、开关等组件,这样做的好处是文本层次鲜明,失去控制的过多色彩会让内容缺乏重点。

正文使用黑白色(在大段的正文中使用彩色文本不利于阅读)。

3.3 使用不同半透明度的文本

按照功能的重要性,为文本制定不同半透明度的规范,从而对不同层次信息的对比度加以区分。

使用不同半透明度的文本,用于区分标题、正文、描述文本、提示文本和 icon。这样做可以让读者阅读起来有一定的优先级关系,可以让信息层次鲜明,有助于用户理解关键信息,减少阅读时候的疲劳感。

3.4 多种色彩的背景上使用文本

如果背景的色彩比较复杂(如渐变色、图案等),则可以根据平均值来作为背景色计算对比度。

如果背景色的色彩差异较大,则应在文本和背景之间添加遮罩。

3.5 使用 JS 进行对比度的计算

即上文提到的,使用 TinyColor 工具进行精确计算(https://github.com/bgrins/TinyColor#readability),在构建工具中验证。

3.6 根据实际场景调整对比度

虽然 WCAG 提出了 AA/AAA 级对比度标准,但是实际使用时应从用户角度出发,根据实际场景确定是否需要更强烈的对比度,举几个例子:

  • 设备质量:屏幕质量是否比较差、投影仪是否老化,显示效果不好的时候应提高对比度;

  • 分散注意:用户在跑步、乘车、嘈杂环境这样的不稳定场景中使用,分散在界面上的注意力会大打折扣,这时应提高对比度;

  • 用户视觉能力:近视、散光甚至色盲、色弱的比例;

  • 其他影响:是否有其他光源影响显示。

4 算法探究与优化

4.1 Material Design 根据对比度判断使用黑色/白色的源码

源码地址:https://github.com/material-components/material-components-web/blob/master/packages/mdc-theme/_functions.scss

 
           
  1. //

  2. // Copyright 2017 Google Inc.

  3. //

  4. // Permission is hereby granted, free of charge, to any person obtaining a copy

  5. // of this software and associated documentation files (the "Software"), to deal

  6. // in the Software without restriction, including without limitation the rights

  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell

  8. // copies of the Software, and to permit persons to whom the Software is

  9. // furnished to do so, subject to the following conditions:

  10. //

  11. // The above copyright notice and this permission notice shall be included in

  12. // all copies or substantial portions of the Software.

  13. //

  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR

  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER

  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,

  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN

  20. // THE SOFTWARE.

  21. //

  22. @import "./constants";

  23. // Calculate the luminance for a color.

  24. // See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests

  25. @function mdc-theme-luminance($color) {

  26.  $red: nth($mdc-theme-linear-channel-values, red($color) + 1);

  27.  $green: nth($mdc-theme-linear-channel-values, green($color) + 1);

  28.  $blue: nth($mdc-theme-linear-channel-values, blue($color) + 1);

  29.  @return .2126 * $red + .7152 * $green + .0722 * $blue;

  30. }

  31. // Calculate the contrast ratio between two colors.

  32. // See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests

  33. @function mdc-theme-contrast($back, $front) {

  34.  $backLum: mdc-theme-luminance($back) + .05;

  35.  $foreLum: mdc-theme-luminance($front) + .05;

  36.  @return max($backLum, $foreLum) / min($backLum, $foreLum);

  37. }

  38. // Determine whether the color is "light" or "dark".

  39. @function mdc-theme-tone($color) {

  40.  @if $color == "dark" or $color == "light" {

  41.    @return $color;

  42.  }

  43.  $minimumContrast: 3.1;

  44.  $lightContrast: mdc-theme-contrast($color, white);

  45.  $darkContrast: mdc-theme-contrast($color, rgba(black, .87));

  46.  @if ($lightContrast < $minimumContrast) and ($darkContrast > $lightContrast) {

  47.    @return "light";

  48.  } @else {

  49.    @return "dark";

  50.  }

  51. }

  52. // Determine whether to use dark or light text on top of given color to meet accessibility standards for contrast.

  53. // Returns "dark" if the given color is light and "light" if the given color is dark.

  54. @function mdc-theme-contrast-tone($color) {

  55.  @return if(mdc-theme-tone($color) == "dark", "light", "dark");

  56. }

这段代码用 Sass 实现了若干函数,其中:

1. @function mdc-theme-contrast-tone 传入一个颜色值,返回该颜色背景上应该使用的颜色为 light / dark 颜色;

2. @function mdc-theme-tone 传入一个颜色值,返回该颜色为 light / dark 颜色,核心代码:

 
           
  1. // 传入的颜色值为 $color

  2. $lightContrast: mdc-theme-contrast($color, white); // 计算传入颜色和白色的对比度

  3. $darkContrast: mdc-theme-contrast($color, rgba(black, .87)); // 计算传入颜色和黑色的对比度

  4. $minimumContrast: 3.1; // 设置最小对比度

  5. @if ($lightContrast < $minimumContrast) and ($darkContrast > $lightContrast) {

  6.  // 传入的颜色值和白色的对比度小于最小对比度 并且 和黑色的对比度更高

  7.  @return "light"; // 我们认为传入的颜色是浅色

  8. } @else {

  9.  // 我们认为传入的颜色是深色

  10.  @return "dark";

  11. }

3. 👆 上面的代码中用到了计算对比度的函数 mdc-theme-contrast 也在这段代码中,对比度是这样计算的:

 
           
  1. // 计算颜色的相对亮度(relative luminance)

  2. @function mdc-theme-luminance($color) {

  3.  $red: nth($mdc-theme-linear-channel-values, red($color) + 1);

  4.  $green: nth($mdc-theme-linear-channel-values, green($color) + 1);

  5.  $blue: nth($mdc-theme-linear-channel-values, blue($color) + 1);

  6.  @return .2126 * $red + .7152 * $green + .0722 * $blue;

  7. }

  8. // 计算两个颜色的对比度(contrast ratio)

  9. @function mdc-theme-contrast($back, $front) {

  10.  $backLum: mdc-theme-luminance($back) + .05;

  11.  $foreLum: mdc-theme-luminance($front) + .05;

  12.  @return max($backLum, $foreLum) / min($backLum, $foreLum);

  13. }

这里用到了一个对比度计算公式:https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests

可以看到一些有趣的参数:

 
           
  1. @return .2126 * $red + .7152 * $green + .0722 * $blue;

相同的色值,绿色要显得亮一些,相反蓝色要显得暗一些。

4.2 优化:支持半透明度颜色混合

Material Design 在计算传入颜色和黑色的对比度时,传入的是一个带有半透明度的色值,实际上在执行时会按照纯黑色(不带有半透明度)进行解析。

 
           
  1. $darkContrast: mdc-theme-contrast($color, rgba(black, .87)); // 计算传入颜色和黑色的对比度

我理解的 rgba(black, .87) 指的是 Material Design 规定的正文字体颜色(深色),因此这里按照半透明色进行解析会更好一些,这样我们判断颜色是深色还是浅色时的依据就是页面上最深的文本颜色 (#000 87%) 和最浅的背景颜色 (#fff 100%) 了。

用 JS 来实现这个混合的过程 (依赖 tinycolor):

 
           
  1. // Alpha 合成 支持前景色半透明

  2. function mixColor(front, back) {

  3.  var rgbFront = tinycolor(front).toRgb();

  4.  var rgbBack = tinycolor(back).toRgb();

  5.  var alphaFront = rgbFront.a;

  6.  var mixR = alphaFront * rgbFront.r + (1 - alphaFront) * rgbBack.r;

  7.  var mixG = alphaFront * rgbFront.g + (1 - alphaFront) * rgbBack.g;

  8.  var mixB = alphaFront * rgbFront.b + (1 - alphaFront) * rgbBack.b;

  9.  var res = tinycolor({ r: mixR, g: mixG, b: mixB }).toHexString();

  10.  return res;

  11. }

Alpha 合成算法来自:维基百科 - Alpha 合成(https://zh.wikipedia.org/wiki/Alpha)。

4.3 优化:支持调节最小对比度

Material Design 中最小对比度是写死的 3.1,不知道为什么没有设置为 AA/AAA 级对比度标准,这里可以传入参数来指定最小对比度:

 
           
  1. function contrast(color, contrast) {

  2.  if (color === 'light' || color === 'dark') return color;

  3.  var minimumContrast = contrast || 3.5;

  4.  var lightContrast = getContrast(color, '#fff');

  5.  var darkContrast = getContrast(color, '#000');

  6.  if (lightContrast < minimumContrast && darkContrast > lightContrast) {

  7.    return 'light';

  8.  } else {

  9.    return 'dark';

  10.  }

  11. }

4.4 用 JS 实现

上文提到的 Material Design 的源码包含的几个函数,在 TinyColor 这个工具里都有实现。

我按照 Material Design 的实现思路,用 JS 实现了一遍,增加了上文提到的两个优化点 (支持半透明度颜色混合 / 支持调节最小对比度)。

源码地址:https://github.com/dwb1994/lightordark

参考资料

  • Android accessibility 帮助 - 色彩对比度 (https://support.google.com/accessibility/android/answer/7158390?hl=zh-Hans&from=singlemessage&isappinstalled=0)

  • Human Interface Guidelines - iOS - Accessibility (https://developer.apple.com/design/human-interface-guidelines/ios/app-architecture/accessibility/)

  • Material Design - Text legibility (https://material.io/design/color/text-legibility.html#legibility-standards)

 
Qunar技术沙龙 更多文章 机器学习之 scikit-learn 开发入门(3) 机器学习之 scikit-learn 开发入门(4) 一百行 python 代码告诉你国庆哪些景点爆满 AJAX 跨域请求方案详细介绍 第四届 Hackathon 大赛 CodeCode 小组 JSON
猜您喜欢 关于 Git 你不知道的十件事 大话Android 资源目录命名规则 我的2016碎碎念上篇—工作&amp;读书 谁是王者?百度、阿里和腾讯的大数据发展路线和区别 【原译】webpack 2和babel 6的tree-shaking