前端優秀實踐不完全指南

ChokCoco發表於2021-02-24

本文其實應該叫,Web 使用者體驗設計提升指南。

一個 Web 頁面,一個 APP,想讓別人用的爽,也就是所謂的良好的使用者體驗,我覺得他可能包括但不限於:

  • 急速的開啟速度
  • 眼前一亮的 UI 設計
  • 酷炫的動畫效果
  • 豐富的個性化設定
  • 便捷的操作
  • 貼心的細節
  • 關注殘障人士,良好的可訪問性
  • ...

所謂的使用者體驗設計,其實是一個比較虛的概念,是秉承著以使用者為中心的思想的一種設計手段,以使用者需求為目標而進行的設計。設計過程注重以使用者為中心,使用者體驗的概念從開發的最早期就開始進入整個流程,並貫穿始終。

良好的使用者體驗設計,是產品每一個環節共同努力的結果。

除去一些很難一蹴而就的,本文將就頁面展示互動細節可訪問性三個方面入手,羅列一些在實際的開發過程中,積攢的一些有益的經驗。通過本文,你將能收穫到:

  1. 瞭解到一些小細節是如何影響使用者體驗的
  2. 瞭解到如何在儘量小的開發改動下,提升頁面的使用者體驗
  3. 瞭解到一些優秀的互動設計細節
  4. 瞭解基本的無障礙功能及頁面可訪問性的含義
  5. 瞭解基本的提升頁面可訪問性的方法

頁面展示

就整個頁面的展示,頁面內容的呈現而言,有一些小細節是需要我們注意的。

整體佈局

先來看看一些佈局相關的問題。

對於大部分 PC 端的專案,我們首先需要考慮的肯定是最外層的一層包裹。假設就是 .g-app-wrapper

<div class="g-app-wrapper">
    <!-- 內部內容 -->
</div>

首先,對於 .g-app-wrapper,有幾點,是我們在專案開發前必須弄清楚的:

  1. 專案是全屏佈局還是定寬佈局?
  2. 對於全屏佈局,需要適配的最小的寬度是多少?

對於定寬佈局,就比較方便了,假設定寬為 1200px,那麼:

.g-app-wrapper {
    width: 1200px;
    margin: 0 auto;
}

利用 margin: 0 auto 實現佈局的水平居中。在螢幕寬度大於 1200px 時,兩側留白,當然螢幕寬度小於 1200px 時,則出現滾動條,保證內部內容不亂。

對於現代佈局,更多的是全屏佈局。其實現在也更提倡這種佈局,即使用可隨使用者裝置的尺寸和能力而變化的自適應佈局。

通常而言是左右兩欄,左側定寬,右側自適應剩餘寬度,當然,會有一個最小的寬度。那麼,它的佈局應該是這樣:

<div class="g-app-wrapper">
    <div class="g-sidebar"></div>
    <div class="g-main"></div>
</div>
.g-app-wrapper {
    display: flex;
    min-width: 1200px;
}
.g-sidebar {
    flex-basis: 250px;
    margin-right: 10px;
}
.g-main {
    flex-grow: 1;
}

利用了 flex 佈局下的 flex-grow: 1,讓 .main 進行伸縮,佔滿剩餘空間,利用 min-width 保證了整個容器的最小寬度。

當然,這是最基本的自適應佈局。對於現代佈局,我們應該儘可能的考慮更多的場景。做到:

下面一種情形也是非常常見的一個情景。

頁面存在一個 footer 頁尾部分,如果整個頁面的內容高度小於視窗的高度,則 footer 固定在視窗底部,如果整個頁面的內容高度大於視窗的高度,則 footer 正常流排布(也就是需要滾動到底部才能看到 footer)。

看看效果:

嗯,這個需求如果能夠使用 flex 的話,使用 justify-content: space-between 可以很好的解決,同理使用 margin-top: auto 也非常容易完成:

<div class="g-container">
    <div class="g-real-box">
        ...
    </div>
    <div class="g-footer"></div>
</div>
.g-container {
    height: 100vh;
    display: flex;
    flex-direction: column;
}

.g-footer {
    margin-top: auto;
    flex-shrink: 0;
    height: 30px;
    background: deeppink;
}

Codepen Demo -- sticky footer by flex margin auto

當然,實現它的方法有很多,這裡僅給出一種推薦的解法。

處理動態內容 - 文字超長

對於所有接收後端介面欄位的文字展示類的介面。都需要考慮全面(防禦性程式設計:所有的外部資料都是不可信的),正常情況如下,是沒有問題的。

image

但是我們是否考慮到了文字會超長?超長了會折行還是換行?

image

對於單行文字,使用單行省略:

{
    width: 200px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

image

當然,目前對於多行文字的超長省略,相容性也已經非常好了:

{
    width: 200px;
    overflow : hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

image

處理動態內容 - 保護邊界

對於一些動態內容,我們經常使用 min/max-widthmin/max-height 對容器的高寬限度進行合理的控制。

在使用它們的時候,也有一些細節需要考慮到。

譬如經常會使用 min-width 控制按鈕的最小寬度:

.btn {
    ...
    min-width: 120px;
}

image

當內容比較少的時候是沒問題的,但是當內容比較長,就容易出現問題。使用了 min-width 卻沒考慮到按鈕的過長的情況:

image

這裡就需要配合 padding 一起:

.btn {
    ...
    min-width: 88px;
    padding: 0 16px
}

借用Min and Max Width/Height in CSS中一張非常好的圖,作為釋義:

min-width-2

0 內容展示

這個也是一個常常被忽略的地方。

頁面經常會有列表搜尋,列表展示。那麼,既然存在有資料的正常情況,當然也會存在搜尋不到結果或者列表無內容可展示的情形。

對於這種情況,一定要注意 0 結果頁面的設計,同時也要知道,這也是引導使用者的好地方。對於 0 結果頁面,分清楚:

  • 資料為空:其中又可能包括了使用者無許可權、搜尋無結果、篩選無結果、頁面無資料
  • 異常狀態:其中又可能包括了網路異常、伺服器異常、載入失敗等待

不同的情況可能對應不同的 0 結果頁面,附帶不同的操作引導。

譬如網路異常:

image

或者確實是 0 結果:

image

關於 0 結果頁面設計,可以詳細看看這篇文章:如何設計產品的空白頁面?

小小總結一下,上述比較長的篇幅一直都在闡述一個道理,開發時,不能僅僅關注正常現象,要多考慮各種異常情況,思考全面。做好各種可能情況的處理

圖片相關

圖片在我們的業務中應該是非常的常見了。有一些小細節是需要注意的。

給圖片同時設定高寬

有的時候和產品、設計會商定,只能使用固定尺寸大小的圖片,我們的佈局可能是這樣:

image

對應的佈局:

<ul class="g-container">
    <li>
        <img src="http://placehold.it/150x100">
        <p>圖片描述</p>
    </li>
</ul>
ul li img {
    width: 150px;
}

當然,萬一假設後端介面出現一張非正常大小的圖片,上述不加保護的佈局就會出問題:

image

所以對於圖片,我們總是建議同時寫上高和寬,避免因為圖片尺寸錯誤帶來的佈局問題:

ul li img {
    width: 150px;
    height: 100px;
}

同時,給 <img> 標籤同時寫上高寬,可以在圖片未載入之前提前佔住位置,避免圖片從未載入狀態到渲染完成狀態高寬變化引起的重排問題。

object-fit

當然,限制高寬也會出現問題,譬如圖片被拉伸了,非常的難看:

image

這個時候,我們可以藉助 object-fit,它能夠指定可替換元素的內容(也就是圖片)該如何適應它的父容器的高寬。

ul li img {
    width: 150px;
    height: 100px;
    object-fit: cover;
}

利用 object-fit: cover,使圖片內容在保持其寬高比的同時填充元素的整個內容框。

image

object-fit 還有一個配套屬性 object-position,它可以控制圖片在其內容框中的位置。(類似於 background-position),m預設是 object-position: 50% 50%,如果你不希望圖片居中展示,可以使用它去改變圖片實際展示的 position 。

ul li img {
    width: 150px;
    height: 100px;
    object-fit: cover;
    object-position: 50% 100%;
}

image

像是這樣,object-position: 100% 50% 指明從底部開始展示圖片。這裡有一個很好的 Demo 可以幫助你理解 object-position

CodePen Demo -- Object position

考慮螢幕 dpr -- 響應式圖片

正常情況下,圖片的展示應該沒有什麼問題了。但是對於有圖片可展示的情況下,我們還可以做的更好。

在移動端或者一些高清的 PC 螢幕(蘋果的 MAC Book),螢幕的 dpr 可能大於 1。這種時候,我們可能還需要考慮利用多倍圖去適配不同 dpr 的螢幕。

正好,<img> 標籤是有提供相應的屬性 srcset 讓我們進行操作的。

<img src='photo@1x.png'
   srcset='photo@1x.png 1x,
           photo@2x.png 2x,
           photo@3x.png 3x' 
/>

當然,這是比較舊的寫法,srcset 新增了新的 w 寬度描述符,需要配合 sizes 一起使用,所以更好的寫法是:

<img 
        src = "photo.png" 
        sizes = “(min-width: 600px) 600px, 300px" 
        srcset = “photo@1x.png 300w,
                       photo@2x.png 600w,
                       photo@3x.png 1200w,
>

利用 srcset,我們可以給不同 dpr 的螢幕,提供最適合的圖片。

上述出現了一些概念,dpr,srcset 屬性,不太瞭解的可以移步 前端基礎知識概述

圖片丟失

好了,當圖片連結沒問題時,已經處理好了。接下來還需要考慮,當圖片連結掛了,應該如何處理。

處理的方式有很多種。最好的處理方式,是我最近在張鑫旭老師的這篇文章中 -- 圖片載入失敗後CSS樣式處理最佳實踐 看到的。這裡簡單講下:

  1. 利用圖片載入失敗,觸發 <img> 元素的 onerror 事件,給載入失敗的 <img> 元素新增一個樣式類
  2. 利用新增的樣式類,配合 <img> 元素的偽元素,展示預設兜底圖的同時,還能一起展示 <img> 元素的 alt 資訊
<img src="test.png" alt="圖片描述" onerror="this.classList.add('error');">
img.error {
    position: relative;
    display: inline-block;
}

img.error::before {
    content: "";
    /** 定位程式碼 **/
    background: url(error-default.png);
}

img.error::after {
    content: attr(alt);
    /** 定位程式碼 **/
}

我們利用偽元素 before ,載入預設錯誤兜底圖,利用偽元素 after,展示圖片的 alt 資訊:

image

OK,到此,完整的對圖片的處理就算完成了,完整的 Demo 你可以戳這裡看看:

CodePen Demo -- 圖片處理

互動設計優化

接下來一個大環節是關於一些互動的細節。對於互動設計,一些比較通用的準則:

  • Don’t make me think
  • 符合使用者的習慣與預期
  • 操作便利
  • 做適當的提醒
  • 不強迫使用者

過渡與動畫

在我們的互動過程中,適當的增加過渡與動畫,能夠很好的讓使用者感知到頁面的變化

譬如我們頁面上隨處可見 loading 效果,其實就是這樣一種作用,讓使用者感知頁面正在載入,或者正在處理某些事務。

滾動優化

滾動也是操作網頁中非常重要的一環。看看有哪些可以優化的點:

滾動平滑:使用 scroll-behavior: smooth 讓滾動絲滑

使用 scroll-behavior: smooth,可以讓滾動框實現平穩的滾動,而不是突兀的跳動。看看效果,假設如下結構:

<div class="g-container">
  <nav>
    <a href="#1">1</a>
    <a href="#2">2</a>
    <a href="#3">3</a>
  </nav>
  <div class="scrolling-box">
    <section id="1">First section</section>
    <section id="2">Second section</section>
    <section id="3">Third section</section>
  </div>
</div>

不使用 scroll-behavior: smooth,是突兀的跳動切換:

scrol

給可滾動容器新增 scroll-behavior: smooth,實現平滑滾動:

{
    scroll-behavior: smooth;
}

scroll2

使用 scroll-snap-type 優化滾動效果

sroll-snap-type 可能算得上是新的滾動規範裡面最核心的一個屬性樣式。

scroll-snap-type:屬性定義在滾動容器中的一個臨時點(snap point)如何被嚴格的執行。

光看定義有點難理解,簡單而言,這個屬性規定了一個容器是否對內部滾動動作進行捕捉,並且規定了如何去處理滾動結束狀態。讓滾動操作結束後,元素停止在適合的位置。

看個簡單示例:

當然,scroll-snap-type 用法非常多,可控制優化的點很多,限於篇幅無法一一展開,具體更詳細的用法可以看看我的另外一篇文章 -- 使用 sroll-snap-type 優化滾動

控制滾動層級,避免頁面大量重排

這個優化可能稍微有一點難理解。需要了解 CSS 渲染優化的相關知識。

先說結論,控制滾動層級的意思是儘量讓需要進行 CSS 動畫(可以是元素的動畫,也可以是容器的滾動)的元素的 z-index 保持在頁面最上方,避免瀏覽器建立不必要的圖形層(GraphicsLayer),能夠很好的提升渲染效能

這一點怎麼理解呢,一個元素觸發建立一個 Graphics Layer 層的其中一個因素是:

  • 元素有一個 z-index 較低且包含一個複合層的兄弟元素

根據上述這點,我們對滾動效能進行優化的時候,需要注意兩點:

  1. 通過生成獨立的 GraphicsLayer,利用 GPU 加速,提升滾動的效能
  2. 如果本身滾動沒有效能問題,不需要獨立的 GraphicsLayer,也要注意滾動容器的層級,避免因為層級過高而被其他建立了 GraphicsLayer 的元素合併,被動的生成一個 Graphics Layer ,影響頁面整體的渲染效能

如果你對這點還有點懵,可以看看這篇文章 -- 你所不知道的 CSS 動畫技巧與細節

點選互動優化

在使用者點選互動方面,也有一些有意思的小細節。

優化手勢 -- 不同場景應用不同 cursor

對於不同的內容,最好給與不同的 cursor 樣式,CSS 原生提供非常多種常用的手勢。

在不同的場景使用不同的滑鼠手勢,符合使用者的習慣與預期,可以很好的提升使用者的互動體驗。

首先對於按鈕,就至少會有 3 種不同的 cursor,分別是可點選,不可點選,等待中:

{
    cursor: pointer;    // 可點選
    cursor: not-allowed;    // 不可點選
    cursor: wait;    // loading
}

image

除此之外,還有一些常見的,對於一些可輸入的 Input 框,使用 cursor: text,對於提示 Tips 類使用 cursor: help,放大縮小圖片 zoom-inzoom-out 等等:

image

一些常用的簡單列一列:

  • 按鈕可點選: cursor: pointer
  • 按鈕禁止點選:cursor: not-allowed
  • 等待 Loading 狀態:cursor: wait
  • 輸入框:cursor: text;
  • 圖片檢視器可放大可縮小:cursor: zoom-in/ zoom-out
  • 提示:cursor: help;

當然,實際 cursor 還支援非常多種,可以在 MDN 或者下面這個 CodePen Demo 中檢視這裡看完整的列表:

CodePen Demo -- Cursor Demo

點選區域優化 -- 偽元素擴大點選區域

按鈕是我們網頁設計中十分重要的一環,而按鈕的設計也與使用者體驗息息相關。

考慮這樣一個場景,在搖晃的車廂上或者是單手操作著螢幕,有的時候一個按鈕,死活也點不到。

讓使用者更容易的點選到按鈕無疑能很好的增加使用者體驗及可提升頁面的訪問性,尤其是在移動端,按鈕通常都很小,但是受限於設計稿或者整體 UI 風格,我們不能直接去改變按鈕元素的高寬。

那麼這個時候有什麼辦法在不改變按鈕原本大小的情況下去增加他的點選熱區呢?

這裡,偽元素也是可以代表其宿主元素來響應的滑鼠互動事件的。藉助偽元素可以輕鬆幫我們實現,我們可以這樣寫:

.btn::before{
  content:"";
  position:absolute;
  top:-10px;
  right:-10px;
  bottom:-10px;
  left:-10px;
}

當然,在 PC 端下這樣子看起來有點奇怪,但是合理的用在點選區域較小的移動端則能取到十分好的效果,效果如下:

608782-20160527112625428-906375003

在按鈕的偽元素沒有其它用途的時候,這個方法確實是個很好的提升使用者體驗的點。

快速選擇優化 -- user-select: all

作業系統或者瀏覽器通常會提供一些快速選取文字的功能,看看下面的示意圖:

layout3

快速單擊兩次,可以選中單個單詞,快速單擊三次,可以選中一整行內容。但是如果有的時候我們的核心內容,被分隔符分割,或者潛藏在一整行中的一部分,這個時候選取起來就比較麻煩。

利用 user-select: all,可以將需要一次選中的內容進行包裹,使用者只需要點選一次,就可以選中該段資訊:

.g-select-all {
    user-select: all
}

給需要一次選中的資訊,加上這個樣式後的效果,這個細節作用在一些需要複製貼上的場景,非常好用:

layout4

CodePen -- user-select: all 示例

選中樣式優化 -- ::selection

當然,如果你想更進一步,CSS 還有提供一個 ::selection 偽類,可以控制選中的文字的樣式(只能控制color, background, text-shadow),進一步加深效果。

layout5

CodePen -- user-select: all && ::selection 控制選中樣式

新增禁止選擇 -- user-select: none

有快速選擇,也就會有它的對立面 -- 禁止選擇。

對於一些可能頻繁操作的按鈕,可能出現如下尷尬的場景:

  • 文字按鈕的快速點選,觸發了瀏覽器的雙擊快速選擇,導致文字被選中:

btn-click

  • 翻頁按鈕的快速點選,觸發了瀏覽器的雙擊快速選擇:

對於這種場景,我們需要把不可被選中元素設定為不可被選中,利用 CSS 可以快速的實現這一點:

{
    -webkit-user-select: none; /* Safari */
    -ms-user-select: none; /* IE 10 and IE 11 */
    user-select: none; /* Standard syntax */
}

這樣,無論點選的頻率多快,都不會出現尷尬的內容選中:

btn-click-unselect

跳轉優化

現階段,單頁應用(Single Page Application)的應用非常廣泛,Vue 、React 等框架大行其道。但是一些常見的寫法,也容易衍生一些小問題。

譬如,點選按鈕、文字進行路由跳轉。譬如,經常會出現這種程式碼:

<template>
    ...
    <button @click="gotoDetail">
        Detail
    </button>
    ...
<template>
...
gotoDetail() {
    this.$router.push({
      name: 'xxxxx',
    });
}

大致邏輯就是給按鈕新增一個事件,點選之後,跳轉到另外一個路由。當然,本身這個功能是沒有任何問題的,但是沒有考慮到使用者實際使用的場景。

實際使用的時候,由於是一個頁面跳轉,很多時候,使用者希望能夠保留當前頁面的內容,同時開啟一個新的視窗,這個時候,他會嘗試下的滑鼠右鍵,選擇在新標籤頁中開啟頁面,遺憾的是,上述的寫法是不支援滑鼠右鍵開啟新頁面的。

原因在於瀏覽器是通過讀取 <a> 標籤的 href 屬性,來展示類似在新標籤頁中開啟頁面這種選項,對於上述的寫法,瀏覽器是無法識別它是一個可以跳轉的連結。簡單的示意圖如下:

image

所以,對於所有路由跳轉按鈕,建議都使用 <a> 標籤,並且內建 href 屬性,填寫跳轉的路由地址。實際渲染出來的 DOM 可能是需要類似這樣:

<a href="/xx/detail">Detail</a>

易用性

易用性也是互動設計中需要考慮的一個非常重要的環節,能做的有非常多。簡單的羅列一下:

  • 注意介面元素的一致性,降低使用者學習成本
  • 延續使用者日常的使用習慣,而不是重新創造
  • 給下拉框增加一些預設值,降低使用者填寫成本
  • 同類的操作合併在一起,降低使用者的認知成本
  • 任何操作之後都要給出反饋,讓使用者知道操作已經生效

先探索,後表態

這一點非常的有意思,什麼叫先探索後表態呢?就是我們不要一上來就強迫使用者去做一些事情,譬如登入。

想一想一些常用網站的例子:

  • 類似虎牙、Bilibili 等視訊網站,可以先觀看體驗,一定觀看時間後才會要求登入(登入享受藍光)
  • 電商網站,只有到付款的時候,才需要登入

上述易用性先探索,後表態的內容,部分來源於:Learn From What Leading Companies A/B Test,可以好好讀一讀。

字型優化

字型的選擇與使用其實是非常有講究的。

如果網站沒有強制必須使用某些字型。最新的規範建議我們更多的去使用系統預設字型。也就是 CSS Fonts Module Level 4 -- Generic font families 中新增的 font-family: system-ui 關鍵字。

font-family: system-ui 能夠自動選擇本作業系統下的預設系統字型。

預設使用特定作業系統的系統字型可以提高效能,因為瀏覽器或者 webview 不必去下載任何字型檔案,而是使用已有的字型檔案。 font-family: system-ui 字型設定的優勢之處在於它與當前作業系統使用的字型相匹配,對於文字內容而言,它可以得到最恰當的展示。

舉兩個例子,天貓的字型定義與 Github 的字型定義:

  • 天貓font-family: "PingFang SC",miui,system-ui,-apple-system,BlinkMacSystemFont,Helvetica Neue,Helvetica,sans-serif;
  • Githubfont-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;

簡單而言,它們總體遵循了這樣一個基本原則:

1、儘量使用系統預設字型

使用系統預設字型的主要原因是效能,並且系統字型的優點在於它與當前作業系統使用的相匹配,因此它的文字展示必然也是一個讓人舒適展示效果。

2、兼顧中西,西文在前,中文在後

中文或者西文(英文)都要考慮到。由於大部分中文字型也是帶有英文部分的,但是英文部分又不怎麼好看,但是英文字型中大多不包含中文。通常會先進行英文字型的宣告,選擇最優的英文字型,這樣不會影響到中文字型的選擇,中文字型宣告則緊隨其次。

3、兼顧多作業系統

選擇字型的時候要考慮多作業系統。例如 MAC OS 下的很多中文字型在 Windows 都沒有預裝,為了保證 MAC 使用者的體驗,在定義中文字型的時候,先定義 MAC 使用者的中文字型,再定義 Windows 使用者的中文字型;

4、兼顧舊作業系統,以字型族系列 serif 和 sans-serif 結尾

當使用一些非常新的字型時,要考慮向下相容,兼顧到一些極舊的作業系統,使用字型族系列 serif 和 sans-serif 結尾總歸是不錯的選擇。

對於上述的一些字型可能會有些懵,譬如 -apple-system, BlinkMacSystemFont,這是因為不同瀏覽器廠商對規範的實現有所不同,對於字型定義更多的相關細節,可以再看看這篇文章 -- Web 字型 font-family 再探祕

可訪問性(A11Y)

可訪問性,在我們的網站中,屬於非常重要的一環,但是大部分前端(其實應該是設計、前端、產品)同學都會忽視它。

我潛伏在一個叫無障礙設計小組的群裡,其中包含了很多無障礙設計師以及患有一定程度視覺、聽力、行動障礙的使用者,他們在群裡經常會表達出一個觀點,就是國內的大部分 Web 網站及 APP 基本沒有考慮過殘障人士的使用(或者可訪問性做的非常差),非常的令人揪心。

尤其在我們一些重互動、重邏輯的網站中,我們需要考慮使用者的使用習慣、使用場景,從高可訪問性的角度考慮,譬如假設使用者沒有滑鼠,僅僅使用鍵盤,能否順暢的使用我們的網站?

假設使用者沒有滑鼠,這個真不一定是針對殘障人士,很多情況下,使用者拿滑鼠的手可能在幹其他事情,比如在吃東西,又或者在 TO B 類的業務,如超市收銀、倉庫收貨,很可能使用者拿滑鼠的手操作著其他裝置(掃碼槍)等等。

本文不會專門闡述無障礙設計的方方面面,只是從一些我覺得前端工程師需要關注的,並且僅需要花費少量代價就能做好的一些無障礙設計細節。記住,無障礙設計對所有人都更友善

色彩對比度

顏色,也是我們天天需要打交道的屬性。對於大部分視覺正常的使用者,可能對頁面的顏色敏感度還沒那麼高。但是對於一小部分色弱、色盲使用者,他們對於網站的顏色會更加敏感,不好的設計會給他們訪問網站帶來極大的不便。

什麼是色彩對比度

是否曾關心過頁面內容的展示,使用的顏色是否恰當?色弱、色盲使用者能否正常看清內容?良好的色彩使用,在任何時候都是有益的,而且不僅僅侷限於對於色弱、色盲使用者。在戶外用手機、陽光很強看不清,符合無障礙標準的高清晰度、高對比度文字就更容易閱讀。

這裡就有一個概念 -- 顏色對比度,簡單地說,描述就是兩種顏色在亮度(Brightness)上的差別。運用到我們的頁面上,大多數的情況就是背景色(background-color)與內容顏色(color)的對比差異。

最權威的網際網路無障礙規範 —— WCAG AA規範規定,所有重要內容的色彩對比度需要達到 4.5:1 或以上(字號大於18號時達到 3:1 或以上),才算擁有較好的可讀性。

借用一張圖 -- 知乎 -- 助你輕鬆做好無障礙的15個UI設計工具推薦

很明顯,上述最後一個例子,文字已經非常的不清晰了,正常使用者都已經很難看得清了。

檢查色彩對比度的工具

Chrome 瀏覽器從很早開始,就已經支援檢查元素的色彩對比度了。以我當前正在寫作的頁面為例子,Github Issues 編輯頁面的兩個按鈕:

image

審查元素,分別可以看到兩個按鈕的色彩對比度:

image

可以看到,綠底白字按鈕的色彩對比度是沒有達到標準的,也被用黃色的歎號標識了出來。

除此之外,在審查元素的 Style 介面的取色器,改變顏色,也能直觀的看到當前的色彩對比度:

image

焦點響應

類似百度、谷歌的首頁,進入頁面後會預設讓輸入框獲得焦點:

image

並非所有的有輸入框的頁面,都需要進入頁面後進行聚焦,但是焦點能夠讓使用者非常明確的知道,當前自己在哪,需要做些什麼。尤其是對於無法操作滑鼠的使用者。

頁面上可以聚焦的元素,稱為可聚焦元素,獲得焦點的元素,則會觸發該元素的 focus 事件,對應的,也就會觸發該元素的 :focus 偽類。

瀏覽器通常會使用元素的 :focus 偽類,給元素新增一層邊框,告訴使用者,當前的獲焦元素在哪裡。

我們可以通過鍵盤的 Tab 鍵,進行焦點的切換,而獲焦元素則可以通過元素的 :focus 偽類的樣式,告訴使用者當前焦點位置。

當然,除了 Tab 鍵之外,對於一些多輸入框、選擇框的表單頁面,我們也應該想著如何簡化使用者的操作,譬如使用者按Enter鍵時自動前進到下一欄位。一般而言,使用者必須執行的觸按越少,體驗越佳。?

下面的截圖,完全由鍵盤操作完成

a11y

通過元素的 :focus 偽類以及鍵盤 Tab 鍵切換焦點,使用者可以非常順暢的在脫離滑鼠的情況下,對頁面的焦點切換及操作。

然而,在許多 reset.css 中,經常能看到這樣一句 CSS 樣式程式碼,為了樣式的統一,消除了可聚焦元素的 :focus 偽類:

:focus {
    outline: 0;
}

我們給上述操作的程式碼。也加上這樣一句程式碼,全程再用鍵盤操作一下

a11y2

除了在 input 框有游標提示,當使用 Tab 進行焦點切換到 select 或者到 button 時,由於沒有了 :focus 樣式,使用者將完全懵逼,不知道頁面的焦點現在處於何處。

保證非滑鼠使用者體驗,合理運用 :focus-visible

當然,造成上述結果很重要的一個原因在於。:focus 偽類不論使用者在使用滑鼠還是使用鍵盤,只要元素獲焦,就會觸發。

而其本身的預設樣式又不太能被產品或者設計接受,導致了很多人會在焦點元素觸發 :focus 偽類時,通過改變 border 的顏色或者其他一些方式替代或者直接禁用。而這樣做,從可訪問性的角度來看,對於非滑鼠使用者,無疑是災難性的。

基於此,在W3 CSS selectors-4 規範 中,新增了一個非常有意思的 :focus-visible 偽類。

:focus-visible:這個選擇器可以有效地根據使用者的輸入方式(滑鼠 vs 鍵盤)展示不同形式的焦點。

有了這個偽類,就可以做到,當使用者使用滑鼠操作可聚焦元素時,不展示 :focus 樣式或者讓其表現較弱,而當使用者使用鍵盤操作焦點時,利用 :focus-visible,讓可獲焦元素獲得一個較強的表現樣式。

看個簡單的 Demo:

<button>Test 1</button>
button:active {
  background: #eee;
}
button:focus {
  outline: 2px solid red;
}

使用滑鼠點選:

a11y3

可以看到,使用滑鼠點選的時候,觸發了元素的 :active 偽類,也觸發了 :focus偽類,不太美觀。但是如果設定了 outline: none 又會使鍵盤使用者的體驗非常糟糕。嘗試使用 :focus-visible 偽類改造一下:

button:active {
  background: #eee;
}
button:focus {
  outline: 2px solid red;
}
button:focus:not(:focus-visible) {
  outline: none;
}

看看效果,分別是在滑鼠點選 Button 和使用鍵盤控制焦點點選 Button:

a11y4

CodePen Demo -- :focus-visible example

可以看到,使用滑鼠點選,不會觸發 :foucs,只有當鍵盤操作聚焦元素,使用 Tab 切換焦點時,outline: 2px solid red 這段程式碼才會生效。

這樣,我們就既保證了正常使用者的點選體驗,也保證了一批無法使用滑鼠的使用者的焦點管理體驗。

值得注意的是,有同學會疑惑,這裡為什麼使用了 :not 這麼繞的寫法而不是直接這樣寫呢:

button:focus {
  outline: unset;
}
button:focus-visible {
  outline: 2px solid red;
}

為的是相容不支援 :focus-visible 的瀏覽器,當 :focus-visible 不相容時,還是需要有 :focus 偽類的存在。

使用 WAI-ARIA 規範增強語義 -- div 等非可獲焦元素模擬獲焦元素

還有一個非常需要注意的點。

現在很多前端同學在前端開發的過程中,喜歡使用非可獲焦元素模擬獲焦元素,譬如:

  • 使用 div 模擬 button 元素
  • 使用 ul 模擬下拉選單 select 等等

當下很多元件庫都是這樣做的,譬如 element-ui 和 ant-design。

在使用非可獲焦元素模擬獲焦元素的時候,一定要注意,不僅僅只是外觀長得像就完事了,其行為表現也需要符合原本的 buttonselect 等可聚焦元素的性質,能夠體現元素的語義,能夠被聚焦,能夠通過 Tab 切換等等。

基於大量類似的場景,有了 WAI-ARIA 標準,WAI-ARIA是一個為殘疾人士等提供無障礙訪問動態、可互動Web內容的技術規範。

簡單來說,它提供了一些屬性,增強標籤的語義及行為:

  • 可以使用 tabindex 屬性控制元素是否可以聚焦,以及它是否/在何處參與順序鍵盤導航
  • 可以使用 role 屬性,來標識元素的語義及作用,譬如使用 <div id="saveChanges" tabindex="0" role="button">Save</div> 來模擬一個按鈕
  • 還有大量的 aria-* 屬性,表示元素的屬性或狀態,幫助我們進一步地識別以及實現元素的語義化,優化無障礙體驗

使用工具檢視標籤的語義

我們來看看 Github 頁面是如何定義一個按鈕的,以 Github Issues 頁面的 Edit 按鈕為例子:

image

這一塊,清晰的描述了這個按鈕在可訪問性相關的一些特性,譬如 Contrast 色彩對比度,按鈕的描述,也就是 Name,是給螢幕閱讀器看到的,Role 標識是這個元素的屬性,它是一個按鈕,Keyboard focusable 則表明他能否被鍵盤的 Tab 按鈕給捕獲。

分析使用非可聚焦元素模擬的按鈕

這裡,我隨便選取了我們業務中一個使用 span 模擬按鈕的場景,是一個麵包屑導航,點選可進行跳轉,發現慘不忍睹:

image

HTML 程式碼:

<span class="ssc-breadcrumb-item-link"> Inbound </span>

image

基本上可訪問性為 0,作為一個按鈕,它不可被聚焦,無法被鍵盤使用者選中,沒有具體的語義,色彩對比度太低,可能視障使用者無法看清。並且,作為一個能進行頁面跳轉的按鈕,它沒有不是 a 標籤,沒有 href 屬性。

即便對於麵包屑導航,我們可以不將它改造成 <a> 標籤,也需要做到最基本的一些可訪問性改造:

<span role="button" aria-label="goto inbound page" tabindex="0" class="ssc-breadcrumb-item-link"> Inbound </span>

不要忘了再改一下顏色,達到最低色彩對比度以上,再看看:

image

OK,這樣,一個最最最基本的,滿足最低可訪問性需求的按鈕算是勉強達標,當然,這個按鈕可以再更進一步進行改造,涉及了更深入的可訪問性知識,本文不深入展開。

分析元件庫的 A11Y

最後,在我們比較常用的 Vue - element-ui、React - ant-design 中,我們來看看 ant-design 在提升可訪問性相關的一些功能。

以 Select 選擇框元件為例,ant-design 利用了大量的 WAI-ARIA 屬性,使得用 div 模擬的下拉框不僅僅在表現上符合一個下拉框,在語義、行為上都符合一個下拉框,簡單的一個例子:

image

看看使用 div 模擬下拉框的 DOM 部分:

image

再看看在互動體驗上:

a11y5

上述操作全是在鍵盤下完成,看著平平無奇,實際上元件庫在正常響應可獲焦元素切換的同時,給用 div 模擬的 select 加了很多鍵盤事件的響應,可以利用回車,上下鍵等對可選項進行選擇。其實是下了很多功夫。

對於 A11Y 相關的內容,篇幅及內容非常之多,本文無法一一展開,感興趣的可以通讀下下列文章:

總結一下

本文從頁面展示互動細節可訪問性三個大方面入手,羅列一些在實際的開發過程中,積攢的一些有益的經驗。雖然不夠全面,不過從一開始也就沒想著大而全,主要是一些可能有用但是容易被忽視的點,也算是一個不錯的查缺補漏小指南。

當然,很多都是我個人的觀點想法,可能有一些理解存在一些問題,一些概念沒有解讀到位,也希望大家幫忙指出。

最後

本文到此結束,希望對你有幫助 ?

更多精彩 CSS 技術文章彙總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

如果還有什麼疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

相關文章