本教學課程說明如何建構可供使用者存取的網站主要導覽。您將會學到語意 HTML、無障礙功能,以及使用 ARIA 屬性有時能帶來更大的傷害。
有許多不同的方法可以建構網站的主要導覽,例如樣式、功能、基礎標記和語意資訊。如果導入方式太精簡,雖然適合大多數使用者,但使用者體驗 (UX) 可能不夠好。 設計不當時,可能會讓使用者感到困惑,甚至完全妨礙使用者存取。
對於大多數網站,您需要建立既簡單又複雜也要的網站。
各圖層的建築物圖層
在本教學課程中,您將從基本設定開始,然後再疊加一個位置,以便為大多數使用者提供充足的資訊、樣式和功能。為了達到您採用漸進式增強原則,您會先從最基本可靠的解決方案著手,然後逐步加入多層功能。如果某個圖層因故無法運作,導覽功能仍可正常運作,以便順利改回底層。
基本結構
在基本導覽中,您需要具備兩個項目:<a>
元素和幾行 CSS,改善連結的預設樣式和版面配置。
<a href="/home">Home</a>
<a href="/about-us">About us</a>
<a href="/pricing">Pricing</a>
<a href="/contact">Contact</a>
/* Define variables for your colors */
:root {
--color-shades-dark: rgb(25, 25, 25);
}
/* Use the alternative box model
Details: <https://web.dev/learn/css/box-model/> */
*{
box-sizing: border-box;
}
/* Basic font styling */
body {
font-family: Segoe UI, system-ui, -apple-system, sans-serif;
font-size: 1.6rem;
}
/* Link styling */
a {
--text-color: var(--color-shades-dark);
border-block-end: 3px solid var(--border-color, transparent);
color: var(--text-color);
display: inline-block;
margin-block-end: 0.5rem; /* See note at the bottom of this chapter */
margin-inline-end: 0.5rem;
padding: 0.1rem;
text-decoration: none;
}
/* Change the border-color on :hover and :focus */
a:where(:hover, :focus) {
--border-color: var(--text-color);
}
這種做法適用於大多數使用者,無論透過何種方式存取網站。使用者可以透過滑鼠、鍵盤、觸控裝置或螢幕閱讀器進行瀏覽,但也有改進空間。您只要提供額外的功能和資訊,就能擴充這個基本模式,讓使用者享有更優質的體驗。
您可以採取下列步驟:
- 醒目顯示使用中的頁面。
- 向螢幕閱讀器使用者朗讀項目數量。
- 新增地標,讓螢幕閱讀器使用者直接透過捷徑存取導覽功能。
- 在狹窄的可視區域中隱藏導覽功能。
- 改善聚焦樣式。
醒目顯示使用中的頁面
如要醒目顯示有效頁面,請在對應的連結中新增課程。
<a href="/about-us" class="active-page">About us</a>
這種方法的問題在於,它可以單純以視覺化的方式傳達使用中的連結資訊,失明的螢幕閱讀器使用者無法分辨有效網頁和其他網頁。幸運的是,可存取的豐富網際網路應用程式 (ARIA) 標準也能以語意方式傳達資訊。請使用 aria-current="page" 屬性和值,不要使用類別。
aria-current
(狀態) 表示在容器或一組相關元素中代表目前項目的元素。網頁符記,用來表示一組分頁連結中的連結,其中連結會以視覺化方式呈現,呈現目前顯示的網頁。
[無障礙豐富網際網路應用程式 (WAI-ARIA) 1.1](https://www.w3.org/TR/wai-aria/#aria-current)
有了額外屬性,螢幕閱讀器現在會朗讀「目前網頁、連結、關於我們」之類的內容,而非只有「連結」、「關於我們」的內容。
<a href="/about-us" aria-current="page" class="active-page">About us</a>
另一個便利的副作用是,您可以使用這項屬性在 CSS 中選取有效連結,讓 active-page
類別過時。
<a href="/home">Home</a>
<a href="/about-us" aria-current="page">About us</a>
<a href="/pricing">Pricing</a>
<a href="/contact">Contact</a>
/* Change border-color and color for the active page */
[aria-current="page"] {
--border-color: var(--color-highlight);
--text-color: var(--color-highlight);
}
朗讀項目數量
透過瀏覽導覽,視障者可以看出該頁面只包含四個連結。失明的螢幕閱讀器使用者無法迅速取得這項資訊。他們可能需要處理整個連結清單。如果這份清單太簡短,這可能不是問題,但如果清單內含 40 個連結,這項工作可能就會相當麻煩。如果螢幕閱讀器使用者一開始就知道導覽內容含有大量連結,他們可能會決定改用其他更有效率的瀏覽方式 (例如站內搜尋)。
預先傳達項目數量的好方法,就是將每個連結納入清單項目 (<li>
),巢狀結構中未排序的清單 (<ul>
)。
<ul>
<li>
<a href="/home">Home</a>
</li>
<li>
<a href="/about-us" aria-current="page">About us</a>
</li>
<li>
<a href="/pricing">Pricing</a>
</li>
<li>
<a href="/contact">Contact</a>
</li>
</ul>
當螢幕閱讀器使用者找到這份清單時,他們的軟體會說出「清單,4 個項目」之類的指令。
以下示範在 Windows 上透過螢幕閱讀器 NVDA 使用導覽的方式。
現在,您必須調整樣式,讓樣式看起來像之前一樣。
/* Remove the default list styling and create a flexible layout for the list */
ul {
display: flex;
flex-wrap: wrap;
gap: 1rem;
list-style: none;
margin: 0;
padding: 0;
}
/* Basic link styling */
a {
--text-color: var(--color-shades-dark);
border-block-end: 3px solid var(--border-color, transparent);
color: var(--text-color);
padding: 0.1rem;
text-decoration: none;
}
使用清單可為螢幕閱讀器使用者帶來許多好處:
- 他們可以在與項目互動之前取得項目總數。
- 他們可以使用快速鍵從清單項目跳至清單項目。
- 他們可以使用快速鍵從清單跳至清單。
- 螢幕閱讀器可能會朗讀目前項目的索引 (例如「清單項目,第 2 個,共四個」)。
除此之外,如果網頁沒有透過 CSS 顯示,該清單會以連貫的項目群組顯示連結,而非一堆連結。
關於 Safari 中的 VoiceOver 有一項值得注意之處,那就是設定 list-style: none
後,就無法使用這些功能。這是設計所致。WebKit 團隊決定在清單看起來不像清單時,移除清單語意。視瀏覽的複雜度而定,這不一定是問題。一方面,導覽功能仍可用,只有 Safari 中的 VoiceOver 有作用。透過 Chrome 或 Firefox 使用 VoiceOver 時,仍會讀出項目數量,以及 NVDA 等其他螢幕閱讀器。另一方面,語意資訊在某些情況下非常實用。為了做出決定,建議您邀請螢幕閱讀器使用者測試導覽功能,並取得他們的意見回饋。如果您決定讓 Safari 的 VoiceOver 行為與其他螢幕閱讀器相同,可以在 <ul>
上明確設定 ARIA 清單角色來解決這個問題。這樣會在移除清單樣式前,將行為還原為狀態。看來,清單看起來仍然相同。
<ul role="list">
<li>
<a href="/home">Home</a>
</li>
...
</ul>
新增地標
您只要多花一點點功夫,就能提升螢幕閱讀器使用者的體驗,但使用者還有一項方法可以改進。在語意上,我們只在語意上提供連結清單,很難判斷這份清單是網站的主要導覽。只要在 <nav>
元素中納入 <ul>
,即可將此一般清單轉換為導覽清單。
使用 <nav>
元素有幾個優點。值得一提的是,螢幕閱讀器會在使用者與這項功能互動時朗讀「導覽」之類的內容,並在頁面上新增「地標」。地標是指頁面上的特殊區域,例如 <header>
、<footer>
或 <main>
,螢幕閱讀器可跳到這些區域。在網頁上加入地標相當實用,因為這可讓螢幕閱讀器使用者直接存取網頁上的重要區域,而無須與網頁的其他部分互動。舉例來說,按下 NVDA 中的 D 鍵,就能從地標跳到地標。在 Voice Over 中,按下 VO + U 鍵即可使用轉輪列出頁面上的所有地標。
這份清單會顯示 4 個位置標記:「橫幅」是 <header>
元素,「導覽」是 <nav>
、「主要」是 <main>
元素,「內容資訊」則是 <footer>
。這份清單內容不應過長,您只想將 UI 的重要部分標示為地標,例如站內搜尋、本機導覽或分頁。
如果為整個網站的導覽功能、網頁的本機導覽功能,以及單一頁面設有分頁,那麼也可能會有 3 個 <nav>
元素。沒問題,但現在有三個導覽界標,而且這些地標在語意上都一樣。除非熟悉網頁的結構,否則很難區別兩者。
如要區別這些事件,請使用 aria-labelledby
或 aria-label
加上標籤。
<nav aria-label="Main">
<ul>
<li>
<a href="/home">Home</a>
</li>
...
</ul>
</nav>
...
<nav aria-label="Select page">
<ul>
<li>
<a href="/page-1">1</a>
</li>
...
</ul>
</nav>
如果選取的標籤已存在於頁面中的某處,您可以改用 aria-labelledby
,並利用 id
屬性參照現有的標籤。
<nav aria-labelledby="pagination_heading">
<h2 id="pagination_heading">Select a page</h2>
<ul>
<li>
<a href="/page-1">1</a>
</li>
...
</ul>
</nav>
簡單扼要的標籤夠清楚,不要太冗長。請省略「navigation」或「menu」等運算式,因為螢幕閱讀器已為使用者提供這項資訊。
在狹窄的可視區域中隱藏導覽功能
我個人不喜歡在狹窄的可視區域中隱藏主要導覽功能,但如果連結清單太久,就無法瀏覽。在這種情況下,使用者會看到標示「菜單」、漢堡圖示或圖示組合的按鈕,而不是清單。按一下按鈕即可顯示並隱藏清單。如果您知道基本的 JavaScript 和 CSS,這是一項可處理的工作,但就使用者體驗和無障礙方面而言,有幾項必須解決。
- 您必須以方便的方式隱藏清單。
- 瀏覽時必須要能用鍵盤操作。
- 導覽機制必須讓使用者知道是否可看見。
新增漢堡按鈕
由於您採取漸進式增強原則,因此最好確保在停用 JavaScript 的情況下,導覽功能仍可正常運作且沒有意義。
首先要瀏覽的是漢堡按鈕。請在範本元素中的 HTML 中建立、在 JavaScript 中複製,然後新增至導覽面板。
<nav id="mainnav">
...
</nav>
<template id="burger-template">
<button type="button" aria-expanded="false" aria-label="Menu" aria-controls="mainnav">
<svg width="24" height="24" aria-hidden="true">
<path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z">
</svg>
</button>
</template>
aria-expanded
屬性會告知螢幕閱讀器軟體,判斷按鈕控制項是否展開的元素。aria-label
會為按鈕提供所謂的無障礙名稱,而這是漢堡圖示的替代文字。- 「
<svg>
」已有「aria-label
」提供的文字標籤,因此已使用aria-hidden
從輔助技術中隱藏。 aria-controls
會向輔助技術提供輔助技術,支援屬性 (例如 JAWS)。
const nav = document.querySelector('#mainnav')
const list = nav.querySelector('ul');
const burgerClone = document.querySelector('#burger-template').content.cloneNode(true);
const button = burgerClone.querySelector('button');
// Toggle aria-expanded attribute
button.addEventListener('click', e => {
// aria-expanded="true" signals that the menu is currently open
const isOpen = button.getAttribute('aria-expanded') === "true"
button.setAttribute('aria-expanded', !isOpen);
});
// Hide list on keydown Escape
nav.addEventListener('keyup', e => {
if (e.code === 'Escape') {
button.setAttribute('aria-expanded', false);
}
});
// Add the button to the page
nav.insertBefore(burgerClone, list);
- 使用者可以視需要隨時關閉導覽功能 (例如按下 Escape 鍵)。
- 請務必使用
insertBefore
而非appendChild
,因為按鈕應為導覽中的第一個元素。如果鍵盤或螢幕閱讀器使用者在點選按鈕後按下 Tab,會聚焦於清單中的第一個項目。要是按鈕位於清單後面,就不是如此。
接下來,您可以重設按鈕的預設樣式,確保按鈕只能在狹窄的可視區域中顯示。
@media (min-width: 48em) {
nav {
--nav-button-display: none;
}
}
/* Reset button styling */
button {
all: unset;
display: var(--nav-button-display, flex);
}
隱藏清單
隱藏清單之前,請先調整導覽和清單的位置和樣式,以便針對較小的可視區域調整版面配置,但在較大的螢幕上仍能呈現最佳效果。
首先,將 <nav>
從網頁的自然流程中移除,並放在可視區域的頂端角落。
@media (min-width: 48em) {
nav {
--nav-button-display: none;
--nav-position: static;
}
}
nav {
position: var(--nav-position, fixed);
inset-block-start: 1rem;
inset-inline-end: 1rem;
}
接著,請加入新的自訂屬性 (—-nav-list-layout)
,變更小可視區域的版面配置。根據預設,版面配置為欄,在大型螢幕上會切換為列。
@media (min-width: 48em) {
nav {
--nav-button-display: none;
--nav-position: static;
}
ul {
--nav-list-layout: row;
}
}
ul {
display: flex;
flex-direction: var(--nav-list-layout, column);
flex-wrap: wrap;
gap: 1rem;
list-style: none;
margin: 0;
padding: 0;
}
在狹窄的可視區域中,導覽看起來應該會像這樣。
這份清單顯然需要一些 CSS。我們將其移到頂端角落並垂直填滿整個螢幕,並套用 background-color
和 box-shadow
。
@media (min-width: 48em) {
nav {
--nav-button-display: none;
--nav-position: static;
}
ul {
--nav-list-layout: row;
--nav-list-position: static;
--nav-list-padding: 0;
--nav-list-height: auto;
--nav-list-width: 100%;
--nav-list-shadow: none;
}
}
ul {
background: rgb(255, 255, 255);
box-shadow: var(--nav-list-shadow, -5px 0 11px 0 rgb(0 0 0 / 0.2));
display: flex;
flex-direction: var(--nav-list-layout, column);
flex-wrap: wrap;
gap: 1rem;
height: var(--nav-list-height, 100vh);
list-style: none;
margin: 0;
padding: var(--nav-list-padding, 2rem);
position: var(--nav-list-position, fixed);
inset-block-start: 0; /* Logical property. Equivalent to top: 0; */
inset-inline-end: 0; /* Logical property. Equivalent to right: 0; */
width: var(--nav-list-width, min(22rem, 100vw));
}
button {
all: unset;
display: var(--nav-button-display, flex);
position: relative;
z-index: 1;
}
在狹窄的可視區域中,清單看起來應該會像這樣,比簡單的清單更像側欄。
最後,隱藏清單,只在使用者點選按鈕一次時顯示,在再次點選時隱藏。請務必只隱藏清單,不要隱藏整個導覽項目,因為隱藏導覽連結也會隱藏重要的標記。
您先前已在按鈕中加入點擊事件,用來切換 aria-expanded
屬性的值。你可以將這些資訊設為在 CSS 中顯示及隱藏清單的條件。
@media (min-width: 48em) {
ul {
--nav-list-visibility: visible;
}
}
ul {
visibility: var(--nav-list-visibility, visible);
}
/* Hide the list on narrow viewports, if it comes after an element with
aria-expanded set to "false". */
[aria-expanded="false"] + ul {
visibility: var(--nav-list-visibility, hidden);
}
如要隱藏清單,請務必使用 visibility: hidden
或 display: none
之類的屬性宣告,不要使用 opacity: 0
或 translateX(100%)
。這些屬性可確保導覽隱藏時,使用者無法聚焦這些連結。使用 opacity
或 translate
會移除內容,因此使用者仍然可以透過鍵盤存取連結,而可能造成混淆和困擾。使用 visibility
或 display
會隱藏這些內容,導致無法存取,因此所有使用者都無法看到。
建立清單動畫
如果您想知道為何使用 visibility: hidden;
超過 display: none;
,這是因為您可以使用動畫顯示顯示設定。它只有 hidden
和 visible
兩種狀態,但您可以將其與 transform
或 opacity
等其他屬性結合,藉此建立滑動或淡入效果。不適用於多媒體廣告:無,因為多媒體廣告屬性無法製作。
下列 CSS 轉換 opacity
,可建立淡入和淡出效果。
ul {
transition: opacity 0.6s linear, visibility 0.3s linear;
visibility: var(--nav-list-visibility, visible);
}
[aria-expanded="false"] + ul {
opacity: 0;
visibility: var(--nav-list-visibility, hidden);
}
如果想改為建立動畫效果,建議您將 transition
屬性納入「優先減少動作」媒體查詢中,因為動畫可能會觸發部分使用者的噁心、暈眩和頭痛。
ul {
visibility: var(--nav-list-visibility, visible);
}
@media (prefers-reduced-motion: no-preference) {
ul {
transition: transform 0.6s cubic-bezier(.68,-0.55,.27,1.55), visibility 0.3s linear;
}
}
[aria-expanded="false"] + ul {
transform: var(--nav-list-transform, translateX(100%));
visibility: var(--nav-list-visibility, hidden);
}
確保只有對低動態效果選擇不喜歡的使用者,才能看見動畫。
改善焦點樣式設定
鍵盤使用者會根據元素的焦點樣式設定網頁方向和導覽。預設焦點樣式優於無焦點樣式 (設定 outline: none
後會發生此情況),但提供更顯眼的自訂焦點樣式可改善使用者體驗。
在 Chrome 103 中,連結的預設焦點樣式如下。
您可以為自己的顏色提供專屬顏色,改善這一點。只要使用 :focus-visible
(而非 :focus
),瀏覽器就能決定何時適合顯示焦點樣式。所有使用者、滑鼠、鍵盤和觸控使用者均可看到 :focus
樣式,不論使用者是否有需要。透過 :focus-visible
,瀏覽器會使用內部經驗法則,決定要只向鍵盤使用者或所有人顯示內容。
/* Remove the default :focus outline */
*:focus {
outline: none;
}
/* Show a custom outline on :focus-visible */
*:focus-visible {
outline: 2px solid var(--color-shades-dark);
outline-offset: 4px;
}
瀏覽器支援 :focus-visible
系統會以不同方式醒目顯示焦點項目。建議您使用 outline
屬性,因為該屬性不會破壞版面配置,使用 border
時可能會發生這種狀況,而且適用於 Windows 高對比模式。成效不佳的屬性包括 background-color
或 box-shadow
,因為可能完全無法透過自訂對比設定顯示。
恭喜!您打造了漸進式強化、語意豐富、方便存取且適合行動裝置的主要導覽功能。
你可以隨時改善這項功能,例如:
- 可以考慮在導覽列中將焦點移開,或在範圍較小的可視區域上「中斷」網頁的其他部分。
- 您可以在頁面頂端新增「略過連結」,讓鍵盤使用者略過導覽。
如果你還記得本文的起源,希望解決方案應「不能變得太簡單或太複雜」,此時就可以派上用場。但您也可以過度工程瀏覽。
導覽與選單
導覽和選單有明顯差異。導覽是一組導覽連結,可用於瀏覽相關文件。選單是用來在文件中執行的動作集合。這些任務有時會重疊。您可能有一個導覽項目包含可執行動作的按鈕 (例如開啟互動視窗),或是有一個選單,將使用者帶到另一個頁面,例如說明頁面。在這種情況下,請不要混合 ARIA 角色,但請指明元件的主要用途,然後據此挑選標記和角色。
<nav>
元素具有隱含的 ARIA 角色,足以傳達該元素是導覽元素,但網站通常會使用選單、選單列和選單項目。由於我們有時會交替使用這些字詞,因此不妨將這些詞彙合併,藉此改善螢幕閱讀器的使用體驗。但在瞭解原因前,我們先來看看這些角色的正式定義。
導覽角色
導覽 (角色) WAI-ARIA 1.1
選單角色
選單通常是使用者可以叫用的常見動作或功能清單。如果選單項目清單的呈現方式與電腦應用程式中的選單相似,則適合使用選單 role。
選單 (角色) WAI-ARIA 1.1
選單列角色
選單呈現,通常保持顯示,且通常會以水平方式呈現。 選單列角色可用來建立與 Windows、Mac 和 Gnome 桌面應用程式中類似的選單列。選單列是用來建立一組一致的常用指令。作者應確保選單列的互動與電腦圖形使用者介面中的一般選單列互動類似。
選單列 (角色) WAI-ARIA 1.1
menuitem 角色
menuitem (角色) WAI-ARIA 1.1
這裡的規格非常清晰,僅針對與電腦版應用程式中的選單類似的動作或功能清單,使用導覽功能瀏覽文件或相關文件和選單。如果您不需建立下一項 Google 文件,則可能不需要主要導覽選單的任何選單角色。
何時適合使用菜單?
選單項目的主要用途不是瀏覽,而是執行動作。假設你有一份資料清單或表格,且使用者可對清單中的每個項目執行特定動作。您可以在每一列中加入按鈕,並在使用者點選按鈕時顯示動作。
<ul>
<li>
Product 1
<button aria-expanded="false" aria-controls="options1">Edit</button>
<div role="menu" id="options1">
<button role="menuitem">
Duplicate
</button>
<button role="menuitem">
Delete
</button>
<button role="menuitem">
Disable
</button>
</div>
</li>
<li>
Product 2
...
</li>
</ul>
使用選單角色的影響
妥善使用這些選單角色非常重要,因為操作可能會出錯。
選單需要特定的 DOM 結構。menuitem
必須是 menu
的直接子項。下列程式碼可能會破壞語意行為:
<!-- Wrong, don't do this -->
<ul role="menu">
<li>
<a href="#" role="menuitem">Item 1</a>
</li>
</ul>
精通的使用者會希望特定鍵盤快速鍵能與選單和選單列搭配運作。根據 ARIA 編寫實務指南 (APG),包括:
- 按下 Enter 鍵和空格鍵即可選取選單項目。
- 朝所有方向方向鍵操作,可在項目之間移動。
- 使用「Home」鍵和「End」鍵,可將焦點分別移至第一個或最後一個項目。
- a-z 即可將焦點移至下一個選單項目,該標籤以所輸入的字元開頭。
- 按下 Esc 鍵可關閉選單。
如果螢幕閱讀器偵測到選單,軟體可能會自動變更瀏覽模式,允許使用上述快速鍵。不瞭解螢幕閱讀器的使用者因不熟悉這類快速鍵或使用方式,所以可能無法使用選單。
對鍵盤使用者來說,使用 Shift 和 Shift + Tab 鍵時也是如此。
建立選單和選單列時,有很多層面需要考量,在開頭判斷是否適合使用這些選單和列。建構一般網站時,您只需要使用包含清單和連結的導覽元素即可。這也包括單頁應用程式 (SPA) 或網頁應用程式。基礎堆疊並不重要。除非您要建構與桌面應用程式非常接近的應用程式,否則請避免使用選單角色。
其他資源
- 修正清單:Scott O'hara。
- 不要在網站導覽中使用 ARIA 選單角色,由 Adrian Roselli 提供。
- Heydon Pickering 提供的選單和選單按鈕。
- WAI-ARIA 選單,以及你為何應謹慎使用選單 (Marco Zehe)。
- Kitty Giraudel 以負責任的方式隱藏內容。
- :focus-visible Is Here by Matthias Ott.
主頁橫幅由 Mick Haupt 提供