[譯] 如何讓你的 CSS Grid 佈局有良好的可訪問性

Yuqi發表於2019-04-25

CSS Grid 可以將元素放入有行和列的網格中,從而讓建立二維佈局成為可能。有了它,你可以自定義網格的任何形態,例如網格寬高、網格範圍、或者網格之間的間隙。但是,CSS Grid 可能會有訪問性不佳的問題,尤其是對於那些使用螢幕閱讀器和僅使用鍵盤的使用者。本篇教程將會幫助你避免此類問題。

源順序獨立性

“源順序獨立性”是 CSS Grid 強大優勢之一。這意味著你不需要像使用 float 或者表格佈局那樣,在 HTML 中定義佈局結構。你可以使用 CSS Grid 的排序和網格位置屬性改變 HTML 呈現的視覺效果。

W3C 的 CSS Grid 文件中的重排序和可訪問性章節,將源順序獨立性定義為:

“通過將網格佈局與媒體查詢相結合,開發者可以使用相同的語義標記,但是元素佈局的重新排列是脫離原始碼順序而獨立存在的,這樣就可以同時在原始碼順序和渲染出的視覺效果兩個方面實現需要的佈局。”

使用 CSS Grid,你可以將邏輯順序和視覺順序解耦。源順序獨立性在很多時候都非常有用,但是它也有可能會破壞程式碼的可訪問性。使用螢幕閱讀器和鍵盤的使用者都只能看到你 HTML 檔案的程式碼邏輯順序,但是無法看到通過 CSS Grid 建立出來的視覺順序。

如果你的文件很簡單,這通常不是什麼大問題,因為這時候原始碼邏輯順序和視覺順序基本是一致的。但是,比較複雜、不對稱、零散,或者使用了其他創意佈局的檔案通常就會對使用螢幕閱讀器或者鍵盤的使用者造成困惑。

能改變視覺順序的屬性

CSS Grid 有很多可以改變文件視覺順序的屬性:

如果你想知道更多關於網格位置屬性的使用方法,可以看看我們之前關於網格區域的文章。現在,讓我們看看視覺重排序是如何造成程式碼可訪問性的問題的。

視覺效果與邏輯的重排序

這是一個簡單的網格佈局,只有幾個簡單的連結,所以你可以使用鍵盤測試程式碼:

<div class="container">
    <div class="item-1"><a href="#">Link 1</a></div>
    <div class="item-2"><a href="#">Link 2</a></div>
    <div class="item-3"><a href="#">Link 3</a></div>
    <div class="item-4"><a href="#">Link 4</a></div>
    <div class="item-5"><a href="#">Link 5</a></div>
    <div class="item-6"><a href="#">Link 6</a></div>
</div>
複製程式碼

現在我們再加入一些樣式。下面的 CSS 程式碼將網格元素放入了三個寬度相同的列中。使用 grid-row 屬性,第一個元素被移動到了第二行的開始。

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 0.625rem;
}
 
.item-1 {
  grid-row: 2;
}
複製程式碼

在下面這個圖中,你可以看到最終的視覺效果,其中 Link 1 被加上了一些特殊樣式以便突出說明。普通的使用者將會首先看到 Link 2,但是使用螢幕閱讀器的使用者將會從 Link 1 開始,因為他們遵從的是 HTML 程式碼中定義的邏輯順序。

對於純鍵盤使用者,使用 tab 鍵瀏覽頁面也同樣困難,因為這樣依舊會從 Link 1 開始,也就是頁面的左下角(你可以自己嘗試一下)。

image

解決方案

解決方案非常簡單優雅。不要改變視覺順序,你只需要將 Link 1 移動到 HTML 檔案的下面。這樣,原始碼順序和視覺順序就一致了。

<div class="container">
  <div class="item-2"><a href="#">Link 2</a></div>
  <div class="item-3"><a href="#">Link 3</a></div>
  <div class="item-4"><a href="#">Link 4</a></div>
  <div class="item-1"><a href="#">Link 1</a></div>
  <div class="item-5"><a href="#">Link 5</a></div>
  <div class="item-6"><a href="#">Link 6</a></div>
</div>
複製程式碼

你不需要在 CSS 中為 .item-1 新增任何關於 Grid 的屬性。因為你也不用改變預設的原始碼順序了,那麼你只需要為網格容器定義屬性即可。

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 0.625rem;
}
複製程式碼

看,儘管這個例子最終結果和以前一樣,現在它的可訪問性更高了。使用 tab 或者螢幕閱讀器都會從 Link 2 開始,邏輯上也遵循原始碼順序。

image

如何讓佈局的可訪問性更好

這裡有幾個通用的佈局模版,你可以讓使用 CSS Grid 重排序屬性的程式碼可訪問性更高。例如,“聖盃佈局”就是這樣一種模式。它包括一個頭部,一個主要內容區域,一個頁尾,還有兩個固定寬度的側邊欄,它們倆一個在左一個在右。

左邊欄佈局可能會為使用螢幕閱讀器的使用者造成困惑。因為左邊欄在原始碼順序要要比主要內容區域靠前,而它則是使用螢幕閱讀器的使用者最先看到的內容。但是,通常情況下,使用螢幕閱讀器的使用者開始閱讀的位置最好是主要內容。特別是當左邊欄主要包括的其實是廣告,部落格目錄,標籤雲,或者其他一些不相關的內容。

CSS Grid 允許你改變 HTML 檔案的原始碼順序,並將主要內容放在兩個側邊欄前面

<div class="container">
    <header>Header</header>
    <main>Main content</main>
    <aside class="left-sidebar">Left sidebar</aside>
    <aside class="right-sidebar">Right sidebar</aside>
    <footer>Footer</footer>
</div>
複製程式碼

還有一些其他可用的解決方案,來使用 CSS Grid 定義視覺順序的改變。大部分教程都會使用命名的網格區域,並使用 grid-template-areas 屬性對它們進行重排列。

下面的程式碼是最簡單的解決方案,因為它只是為視覺順序和原始碼順序不同的元素新增了幾個額外的規則。CSS Grid 有優秀的自動排列功能,能夠把餘下的網格元素搞定。

.container {
  display: grid;
  grid-template-columns: 9.375rem 1fr 9.375rem;
  grid-gap: 0.625rem;
}
header, 
footer {
  grid-column: 1 / span 3;
}
.left-sidebar {
  grid-area: 2 / 1;
}
複製程式碼

這樣,grid-column<header> 和 <footer> 區域橫跨整個螢幕(三列),然後 grid-areagrid-row 和 grid-column 的簡寫)固定了左邊欄的位置。如下就是使用這些樣式後的樣子:

image

儘管聖盃佈局是一個相對簡單的佈局,你還可以使用相同的邏輯來完成一些更復雜的佈局。要始終牢記頁面的哪個部分是最重要的,哪部分是使用螢幕閱讀器的使用者在看到其他內容之前可能最想看的。

語義丟失怎麼辦

某些情況下,CSS Grid 也會對語義造成破壞;這也是影響可訪問性的一個方面。由於 display: grid; 佈局僅被元素的直接子元素繼承,網格元素的子元素其實就不是網格佈局的一部分了。為了節省工作量,開發者也許認為將佈局扁平化是一個不錯的解決方案,所以他們就將所有希望包括在網格佈局內的元素都作為網格容器的直接子元素。但是,如果一個佈局被認為的扁平化了,檔案的語義通常也就丟失了。

加入你想要建立一個元素展覽牆(比如圖片牆),在這裡,元素按照網格排列並被一個頭部和一個頁尾包圍。如下是帶語義的標籤寫法:

<section class="container">
    <header>Header</header>
    <ul>
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
        <li>Item 4</li>
        <li>Item 5</li>
        <li>Item 6</li>
    </ul>
    <footer>Footer</footer>
</section>
複製程式碼

但是如果你想要使用 CSS Grid,<section> 應該作為網格容器,<h1><h2><ul> 是網格元素。但是,列表內的元素不被包括在網格內,因為他們是網格容器子元素的子元素

所以,如果你想要快速的完成工作,將佈局結構扁平化也許是一個不錯的主意,也就是讓所有的元素都作為網格容器的子元素:

<section class="container">
    <header>Header</header>
    <div class="item">Item 1</div>
    <div class="item">Item 2</div>
    <div class="item">Item 3</div>
    <div class="item">Item 4</div>
    <div class="item">Item 5</div>
    <div class="item">Item 6</div>
    <footer>Footer</footer>
</section>
複製程式碼

現在,你就可以很輕鬆地使用 CSS Grid 建立出想要的佈局:

.container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-gap: 0.625rem;
}
header,
footer {
    grid-column: 1 / span 3;
}
複製程式碼

image

一切看上去都非常好,但是文件已經丟失了它最初的語義,所以:

  • 使用螢幕閱讀器的使用者無法知道元素之間的關係,也無法知道它們其實是列表的一部分(大部分的螢幕閱讀器都會通知使用者列表元素的數量);
  • 被破壞的語義也會讓搜尋引擎很難明白你的內容;
  • 如果使用者在禁用 CSS 的時候訪問你的內容(例如,網速不佳的時候),在瀏覽頁面時可能會很困惑,因為他們只看到一系列不相關的 div。

最重要的規則是,你絕對不能為了看上去好看而放棄語義。

解決方案

目前的解決方案通過為未排序的列表新增了 CSS 規則,建立出了巢狀的網格。

.container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-gap: 0.625rem;
}
.container > * {
    grid-column: 1 / span 3;
}
ul {
    display: inherit;
    grid-template-columns: inherit;
    grid-gap: inherit;
}
複製程式碼

在如下例子中,你可以看到巢狀的網格和父級網格是如何關聯的。元素按照期望的樣子排列出來了,但是此時,文件始終保留著它的語義。

image

總結

簡單的 CSS Grid 佈局可能不會導致可訪問性的問題。但是當你想要改變視覺順序或者建立多層網格的時候,問題就可能暴露出來。解決這些問題通常不會很麻煩,所以這樣做來修復那些可訪問性問題是很值得的,因為這樣你能夠讓那些使用輔助工具的使用者更易讀懂你的內容。

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章