基礎概念總覽:如何建構回應式無障礙麵包屑元件,供使用者瀏覽網站。
在這篇文章中,我想分享如何建構麵包屑元件的思考過程。 立即試用。
如果比較喜歡看影片,可以觀看這篇貼文的 YouTube 版本:
總覽
導覽標記元件會顯示使用者在網站階層中的位置。這個名稱源自糖果屋的故事,漢賽爾和葛麗特在黑暗的森林中留下導覽標記,並循著標記找到回家的路。
這篇文章中的麵包屑並非標準麵包屑,而是類似麵包屑的項目。只要在導覽中加入 <select>,即可將同層級的網頁直接放入導覽中,提供額外功能,實現多層級存取。
背景使用者體驗
在上方元件的示範影片中,預留位置類別是電玩遊戲類型。如要建立這項路徑,請按照下圖所示前往 home »
rpg » indie » on sale。
這個麵包屑元件應可讓使用者在資訊階層中移動,快速準確地跳轉分支並選取頁面。
資訊架構
我認為以集合和項目為單位思考會很有幫助。
集合
集合是可供選擇的選項陣列。從這篇文章的麵包屑原型首頁開始,收藏包括第一人稱射擊、角色扮演、格鬥、地城探索、運動和益智。
項目
電玩遊戲是項目,如果特定收藏代表另一個收藏,也可以是項目。舉例來說,RPG 是項目,也是有效的集合。如果是項目,使用者會位於該集合頁面。舉例來說,他們位於角色扮演遊戲頁面,該頁面會顯示角色扮演遊戲清單,包括 AAA、獨立和自行發布等額外子類別。
以電腦科學術語來說,這個麵包屑元件代表多維陣列:
const rawBreadcrumbData = {
"FPS": {...},
"RPG": {
"AAA": {...},
"indie": {
"new": {...},
"on sale": {...},
"under 5": {...},
},
"self published": {...},
},
"brawler": {...},
"dungeon crawler": {...},
"sports": {...},
"puzzle": {...},
}
您的應用程式或網站會有自訂資訊架構 (IA),建立不同的多維陣列,但希望集合到達網頁和階層遍歷的概念也能納入您的麵包屑。
版面配置
標記
合適的 HTML 是製作優質元件的基礎。在下一節中,我將說明標記選擇,以及這些選擇對整體元件的影響。
深色和淺色配置
<meta name="color-scheme" content="dark light">
上述程式碼片段中的 color-scheme 中繼標記會通知瀏覽器,這個網頁需要淺色和深色瀏覽器樣式。範例麵包屑不含任何這些色彩配置的 CSS,因此麵包屑會使用瀏覽器提供的預設顏色。
導覽元素
<nav class="breadcrumbs" role="navigation"></nav>
網站導覽適合使用 <nav> 元素,這個元素具有隱含的 ARIA 導覽角色。在測試中,我發現 role 屬性會改變螢幕閱讀器與元素的互動方式,實際上會將元素宣告為導覽,因此我選擇新增這個屬性。
圖示
如果網頁上重複出現某個圖示,SVG <use> 元素表示您可以定義一次 path,並將其用於所有圖示例項。這樣可避免重複路徑資訊,導致文件變大,以及路徑不一致的潛在問題。
如要使用這項技術,請在網頁中新增隱藏的可擴充向量圖形元素,並將圖示包裝在具有專屬 ID 的 <symbol> 元素中:
<svg style="display: none;">
<symbol id="icon-home">
<title>A home icon</title>
<path d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
</symbol>
<symbol id="icon-dropdown-arrow">
<title>A down arrow</title>
<path d="M19 9l-7 7-7-7"/>
</symbol>
</svg>
瀏覽器會讀取 SVG HTML,將圖示資訊放入記憶體,並繼續處理網頁的其餘部分,參考 ID 以額外使用圖示,例如:
<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
<use href="#icon-home" />
</svg>
<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
<use href="#icon-dropdown-arrow" />
</svg>

定義一次,即可多次使用,對網頁效能的影響極小,且樣式彈性十足。請注意,系統已將 aria-hidden="true" 新增至 SVG 元素。
如果使用者只聽內容,這些圖示就沒有用處,因此對這類使用者隱藏圖示,可避免加入不必要的干擾。
分段連結 .crumb
傳統麵包屑與這個元件中的麵包屑在此分道揚鑣。
通常這只會是 <a> 連結,但我已新增遍歷 UX,並偽裝成選取畫面。.crumb 類別負責配置連結和圖示,而 .crumbicon 則負責將圖示和選取元素堆疊在一起。我將其稱為「分割連結」,因為其功能與分割按鈕非常相似,但用於網頁導覽。
<span class="crumb">
<a href="#sub-collection-b">Category B</a>
<span class="crumbicon">
<svg>...</svg>
<select class="disguised-select" title="Navigate to another category">
<option>Category A</option>
<option selected>Category B</option>
<option>Category C</option>
</select>
</span>
</span>
連結和一些選項沒什麼特別,但可為簡單的麵包屑增添更多功能。在 <select> 元素中新增 title,有助於螢幕閱讀器使用者瞭解按鈕的動作。不過,這項功能也能為其他人提供同樣的協助,因此在 iPad 上會顯示在最顯眼的位置。一個屬性可為多位使用者提供按鈕背景資訊。

分隔符裝飾
<span class="crumb-separator" aria-hidden="true">→</span>
分隔線為選用項目,只加一條也很適合 (請參閱上方影片中的第三個範例)。接著,我為每個 aria-hidden="true" 提供,因為這些圖示僅供點綴,螢幕閱讀器不需要朗讀。
接下來要介紹的 gap 屬性可直接設定這些間距。
樣式
由於顏色使用系統顏色,因此大部分都是樣式的間距和堆疊!
版面配置方向和流程

主要導覽元素 nav.breadcrumbs 會為子項設定範圍內的自訂屬性,供子項使用,否則會建立水平垂直對齊的版面配置。確保麵包屑、分隔線和圖示對齊。
.breadcrumbs {
--nav-gap: 2ch;
display: flex;
align-items: center;
gap: var(--nav-gap);
padding: calc(var(--nav-gap) / 2);
}

每個 .crumb 也會建立水平垂直對齊的版面配置,並留出一些間距,但會特別指定連結子項,並指定 white-space: nowrap 樣式。這對多字詞的麵包屑至關重要,因為我們不希望麵包屑換行。我們會在本文稍後部分新增樣式,處理這個 white-space 屬性造成的水平溢位。
.crumb {
display: inline-flex;
align-items: center;
gap: calc(var(--nav-gap) / 4);
& > a {
white-space: nowrap;
&[aria-current="page"] {
font-weight: bold;
}
}
}
系統會新增「aria-current="page"」,讓目前網頁的連結從其他連結中脫穎而出。螢幕閱讀器使用者不僅能清楚瞭解連結是指向目前網頁,我們也為元素設定視覺樣式,讓有視力的使用者獲得類似的使用者體驗。
.crumbicon 元件會使用格線,將 SVG 圖示與「幾乎隱形」的 <select> 元素堆疊在一起。

.crumbicon {
--crumbicon-size: 3ch;
display: grid;
grid: [stack] var(--crumbicon-size) / [stack] var(--crumbicon-size);
place-items: center;
& > * {
grid-area: stack;
}
}
<select> 元素是 DOM 中的最後一個元素,因此位於堆疊頂端,且可互動。新增 opacity: .01 樣式,讓元素仍可使用,且結果是完全符合圖示形狀的選取方塊。這樣一來,您就能自訂 <select> 元素的外觀,同時保留內建功能。
.disguised-select {
inline-size: 100%;
block-size: 100%;
opacity: .01;
font-size: min(100%, 16px); /* Defaults to 16px; fixes iOS zoom */
}
溢位
麵包屑應能代表非常長的追蹤路徑。我喜歡在適當情況下,允許項目在水平方向超出畫面,而我覺得這個麵包屑元件很適合。
.breadcrumbs {
overflow-x: auto;
overscroll-behavior-x: contain;
scroll-snap-type: x proximity;
scroll-padding-inline: calc(var(--nav-gap) / 2);
& > .crumb:last-of-type {
scroll-snap-align: end;
}
@supports (-webkit-hyphens:none) { & {
scroll-snap-type: none;
}}
}
溢位樣式會設定下列使用者體驗:
- 水平捲動,並含有過度捲動容器。
- 水平捲動邊框間距。
- 最後一個麵包屑上的其中一個對齊點。也就是說,載入頁面時,第一個麵包屑會載入並顯示在畫面中。
- 從 Safari 移除快速移動點,因為 Safari 無法處理水平捲動和快速移動效果的組合。
媒體查詢
針對較小的檢視區塊,其中一項細微調整是隱藏「首頁」標籤,只留下圖示:
@media (width <= 480px) {
.breadcrumbs .home-label {
display: none;
}
}

無障礙設定
動作
這個元件的動作並不多,但只要將轉場效果包裝在 prefers-reduced-motion 檢查中,就能避免不必要的動作。
@media (prefers-reduced-motion: no-preference) {
.crumbicon {
transition: box-shadow .2s ease;
}
}
其他樣式都不需要變更,懸停和焦點效果很棒,而且沒有 transition 也很實用,但如果可以加入動態效果,我們會在互動中加入細微的轉場效果。
JavaScript
首先,無論您在網站或應用程式中使用哪種路由器,當使用者變更麵包屑時,網址都必須更新,並向使用者顯示適當的網頁。其次,為了讓使用者體驗正常化,請確保使用者只是瀏覽選項時,不會發生非預期的導覽。<select>
JavaScript 應處理的兩項重要使用者體驗指標:選取項目已變更,以及防止觸發急切的 <select> 變更事件。
由於使用 <select> 元素,因此需要防止急切事件。在 Windows Edge (可能也包括其他瀏覽器) 中,當使用者透過鍵盤瀏覽選項時,系統會觸發選取 changed 事件。這就是我稱之為「急切」的原因,因為使用者只是虛擬選取選項 (例如懸停或焦點),但尚未透過 enter 或 click 確認選擇。由於開啟選取方塊並瀏覽項目時,系統會觸發事件並變更網頁,因此使用者還沒準備好時,就無法使用這項元件類別變更功能。
更完善的<select>已變更活動
const crumbs = document.querySelectorAll('.breadcrumbs select')
const allowedKeys = new Set(['Tab', 'Enter', ' '])
const preventedKeys = new Set(['ArrowUp', 'ArrowDown'])
// watch crumbs for changes,
// ensures it's a full value change, not a user exploring options via keyboard
crumbs.forEach(nav => {
let ignoreChange = false
nav.addEventListener('change', e => {
if (ignoreChange) return
// it's actually changed!
})
nav.addEventListener('keydown', ({ key }) => {
if (preventedKeys.has(key))
ignoreChange = true
else if (allowedKeys.has(key))
ignoreChange = false
})
})
這項策略是監看每個 <select> 元素上的鍵盤按下事件,並判斷按下的按鍵是否為導覽確認 (Tab 或 Enter) 或空間導覽 (ArrowUp 或 ArrowDown)。根據這項判斷結果,元件可以決定在 <select> 元素的事件觸發時等待或繼續。
結論
現在你已瞭解我的做法,你會怎麼做呢?🙂
讓我們多元化地運用各種方法,學習在網路上建構內容的所有方式。 建立試聽版,然後在推特上傳送連結給我,我會將連結加到下方的社群混音區!
社群重混作品
- Tux Solbakk 做為網頁元件:示範和程式碼