Axton
Always dream. Always explore.
无垠
MDx 中增强页面可访问性的细节

这篇文章本来是打算发在 MDx Blog 的,不过写到后来发现这篇文章相对来说还是有价值的,于是就将这篇文章转移到了无垠,MDx Blog 则不再发表这篇文章。

从 MDx 开发之初,增强页面的可访问性就被列为了一个重要目标。从 HTML 语义化,到 ARIA 地标,再到 prefers-reduced-motion,随着浏览器对于增强页面可访问性的支持功能越来越多,MDx 也在不断地更新,让更多的人都能毫无困难地浏览、查看由 MDx 渲染的网页。本文会详细说明一些 MDx 中增强页面可访问性的细节,如果你希望增强自己开发的网页的可访问性,本文可能会对你有所帮助。

基础性增强

增强可访问性,最重要也是最基础的一点就是 HTML 语义化。通过使用 HTML 5 语义化标签和 ARIA Role 标签,我们可以很方便地将整个网页在语义上分为几部分,而这将有效地改善屏幕阅读器对网页的分析质量,使视力障碍用户更好地浏览网页。

HTML 语义化

HTML 语义化属于增强页面可访问性的基础中的基础。通过使用语义化的 HTML 标签,我们可以方便地为网页中的各部分标记功能而不影响页面样式。随着符合 HTML 5 的现代化网页成为主流,语义化标签已被很大一部分网站所支持,我也不过多赘述了。就举几个常用的“地标性”标签吧:

  • header 标签标记页眉,默认样式同 div,不要把它和 head 搞混了
  • main 标签标记页面的主要内容,默认样式同 div
  • article 标签标记文章(在 HTML 5 中它指任何独立或可复用的文本),默认样式同 div
  • footer 标签标记页脚,默认样式同 div
  • nav 标签标记页面导航栏区域,默认样式同 div
  • aside 标签标记侧栏,默认样式同 div
  • section 标签标记“一组内容”,你可以把它看作是划定网页中一块区域的通用标签,默认样式同 div

只要用这些标签替代 div 而不是无脑使用 div 就能提高页面的可访问性。MDx 使用了这些标签来增强页面的可访问性。

下一步,ARIA

有的时候,出于一些原因,我们没法使用语义化的 HTML 标签,或者没有合适的语义化标签来标记内容,这时候我们就需要使用 ARIA (Accessible Rich Internet Applications,可访问的富互联网应用)。ARIA 的基础用法很简单,只要在相应元素上加上 role 属性指定该元素的功能即可。下面是一个简单的例子:

<div role="navigation">
  <ul>
    <li><a href="#">Page A</a></li>
    <li><a href="#">Page B</li>
    <li><a href="#">Page C</a></li>
  </ul>
</div>

通过 role="navigation",我们把一个 div 标记为了导航栏区域。实际上,对屏幕阅读器来说,这和 nav 标签在语义上是一致的。上一节中的例子都可以分别用以下 ARIA Role 表示:

  • role="heading"
  • role="main"
  • role="article"
  • role="contentinfo"
  • role="navigation"
  • role="complementary"
  • role="region"

除此以外,MDx 还使用了 role="banner"role="search" 来分别标记页面中的顶部栏和搜索表单。另外,有的时候 role 属性还需配合 aria-* 属性一同使用,具体规则比较复杂,我就不在这里赘述了,具体可参考 WAI-ARIA Roles – MDN

媒体查询来帮忙

CSS 的媒体查询其实是一个非常强大的特性,妥善利用其中的一些特性,我们可以在网页外观上大大增强网页的可访问性。除了已经烂大街的“对浏览器窗口大小”响应的媒体查询,我会介绍 3 个很少有网站充分利用的媒体查询。

print

print 媒体查询其实已经“历史悠久”了,只要使用 @media print {...} 就可以创建只在打印页面时生效的样式。通过这种方式,我们可以让页面在打印时应用一套为打印优化的样式,增强页面在物理纸张上的可访问性。

维基百科是对打印样式做了单独适配的著名例子。

由于在打印的情况下,页面已经离开了“可交互”的范围,还有分页这种在屏幕中无需考虑的问题,要让页面在物理纸张上仍能被轻松地阅读,我们需要做一些特殊的适配。直接来看一个例子吧。以下是 MDx 针对打印情况进行的一些特殊优化,样式的具体理由已经写在了注释中。

@media print {
    /*隐藏不必要的元素*/
    header, .mdx-ad-after-article, .mdx-ad-in-article, .mdx-post-money, .page-footer-nav, .mdx-same-posts, .spanout button, .mdui-drawer, .mdui-overlay, .mdui-menu, .mdx-share-img-dialog, .mdx-share-wechat-dialog {
        display: none!important;
    }

    /*使用下划线标记链接*/
    article a {
        text-decoration: underline!important;
    }
    /*由于纸张不可交互,在链接后显示链接到的 URL*/
    article a::after {
        content: "(" attr(href) ")";
    }

    /*强制白底黑字*/
    html > body {
        background-color: white!important;
    }
    article p {
        color: rgba(0,0,0,.8)!important;
    }

    /*对于纸张上的长文章,衬线字体拥有更好的可读性。以下字体列表来自 fonts.css*/
    article {
        font: 13pt Georgia, "Nimbus Roman No9 L", "Songti SC", "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif CN", STSong, "AR PL New Sung", "AR PL SungtiL GB", NSimSun, SimSun, "TW\-Sung", "WenQuanYi Bitmap Song", "AR PL UMing CN", "AR PL UMing HK", "AR PL UMing TW", "AR PL UMing TW MBE", PMingLiU, MingLiU, serif!important;
    }

    /*除部分元素外,禁止在元素内部分页*/
    * {
        page-break-inside: avoid;
    }
    article p, article a, #comments, article {
        page-break-inside: auto;
    }
}

MDx 自 1.9.3 版本起已经包含了对文章页的打印样式优化。可以很容易地看出,优化后的样式在物理纸张上将更容易阅读。

https://img.flyhigher.top/wp-content/uploads/2020/01/6.jpg?imageView2/1/w/2690/h/3204/interlace/1/q/90#

prefers-color-scheme

prefers-color-scheme 是最近才被浏览器广泛支持的一个媒体查询,但它的功能却很强大:响应系统级黑暗模式。这样,我们可以在开启黑暗模式的系统中让网页具有更好的可读性。不过由于这个媒体查询直到最近才被广泛支持,因此大部分网站对此并没有进行适配。

一些适配这一特性的网站:少数派、微信公众平台(文章页)等。

通过像这样的媒体查询,我们可以在系统启用黑暗模式时让页面实时响应:

@media (prefers-color-scheme: dark) {
    body {
        background-color: #212121;
        color: rgba(255, 255, 255, .7);
    }
}

prefers-color-scheme 一共有 3 个可能值:no-preference, lightdark。你也可以不像上面的例子那样默认使用亮色样式并在黑暗模式下启用黑暗样式,而可以反过来将黑暗样式作为默认,用亮色样式覆盖。在不支持的浏览器上将会显示默认样式(即媒体查询不会生效)。

MDx 已经在 1.9.6 版本中包含对这一媒体查询的支持。

prefers-reduced-motion

看名字就猜得出,prefers-reduced-motion 是和 prefers-color-scheme 师出同门的媒体查询。因此,和 prefers-color-scheme 一样,它也是在最近才获得了浏览器的广泛支持。它的作用也很强大:响应系统的“减弱动态效果”模式。这对某些不喜欢甚至无法接受动画的用户可能很有帮助。要知道,对有些用户来说这甚至是医疗上的必要。使用如下的媒体查询,我们可以在系统启用减弱动态效果模式时让页面实时响应,减弱动画:

@media (prefers-reduced-motion: reduce) {
    /*只是一个示例,实际情况中可以保留一些淡入淡出效果*/
    * {
        transition: all 0s!important;
        transform: none!important;
    }
}

prefers-reduced-motion 只有 2 个可能值:no-preferencereduce,在不支持的浏览器上该媒体查询不会生效。

MDx 已经在 1.9.6 版本中包含对这一媒体查询的支持。

https://img.flyhigher.top/wp-content/uploads/2020/01/5.jpg?imageView2/1/w/1584/h/1030/interlace/1/q/90#

黑暗模式适配

黑暗模式可不是简单地切换到黑底白字这么简单。单纯地使用黑底白字会导致页面元素之间对比度过高,文字刺眼,反而降低了页面的可访问性。我在下面总结了几条应用在了 MDx 中的黑暗模式优化规则和 Google 推荐的优化规则:

一是避免黑底白字。黑底白字看起来真的很难受,事实上,深黑和浅灰是黑暗模式最好的配色。比较下方例子里的两段文本,毫无疑问深黑色背景和浅灰色文字搭配的样式的可读性要比黑底白字高很多。(p.s. 有时候这条规则在白底黑字时也适用)

二是更改部分元素的亮度和对比度以增强可读性。你可能已经注意到了,下面优化的例子里标题文字的颜色改变了。很明显,未优化的黑暗模式中标题可读性不如优化的版本。在实际中,我们通常可以通过 CSS 滤镜来实现这一更改。

@media (prefers-color-scheme: dark) {
    h1 {
        /*反相颜色后再将色相旋转 180 度,使亮度反相*/
        filter: invert(1) hue-rotate(180deg);
    }
}

三是避免让图片过亮。未经处理的图片通常会导致过高的对比度,降低可读性。在 MDx 中,我选择降低图片的亮度来避免图片刺眼,而 Google 的建议则是使图片灰度,不过这会影响图片的整体观感。无论是哪一种方案,都可以使用 CSS 滤镜来轻松实现。

@media (prefers-color-scheme: dark) {
    img { 
        /*MDx 方案*/
        filter: brightness(.8);
        /*Google 方案*/
        filter: grayscale(.5);
    }
}

Cats

这里是未优化的黑暗模式预览。接下来,让我们来看点可爱的猫猫吧。

https://img.flyhigher.top/wp-content/uploads/2020/01/demo.jpg

Cats

这里是经过优化的黑暗模式预览。接下来,让我们来看点可爱的猫猫吧。

https://img.flyhigher.top/wp-content/uploads/2020/01/demo.jpg

其他细节

还有一些细节,虽然简单,但不可或缺。我把一部分没有在上文提到的 MDx 中的细节列在了这里。

一是允许页面缩放。在开发响应式页面时,很多网站会在页面头部添加这样一个 meta

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

这样的 meta 设定了页面的宽度,却也固定了页面的缩放比例,用户难以手动放大网页。这样的设置对一部分视力障碍用户非常不友好,以至于苹果决定在自家的浏览器上忽略禁止缩放的 meta。按照 Google 的推荐,如果你在 MDx 设置中启用了“允许页面缩放”,那么 MDx 会输出这样的 meta 标签,允许页面进行五倍以内的缩放:

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5">

尽管很多时候放大页面会导致页面结构被破坏,但这大大增强了页面的可访问性,利大于弊。因此,在 MDx 中,“允许页面缩放”是默认启用的。

二是在跨设备阅读时同步阅读记录。这属于 MDx 的“元老级”功能之一。注意到页面右上角的那个“在其他设备上继续阅读”的按钮了吗?如果你滚动页面后重新点击它,你会发现二维码发生变化了。通过在二维码中记录阅读进度,用户在扫描二维码切换设备阅读时,MDx 在新设备上会自动滚动到和原设备上一致的位置——好吧,目前基于百分比的算法在某些情况下可能不太准,但我已经设计了一个更复杂但更精确的阅读进度记录算法,之后就会更新到 MDx 中。

https://img.flyhigher.top/wp-content/uploads/2020/01/8.jpg?imageView2/1/w/2000/h/1148/interlace/1/q/90#

三是避免 Web 字体阻塞渲染。如果你在页面中使用了 Web 字体,在字体加载完成之前,浏览器可能不会渲染任何文本,这就导致用户可能需要等待一段时间才能开始浏览网页。Google 推荐使用 font-display: swap; 来向浏览器指定网页的 Web 字体渲染策略。这一规则会提供一个比较短的等待周期,如果在这段时间内 Web 字体没有完成加载,那么浏览器会以 fallback 字体渲染文本。任意时刻一旦 Web 字体完成渲染,浏览器就会使用 Web 字体重新渲染文本。这在大多数情况下都能满足可访问性的需求。

四是确保 html 元素具有合适的 lang 属性。这一属性声明了页面的主要语言,浏览器可以据此调整渲染策略,或者触发自动翻译;屏幕阅读器也能更好地阅读网页。

五是确保页面主要内容在丢失/禁用了 Javascript 甚至 CSS 的情况下仍然可用。作为一个以内容为主的网站,MDx 在禁用了 Javascript 的情况下页面主要内容仍然可用。这不只是为了 SEO,更是为了保证页面的可访问性。的确,目前的情况下,如果开启了 Lazyload,MDx 在禁用 Javascript 的情况下图片的确无法显示,但我会在后续版本中尽可能的优化这一问题。

附录:媒体查询开发技巧

有的时候,光有 CSS 响应媒体查询还不够,我们可能需要在 Javascript 中实时响应媒体查询。用如下代码就可以很简单地响应媒体查询:

let mql = window.matchMedia("(prefers-reduced-motion: reduce)");
mql.addListener(handleMotionChange);

//在添加监听后先调用一次,获得当前查询结果
handleMediaChange(mql);

function handleMediaChange(mql){
    if(mql.matches){
        //查询匹配
    }else{
        //查询不匹配
    }
}

在开发上述媒体查询的过程中,我们需要频繁地切换系统的相应模式或者调出打印窗口。实际上,Chrome 提供了更简单的调试方法:

https://img.flyhigher.top/wp-content/uploads/2020/01/2333.jpg?imageView2/1/w/1446/h/1362/interlace/1/q/90#

只要在这里更改模式,相关媒体查询就能实时生效。这不会影响其他标签页,在关闭开发者工具之后也会自动恢复系统设置。

赞赏
本文链接:https://flyhigher.top/develop/1912.html
本文采用 CC BY-NC-SA 3.0 Unported 协议进行许可

发表评论

textsms
account_circle
email

  • 但有些用户启用黑暗模式是为了让OLED屏幕能够减少发光面积来省电,所以很多应用都有两个黑暗模式。。。但这个在浏览器上应该就没法区分开了

    2月前 回复
    • Axton博主

      @Zxilly: 的确,而且浏览器目前也没办法检测屏幕类型,所以只能二选一了…至少 MDx 在后台提供了选择。按照咕果的说法,似乎不必完全关闭像素,深色像素也能节省电量,所以我个人更倾向于深灰色背景。

      2月前 回复
  • FlyingSky

    膜拜大佬,学到了很多。

    2月前 回复
  • 膜拜大佬,我都不会去注意这些,写这些规则太麻烦了(~ ̄▽ ̄)~

    2月前 回复
  • 支持( ̄▽ ̄)”

    2月前 回复

无垠

MDx 中增强页面可访问性的细节
这篇文章本来是打算发在 MDx Blog 的,不过写到后来发现这篇文章相对来说还是有价值的,于是就将这篇文章转移到了无垠,MDx Blog 则不再发表这篇文章。 从 MDx 开发之初,增强页面的可访…
扫描二维码继续阅读
2020-01-26