Web 使用者體驗設計提升實踐

Shopee技術團隊發表於2021-11-18
本文首發於微信公眾號“Shopee技術團隊”。

前言

本文是基於 Shopee 供應鏈團隊內部 WMS(Warehouse Management System,倉庫管理系統) 專案的整體重構,總結而出的一份 Web 使用者體驗設計提升指南。

因為是對已經存在的專案進行完全的推翻重構,所以在整個過程中,我們一直在思考如何儘可能地站在使用者的角度,通過前端設計去提升改進使用者的體驗與感受,真正達到重構的目的及意義。

一個 Web 頁面或是一個 App,想讓別人用得爽,獲得良好的使用者體驗,可能需要包括但不限於:

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

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

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

本文將主要從頁面呈現、互動細節、可訪問性三個方面入手,分享一些在實際開發過程中積攢的有益經驗。通過這篇文章,你將能:

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

1. 頁面呈現

就整個頁面的展示和頁面內容的呈現而言,不同的展示方式,所得到的效果截然不同。

這其中有非常多值得注意的細節。接下來分為幾個要點進行闡述:

  • 自適應的佈局
  • 重點內容的排佈設計
  • 相容不同場景與異常回退
  • 圖片的呈現及異常處理
  • 適當的過渡與動畫

1.1 自適應的佈局

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

佈局,是前端在重構頁面過程中需要提前進行規劃思考的,一般應該考慮清楚以下幾個問題:

  • 對於 PC 端,專案是全屏佈局還是定寬佈局?使用者是否還在使用 IE?
  • 對於全屏佈局,需要適配的最小寬度是多少?
  • 對於移動端佈局,你知道使用者裝置的分佈嗎?最少相容到 Android 什麼版本?iOS 什麼版本?
  • 內容應該以什麼樣的方式呈現?

到今天,各種裝置浩如煙海,移動端螢幕尺寸紛繁複雜(下圖僅僅是到 2019 年各種安卓裝置螢幕尺寸圖的分佈):

不過,我們的重構專案整體是以 PC 為主的 ToB 專案,所以這裡主要以 PC 端為例進行講解。

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

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

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

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

對於定寬佈局,就比較方便了,假設定寬為 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 正常流排布(也就是需要滾動到底部才能看到 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

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

1.2 重點內容的排佈設計

下面這一塊關於重點內容的展示。

1.2.1 重要內容及功能的展示

讓吸引使用者注意力的元素前置。如果我們的頁面存在需要讓使用者瞭解、處理的核心資訊或者表單,儘可能將其位置放在上方,讓使用者更容易獲取這部分資訊。

將使用者需要的資訊、重要的功能展示出來而不是藏起來。

類似於導航、搜尋等高頻操作,一定不要讓使用者多次點選才能用到。

1.2.2 處理動態內容:文字超長

對於所有接收後端介面欄位的文字展示類的介面,都需要考慮全面。正常情況如下,是沒有問題的:

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

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

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

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

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

1.2.3 處理動態內容:保護邊界

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

在使用它們的時候,也有一些細節需要考慮到。
譬如經常會使用 min-width 控制按鈕的最小寬度:

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

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

這裡就需要配合 padding 一起:

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

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

1.3 相容不同場景與異常回退:空資料內容展示

這個模組是相容不同場景與異常回退,是一個常常被忽略的地方。

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

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

(1)資料為空

  • 使用者無許可權——要告知使用者無許可權訪問的原因和解決方案
  • 搜尋無結果——告知使用者搜尋無資料的結果,如有必要可推薦相關內容
  • 篩選無結果——一般直接告知篩選無結果
  • 頁面無資料——文案設計有幾個方向:

    • 告訴使用者這裡將會存放什麼資料
    • 給使用者一個主動創造資料的理由,比如通過話術引起使用者心理共鳴
    • 若頁面無資料會給使用者造成困惑,則可以說明原因打消使用者的困惑

(2)異常狀態

  • 網路異常——指出當前狀態為網路異常,並給出解決方案
  • 伺服器異常——指出當前狀態為伺服器異常,並給出解決方案
  • 載入失敗——載入失敗主要會由網路異常或伺服器異常造成
  • 不同的情況可能對應不同的空資料結果頁面,附帶不同的操作引導

例如網路異常,可以引導使用者重新整理頁面:

或者確實是零結果,譬如沒有訂單資訊,可以引導使用者去進行訂單的建立(引導消費):

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

1.4 圖片的呈現及異常處理

圖片在我們的業務中非常常見。但是要完美處理圖片,並不輕鬆。

1.4.1 給圖片同時設定高寬

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

對應的佈局:

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

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

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

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

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

1.4.2 object-fit

限制高寬也可能會出現問題,比如圖片被拉伸了,非常難看:

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

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

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

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

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

像是這樣,object-position: 100% 50% 指明從底部開始展示圖片。

這裡有一個很好的 Demo 可以幫助你理解 object-position

CodePen Demo - Object position

1.4.3 考慮螢幕 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 的螢幕提供最適合的圖片。

1.4.4 圖片丟失

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

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

  • 利用圖片載入失敗,觸發 <img> 元素的 onerror 事件,給載入失敗的 <img> 元素新增一個樣式類;
  • 利用新增的樣式類,配合 <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 資訊:

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

1.5 適當的過渡與動畫

好的頁面呈現需要適當的過渡與動畫,讓整體互動體驗更加流暢。適當增加過渡與動畫,能夠很好地讓使用者感知到頁面的變化,它們有如下作用:

  • 減少認知負荷
  • 防止變化視盲
  • 空間上營造更好的印象
  • 讓使用者介面鮮活起來

這一塊內容也可以放在互動設計優化,讀者朋友們瞭解就好。

1.5.1 loading 等待動畫

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

23.gif

1.5.2 骨架屏動畫

骨架屏的佈局與頁面的視覺呈現保持一致,能引導使用者將關注點聚焦到感興趣的位置,並且能避免過長時間的等待。

1.5.3 滾動平滑:使用 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,就是突兀地跳動切換:

要改善這種現象,可以給可滾動容器新增 scroll-behavior: smooth,實現平滑滾動:

{
    scroll-behavior: smooth;
}

1.5.4 粘性滾動:使用 scroll-snap-type 優化滾動效果

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

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

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

看個簡單示例:

當然,scroll-snap-type 用法非常多,可控制優化的點很多,限於篇幅無法一一展開。

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

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

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

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

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

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

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

1.5.6 轉場動畫

從一個模組跳轉到另外一個模組的時候,轉場動畫就派上了用場。它的作用在於:在合適的時機,將視線引導到適當的位置。

看下面的例子:

在點選按鈕彈出彈窗的過程中,彈窗不是突兀地出現,而是從點選的地方放大至視窗中間,這個引導的過程讓體驗更加絲滑。

1.5.7 操作動畫

這個和 loading 有點類似,遇到一些耗時操作,比如下載時,我們可以通過定製一個特殊的動畫,減緩使用者等待的煩躁、焦慮感。

上述動畫的程式碼,你可以猛擊:

CodePen Demo - Download interaction By Milan Raring

當然,除了下載等待,我們也可以在一些重要的操作互動上,例如點贊、關注等,定製特殊的動畫,讓過程更加生動有趣。下面這個是某網站的點贊動畫:

上述點贊動畫的程式碼,你可以猛擊:

CodePen Demo - Twitter 點贊動畫

1.5.8 使用過渡與動畫的誤區

合理使用動畫能讓頁面增色不少,但同時要避免踩入下面的一些坑:

  • 動畫沒有關聯性
  • 為了動畫而動畫,沒有目的性
  • 過於緩慢,阻礙互動
  • 不夠明確

簡單解釋一下。譬如動畫關聯性,關聯性背後的邏輯能幫助使用者在介面佈局中理解剛發生的變化,是什麼導致了變化。

下圖中,左邊是關聯性差的,右邊是關聯性好的:

還有一點,大部分動畫不宜過久,要足夠迅速。

緩慢的動畫產生了不必要的停頓。過渡動畫應該保持簡短,因為使用者會頻繁看到它們。讓動畫持續時間保持在 300ms 或更短。

看下圖演示,同一個轉場動畫會被頻繁觸發,所以儘可能地讓每次的動畫不要持續過久,能夠幫助使用者節省更多時間。

總而言之,動畫和過渡要用得恰當好處,避免為了動畫而動畫非常重要

2. 互動設計優化

接下來一個大環節是一些關於互動的細節。

什麼是互動設計?互動設計(Interaction Design, IXD)是定義、設計人造系統的行為的設計領域,它定義了兩個或多個互動的個體之間交流的內容和結構,使之互相配合,共同達成某種目的

2.1 Web 端互動方式

Web 端互動圍繞計算機為中心,主要涉及鍵盤、滑鼠兩類裝置的互動。

關於互動設計,一些比較通用的準則有:

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

2.2 Don’t make me think

有一本書就叫 Don’t make me think,它所想表述的核心是,儘可能讓產品做到高可用性,讓使用者無須思考也能明確無誤地使用產品中的各項功能。

那麼,我們的頁面如何儘可能做到高可用性呢?

2.2.1 使用習慣用法

使用習慣用法。當我們設計一個新的頁面時,頁面位置、功能設定、視覺元素應當和慣常用法相差無幾,這樣才能讓使用者有舒適感。

這裡並不是要扼殺大家的想象力,讓所有頁面都一成不變。根據產品的形態及受眾,大部分頁面是不適用於各種天馬行空的想象的。

例如下面這樣一個 PC 端頁面結構,這種佈局已經非常常見。遇到這種頁面,使用者能夠非常快速地在指定區域找到想要的元素及資訊:

可以再和我們的 WMS 重構後的頁面作比較,整體是非常類似的:

採用習慣用法的設定能夠讓人快速上手新的陌生的頁面或者功能。

2.2.2 降低視覺噪聲

另外一種做法是降低視覺噪聲,讓使用者快速聚焦。

譬如彈窗背後的陰影、虛化,就是非常好的降低視覺噪聲的手段。

這一點還是非常好理解的,也屬於常見設計手段之一,在我們內部的相關元件、自研元件庫已經沉澱得很好了。

2.3 符合使用者的習慣與預期

接下來是符合使用者的習慣與預期,它的意義在於減少使用者的思考,符合使用者的習慣,讓使用者的體驗更加舒適。

下面這幾種彈窗,哪一個是更好的選擇?

三個彈窗可供操作的按鈕分別是:

  • 右下角的取消和確認,同時右上角的 X 可以關閉彈窗;
  • 只有右下角的取消和確認;
  • 只有確認按鈕。

更好的選擇應該是第一個:同時有取消和確認按鈕,右上角的 X 可以關閉彈窗。因為這樣最符合使用者的習慣預期。

看看這個 Windows 下的彈窗:

大部分 Windows 的彈窗,都是有取消、確認加上右上角的 X 按鈕的。這種彈窗的好處在於:

  • 視覺原因,元素平衡;
  • 符合使用者習慣,大多數使用者都是 Windows 作業系統過來的;
  • 互動一致性,降低使用者學習成本;
  • 提升頁面的可訪問性和無障礙訪問性。

2.3.1 優化手勢:不同場景應用不同 cursor

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

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

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

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

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

一些常用的簡單列一列:

  • 按鈕可點選: 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

2.3.2 點選區域優化:偽元素擴大點選區域

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

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

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

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

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

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

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

2.4 操作便利

好的系統,操作起來應該是流暢的。同時,它能夠通過一些小細節打動使用者。

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

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

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

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

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

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

在我們 WMS 的很多操作頁面,需要頻繁從表格中複製一些基礎資訊,例如下述的 Location IDCell Name,由於完整的欄位使用了分隔符 -,所以一次點選是無法選中整段資訊的,而利用 user-select: all 可以很好地解決這個痛點:

別看只是減少一次點選滑鼠的次數,但正是這些細節的累積,才更能讓使用者感受到開發者的用心。

CodePen - user-select: all 示例

2.4.2 選中樣式優化:::selection

CSS 還提供了一個 ::selection 偽類,可以控制選中的文字樣式(只能控制 colorbackgroundtext-shadow),進一步加深效果。

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

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

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

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

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

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

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

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

2.5 跳轉優化

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

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

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

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

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

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

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

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

在實際的 WMS 重構過程中,我們對於所有有頁面跳轉功能的按鈕,包括但不限於路由選單、麵包屑導航、跳轉按鈕等,都進行了跳轉優化,以滿足使用者的不同訴求。

路由選單導航:

表格中的一些按鈕跳轉:

2.6 表單互動優化

輸入及選擇於使用者而言,是一項高互動成本的操作。下面提供了一些小的建議來減少使用者輸入出錯、提升使用者體驗。

2.6.1 儘可能地簡化表單

將表單做得簡單點,確保使用者在抓狂之前能進入下一步(表單越複雜,流失率越高):

對於沒法省去的輸入項,儘可能簡化使用者的輸入:

  • 智慧預設預設項
  • 輸入時提供智慧聯想
  • 對於選擇框,儘可能精簡選項資訊
  • 使用單選項來代替下拉選單

2.6.2 及時校驗

表單及時校驗,而不是使用者填完一堆資訊,統一提交後才告訴使用者填錯了:

2.6.3 貼心細節提示,校驗更寬容

還有一些比較有益的建議,可以有效的提升互動過程中使用者的體驗,根據實際情況可以考慮:

  • 在表單中增加一些提示資訊,減少錯誤的機率
  • 嘗試將表單輸入變得更加寬容,讓使用者的填寫更加簡單

2.7 先探索,後表態

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

  • 類似虎牙、bilibili 等視訊網站,可以先藍光體驗,一定觀看時間後才會要求登入;
  • 電商網站,只有到付款的時候,才需要登入。
上述易用性先探索後表態的內容,部分來源於 “Learn From What Leading Companies A/B Test”,推薦閱讀。

2.8 結合產品的創意互動動畫

由於業務的型別限制,在這一塊,我們實際中運用的並不多,但是它也是增強使用者體驗非常有益且重要的一環,下文將簡單講一講。

這一類互動為結合產品的創意互動動畫。通過定製化的有儀式感的互動,提升品牌價值,能給予使用者深刻的印象。

結合產品及業務的創意動畫,是需要挖掘,不斷打磨、不斷迭代的。比如 bilibili 官網的頂部 banner,配合一些節日、活動,經常會出現一些有意思的創意互動動畫:

以及這個:

我非常多次在不同地方看到有人討論 bilibili 的頂部 banner 動畫,可見它這塊的動畫是成功的。很好地結合了一些節日、實事、熱點,作為一種比較固定的產品去不斷推陳出新,在不同時候帶給使用者不同的體驗。

2.9 字型優化

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

在 WMS 專案重構過程中,我們使用的全域性字型定義是:
font-family: Roboto,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Oxygen,Ubuntu,Fira Sans,Droid Sans,Helvetica Neue,"sans-serif";

這裡,我們的設計師對英文字型有一些強限制,首選字型是 Roboto,並且在系統中提供了該字型包。

如果網站沒有強制必須使用某些字型。最新的規範建議我們使用系統預設字型。也就是 “CSS Fonts Module Level 4: Generic font families” 中新增的 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;
  • Github:font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;

簡單而言,它們總體遵循了以下幾項基本原則:

2.9.1 儘量使用系統預設字型

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

2.9.2 兼顧中西:西文在前,中文在後

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

2.9.3 兼顧多作業系統

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

2.9.4 兼顧舊作業系統:以字型族系列 serif 和 sans-serif 結尾

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

3. 可訪問性(A11Y)

可訪問性(accessibility,也被稱為 “A11Y”)在網站中屬於非常重要的一環,但是大部分前端(其實應該是設計、前端、產品)同學都忽視了它。

3.1 什麼是可訪問性

傳統上我們認為這隻與殘障人士有關,但提升網站的可訪問性也可以讓其他使用者群體(每個人)受益。

根據網頁內容可訪問性準則(即 “Web Content Accessibility Guidelines (WCAG) 2.0”),Web 可訪問性有以下四個基礎性原則。

(1)可感知

  • 用文字替代非文字內容
  • 多媒體的字幕及其他替代物
  • 內容有多種呈現方式
  • 內容的看和聽更容易

(2)可操作

  • 可以通過鍵盤使用功能
  • 使用者有充足的時間閱讀和使用內容
  • 內容不要誘發癲癇和物理反應
  • 使用者可以方便地導航、找到內容並確認自己在哪裡
  • 使用者可以使用除鍵盤外的不同輸入方式

(3)可理解

  • 文字容易閱讀和理解
  • 內容以可預測的方式出現和操作
  • 使用者可以得到幫忙,以避免和糾正錯誤

(4)健壯性

  • 健壯的內容和可靠的解釋
  • 內容與現在和未來的使用者工具相容
  • 在各個平臺和環境中都能正確地展示
我潛伏在一個叫無障礙設計小組的群裡,其中包含了很多無障礙設計師以及患有一定程度視覺、聽力、行動障礙的使用者,他們在群裡經常會表達出一個觀點,就是國內的大部分 Web 網站及 App 基本沒有考慮過殘障人士的使用(或者可訪問性做得很差),非常令人揪心。

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

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

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

3.2 色彩對比度

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

3.2.1 什麼是色彩對比度

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

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

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

看看下面這張圖:

很明顯,對比度由低到高。前兩個例子,別說視障使用者,正常使用者都已經很難看得清了。

當然,也會存在一些特例,譬如輸入框的 placeholder、按鈕的禁用狀態等等。因此,在網頁重構的過程中,我們需要儘可能遵循這個規範。但又不是盲目遵循,導致毫無迴旋餘地。

3.2.2 檢查色彩對比度的工具

Chrome 瀏覽器從很早開始,就已經支援檢查元素的色彩對比度了。以 WMS 頁面中的一個 Table Header 為例,我們可以去檢查一些無法直觀界定是否達到標準的文字:

審查元素,分別可以看到當前文字與背景的對比度。下述結果表示,這個對比度是沒有問題的:

當然,我們的頁面上也會存在這樣的麵包屑導航:

可以看到,色彩對比度沒有達到標準的部分,被用黃色歎號標識了出來。

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

3.2.3 不要單純依賴顏色

為了保證無障礙的準確性,應當確保你沒有完全依賴顏色來展示系統不同層級的關鍵資訊。

看看下面這個例子:

本來我們期待利用綠色和紅色來表達正確與錯誤,但是對於部分視覺障礙使用者,他可能根本感受不到這個提示。

正確的做法是,使用必要的文字和圖示進行說明:

因此在實際應用中,我們需要利用能傳達準確資訊的圖示配合文字描述去表達,譬如 WMS 登入錯誤頁面:

3.3 焦點響應

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

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

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

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

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

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

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

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

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

:focus {
    outline: 0;
}

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

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

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

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

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

基於此,在 W3 CSS selectors-4 規範中,新增了一個非常有意思的 :focus-visible 偽類。這個選擇器可以有效地根據使用者的輸入方式(滑鼠或是鍵盤)展示不同形式的焦點。

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

看個簡單的 Demo:

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

使用滑鼠點選:

可以看到,使用滑鼠點選的時候,觸發了元素的 :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:

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 偽類的存在。

我們在實際的 WMS 重構過程中,也會盡量保持這一點,讓使用者儘可能在非滑鼠操作下(僅僅使用鍵盤),也能使用我們的頁面,能做到基礎的焦點切換、回車響應事件。

下圖是一個簡單的演示(僅僅使用鍵盤進行頁面的操作,能夠知道當前焦點在哪,可以回車觸發點選):

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

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

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

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

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

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

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

簡單來說,WAI-ARIA 提供了一些屬性,用於增強標籤的語義及行為:

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

3.4 使用工具檢視標籤的語義

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

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

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

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

HTML 程式碼:

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

<br/>

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

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

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

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

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

3.6 分析元件庫的可訪問性

最後,我們來看看常用的 ant-design 在提升可訪問性上的一些相關功能。

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

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

再看看在互動體驗上:

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

對於可訪問性相關的內容非常多,本文無法一一展開,這裡有一份簡單的指南:

4. 總結

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

提升使用者體驗並非易事,但也不難:

  • 頁面呈現 + 注重細節的互動設計 + 完善的可訪問性 + 效能(效能本文沒有過多提及) = 良好的使用者體驗;
  • 使用者體驗是可以被提升的,而且並不難;
  • 良好的使用者體驗設計,是產品開發每一個環節共同努力的結果;
  • 提升使用者體驗也不是能夠一蹴而就的,在不同的細節發力,積少成多。

以上觀點和想法可能有一些理解存在問題,一些概念沒有解讀到位,也希望大家參與交流並指正。

參考資料

本文作者

Coco,前端開發工程師,來自 Shopee 供應鏈倉儲管理(Warehouse management system, WMS)團隊。

相關文章