基礎概略說明如何建構回應式且無障礙的麵包屑元件,方便使用者瀏覽網站。
在本篇文章中,我想分享如何建構麵包屑元件的方法。試用示範模式。
如果你偏好觀看影片,請參閱這篇文章的 YouTube 版本:
總覽
麵包屑元件會顯示使用者在網站階層中的所在位置。這個名稱源自於童話故事「漢塞與葛麗特」,故事中兩位主角在黑暗的森林中掉落麵包屑,並透過反向追蹤麵包屑找到回家的路。
本文中的麵包屑並非標準麵包屑,而是類似麵包屑的功能。透過 <select>
將同層頁面直接放入導覽中,提供額外功能,讓使用者可以多層存取。
背景使用者體驗
在上述元件示範影片中,預留位置類別是電玩遊戲類型。如要建立這條軌跡,請前往以下路徑:home »
rpg » indie » on sale
,如以下所示。
這個麵包屑元件應可讓使用者瀏覽這個資訊階層,快速且精準地跳過分支並選取頁面。
資訊架構
我發現,以集合和項目的角度思考會很有幫助。
集合
集合是可供選擇的選項陣列。根據這篇文章中麵包屑原型設計的首頁,收藏集包括第一人稱射擊遊戲、角色扮演遊戲、格鬥遊戲、地牢探險遊戲、運動遊戲和益智遊戲。
項目
影片遊戲是項目,特定集合如果代表其他集合,也可以是項目。舉例來說,RPG 是項目和有效的集合。如果是商品,使用者會位於該產品集合頁面。舉例來說,這些類別會顯示在 RPG 頁面,其中列出 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>
連結,但我已透過偽裝的選取項目新增了檢視使用者體驗。.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>
連結和一些選項並無特別之處,但可為簡單的麵包屑新增更多功能。將 title
新增至 <select>
元素,可協助螢幕閱讀器使用者瞭解按鈕的動作。不過,這項功能也提供相同的協助,您會在 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 中移除貼齊點,因為該點會與水平捲動和貼齊效果組合產生衝突。
媒體查詢
針對較小的檢視區塊,您可以微調「Home」標籤,只保留圖示:
@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 做為網路元件:示範和程式碼