本教學課程說明如何建立方便存取的網站主要導覽功能。您會瞭解語意式 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 個項目」。
以下是搭配螢幕閱讀器 NVDA 在 Windows 上使用的導覽操作示範。
現在,您必須調整樣式,使樣式看起來像過去一樣。
/* 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 個,共 4 個」)。
更重要的是,如果網頁呈現沒有 CSS,該清單將以連貫方式呈現這些連結,而不只是一堆連結。
關於 Safari 中 VoiceOver 的一大亮點,是設定 list-style: none
後,您將失去所有這些優勢。這是設計所致。WebKit 團隊決定移除清單看起來不像清單的語意。視導覽的複雜度而定,這不一定有問題。在此情況下,導覽仍可使用,只會影響 Safari 中的 VoiceOver。VoiceOver 搭配 Chrome 或 Firefox 仍會朗讀項目數量,以及其他螢幕閱讀器 (例如 NVDA)。另一方面,語意資訊在某些情況下非常實用。為了做出決定,建議您邀請螢幕閱讀器的實際使用者測試導覽功能,並取得他們的意見回饋。如果您決定讓 Safari 中的 VoiceOver 運作方式與其他螢幕閱讀器相同,可以在 <ul>
上明確設定 ARIA 清單角色,即可解決問題。這麼做會將行為還原為移除清單樣式前的狀態。無論如何,清單看起來都一樣。
<ul role="list">
<li>
<a href="/home">Home</a>
</li>
...
</ul>
新增地標
你為螢幕閱讀器使用者帶來了大幅度的改進,但除此之外,你還有一項功能可以幫上忙。導覽在語意上仍只是連結清單,因此很難判斷這份清單就是網站的主要導覽。您可以將這個一般清單轉換為導覽清單,將 <ul>
納入 <nav>
元素中。
使用 <nav>
元素有許多優點。值得注意的是,螢幕閱讀器會朗讀「導覽」這類東西,藉此在使用者進行互動時,系統會在網頁中加上地標。「地標」是指頁面中的特殊區域,例如 <header>
、<footer>
或 <main>
,可供螢幕閱讀器跳轉。在網頁上加入標記相當實用,因為螢幕閱讀器使用者無需與網頁上的其他部分互動,就能直接存取網頁上的重要區域。舉例來說,按下 NVDA 中的 D 鍵,即可從地標切換至地標。在 Voice Over 中按下 VO + U 時,你可以使用轉輪列出頁面上的所有地標。
其中您會看到 4 個地標:橫幅是 <header>
元素,navigation 是 <nav>
,主要 <main>
元素,內容資訊則是 <footer>
。這份清單不應過長,您只需將使用者介面的重要部分標示為地標,例如網站搜尋、區域導覽或分頁。
如果你提供了單一網頁的本機導覽、該網頁的本機導覽,以及在單一頁面上建立分頁,也可能有 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」或「選單」因為螢幕閱讀器已經為使用者提供這項資訊
在狹窄的可視區域中隱藏導覽面板
我個人特色是,不擅長將主導覽列隱藏在狹窄的可視區域上,但要是連結清單過長,就沒辦法了。在這個情況下,使用者看到的會是「選單」按鈕,而不是清單按鈕或漢堡圖示或兩者兼具按一下按鈕即可顯示/隱藏清單。如果你具備基本的 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
為按鈕提供一個所謂的無障礙名稱,是漢堡圖示的替代文字。- 你可以使用
aria-hidden
將<svg>
從輔助技術中隱藏,因為該模組已經有aria-label
提供的文字標籤。 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
等其他屬性結合,產生滑動或淡入效果。這不適用於「display」:無,因為顯示屬性不可為假。
下列 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 角色,足以證明這是導覽元素,但網站通常也會使用選單、選單列和選單項目。由於我們有時會交替使用這些字詞,建議您結合兩者,改善螢幕閱讀器的使用體驗。在瞭解原因前,我們先來看看這些角色的正式定義。
導覽角色
一組導覽「元素」 (通常為連結),可用於瀏覽文件或相關文件。
navigation (角色) WAI-ARIA 1.1
選單角色
選單通常包含使用者可叫用的常用動作或功能清單。如果選單項目清單的顯示方式與電腦版應用程式相似,就適合使用選單角色。
選單 (角色) WAI-ARIA 1.1
選單列角色
選單呈現的畫面,內容通常持續顯示,且通常是橫向顯示。 選單列角色可建立與 Windows、Mac 和 Gnome 桌面應用程式類似的選單列。選單列是用來建立一組一致的常用指令。作者應確保選單列互動方式與電腦圖形使用者介面中的一般選單列互動相似。
選單列 (角色) WAI-ARIA 1.1
選單項目角色
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 提供。
- Adrian Roselli 提供的不會將 ARIA 選單角色用於網站導覽。
- 選單和Heydon Pickering 的選單按鈕
- WAI-ARIA 菜單,以及為何你該用 Marco Zehe 謹慎處理這些菜單。
- Kitty Giraudel 以負責任的方式隱藏內容。
- :focus-visible Is Here (Matthias Ott)。
主頁橫幅由 Mick Haupt 提供