構建無障礙菜單系統
已發表: 2022-03-10編者註:本文最初出現在包容性組件上。 如果您想了解更多類似的包容性組件文章,請在 Twitter 上關注 @inclusicomps 或訂閱 RSS 提要。 通過在 Patreon 上支持 inclusive-components.design,您可以幫助使其成為最全面的強大接口組件數據庫。
分類很難。 以螃蟹為例。 寄居蟹、瓷蟹和馬蹄蟹——從分類學上講——不是真正的螃蟹。 但這並不能阻止我們使用“螃蟹”後綴。 隨著時間的推移,由於一種稱為“癌化”的過程,不真實的螃蟹進化成更接近真實的螃蟹時,它變得更加令人困惑。 帝王蟹就是這種情況,過去人們認為它們是寄居蟹。 想像一下它們貝殼的大小!
在設計中,我們經常犯同樣的錯誤,給不同的東西取同一個名字。 它們看起來相似,但外觀可能具有欺騙性。 這可能會對組件庫的清晰度產生不利影響。 在包含方面,它還可能導致您重新利用語義和行為上不適當的組件。 用戶會期待一件事並得到另一件事。
術語“下拉”命名了一個經典示例。 界面中有很多東西“下拉”,包括來自<select>
元素的<option>
集合,以及 JavaScript 顯示的構成導航子菜單的鏈接列表。 一樣的名字; 完全不同的東西。 (當然,有些人稱這些為“下拉”,但我們不要深入探討。)
構成一組選項的下拉菜單通常被稱為“菜單”,我想在這裡談談這些。 我們將設計一份真正的菜單,但在此過程中,關於非真正的菜單還有很多話要說。
讓我們從一個測驗開始。 插圖中導航欄上懸掛的鏈接框是菜單嗎?
答案是否定的,不是真正的菜單。
導航模式由鏈接列表組成是一個長期的約定。 一個幾乎長期存在的約定規定子導航應該作為嵌套的鏈接列表提供。 如果我要刪除上面所示組件的 CSS,我應該會看到類似下面的內容,除了藍色和 Times New Roman。
從語義上講,嵌套的鏈接列表在這種情況下是正確的。 導航系統實際上是目錄,這就是目錄的結構。 唯一真正讓我們想到“菜單”的是嵌套列表的樣式以及它們在懸停或聚焦時顯示的方式。
這就是一些錯誤並開始添加 WAI-ARIA 語義的地方: aria-haspopup="true"
、 role="menu"
、 role="menuitem"
等。這些有一個地方,我們將介紹,但不是在這裡. 這裡有兩個原因:
- ARIA 菜單不是為導航指定的,而是為應用程序行為指定的。 想像一下桌面應用程序的菜單系統。
- 頂級鏈接應該可以用作鏈接,這意味著它的行為不像菜單按鈕。
關於(2):當遍歷帶有子菜單的導航區域時,人們會期望每個子菜單在懸停或聚焦“頂級”鏈接(圖中的“商店”)時出現。 這既顯示了子菜單,又按焦點順序放置了自己的鏈接。 借助 JavaScript 捕獲焦點和模糊事件以在需要時保持子菜單的外觀的一點幫助,使用鍵盤的人應該能夠依次通過每個層的每個鏈接進行選項卡。
採用aria-haspopup="true"
屬性的菜單按鈕的行為不是這樣的。 它們在點擊時被激活,除了顯示一個隱藏的菜單之外沒有其他目的。
如圖所示,該菜單是打開還是關閉應該與aria-expanded
進行通信。 您應該只在單擊時更改此狀態,而不是在焦點上。 用戶通常不期望僅在焦點事件上顯式更改狀態。 在我們的導航系統中,狀態並沒有真正改變。 這只是一個造型技巧。 在行為上,我們可以在導航中使用Tab ,就好像沒有發生這種顯示/隱藏技巧一樣。
導航子菜單的問題
導航子菜單(或某些人的“下拉菜單”)可以很好地使用鼠標或鍵盤,但在觸摸時它們並不那麼熱。 當您第一次按下我們示例中的頂級“Shop”鏈接時,您是在告訴它打開子菜單並點擊鏈接。
這裡有兩種可能的解決方案:
- 在完整的 WAI-ARIA 菜單語義和行為中防止頂級鏈接 (
e.preventDefault()
) 和腳本的默認行為。 - 確保每個頂級目標頁面都有一個目錄作為子菜單的替代項。
(1) 不能令人滿意,因為正如我之前提到的,在這種情況下,這些語義和行為是不期望的,其中鏈接是主題控件。 Plus 用戶無法再導航到頂級頁面(如果存在)。
旁注:哪些設備是觸控設備?
很容易想到,“這不是一個很好的解決方案,但我只會為觸摸界面添加它”。 問題是:如何檢測設備是否有觸摸屏?
您當然不應該將“小屏幕”與“觸摸激活”等同起來。 與為博物館製作觸摸屏的人在同一個辦公室工作過,我可以向你保證,周圍一些最大的屏幕是觸摸屏。 雙鍵盤和触摸輸入筆記本電腦也越來越多產。
同樣,許多但並非所有較小的設備都是觸摸設備。 在包容性設計中,您不能做出假設。
解決方案 (2) 更具包容性和穩健性,因為它為所有輸入的用戶提供了“後備”。 但是這裡的後備術語周圍的嚇人引用是經過深思熟慮的,因為我實際上認為頁內目錄是提供導航的一種更好的方式。
屢獲殊榮的政府數字服務團隊似乎同意這一點。 您可能還曾在 Wikipedia 上看到過它們。
目錄
目錄是相關頁面或頁面部分的導航,在語義上應該類似於主站點導航區域,使用<nav>
元素、列表和組標籤機制。
<nav aria-labelledby="sections-heading"> <h2>Products</h2> <ul> <li><a href="/products/dog-costumes">Dog costumes</a></li> <li><a href="/products/waffle-irons">Waffle irons</a></li> <li><a href="/products/magical-orbs">Magical orbs</a></li> </ul> </nav> <!-- each section, in order, here -->
筆記
- 在這個例子中,我們假設每個部分都是它自己的頁面,就像它在下拉子菜單中一樣。
- 重要的是,這些“商店”頁面中的每一個都具有相同的結構,並且此“產品”目錄出現在同一個位置。 一致性支持理解。
- 該列表將項目分組並在輔助技術輸出中枚舉它們,例如屏幕閱讀器的合成語音。
-
<nav>
使用aria-labelledby
labelledby 由標題遞歸標記。 這意味著“產品導航”將在通過Tab進入區域時在大多數屏幕閱讀器中公佈。 這也意味著“產品導航”將在屏幕閱讀器元素界面中逐項列出,用戶可以從中直接導航到區域。
全部在一頁上
如果您可以將所有部分放在一頁上,而不會變得太長且難以滾動,那就更好了。 只需鏈接到每個部分的哈希標識符。 例如, href="#waffle-irons"
應該指向.
<nav aria-labelledby="sections-heading"> <h2>Products</h2> <ul> <li><a href="#dog-costumes">Dog costumes</a></li> <li><a href="#waffle-irons">Waffle irons</a></li> <li><a href="#magical-orbs">Magical orbs</a></li> </ul> </nav> <!-- dog costumes section here --> <section tabindex="-1"> <h2>Waffle Irons</h2> </section> <!-- magical orbs section here -->
(注意:有些瀏覽器在將焦點實際發送到鏈接的頁面片段方面很差。將tabindex="-1"
放在目標片段上可以解決這個問題。)
當一個網站有很多內容時,一個精心構建的信息架構,通過自由使用目錄“菜單”來表達,比不穩定和笨拙的下拉系統更可取。 不僅更容易做出響應,並且需要更少的代碼來做到這一點,而且它使事情變得更清晰:下拉系統隱藏結構的地方,目錄將其暴露。
一些網站,包括政府數字服務的 gov.uk,包括只是目錄的索引(或“主題”)頁面。 這是一個非常強大的概念,流行的靜態站點生成器 Hugo 默認會生成這樣的頁面。
信息架構是包容性的重要組成部分。 一個組織不善的網站在技術上可以隨心所欲,但仍會疏遠很多用戶——尤其是那些有認知障礙或時間緊迫的用戶。
導航菜單按鈕
雖然我們討論的是與導航相關的人造菜單,但如果我不談論導航菜單按鈕,那就太失職了。 您幾乎肯定已經看到這些由三行“漢堡包”或“navicon”圖標表示。
即使使用精簡的信息架構和只有一層導航鏈接,小屏幕上的空間也是非常寶貴的。 將導航隱藏在按鈕後面意味著視口中的主要內容有更多空間。
導航按鈕是迄今為止我們研究過的最接近真正菜單按鈕的東西。 由於它的目的是在點擊時切換菜單的可用性,它應該:
- 將自己標識為按鈕,而不是鏈接;
- 識別其相應菜單的展開或折疊狀態(嚴格來說,它只是一個鏈接列表)。
漸進式增強
但是,我們不要超越自己。 我們應該注意漸進增強,並考慮在沒有 JavaScript 的情況下這將如何工作。
在未增強的 HTML 文檔中,您可以使用按鈕做的事情不多(提交按鈕除外,但這與我們在這裡想要實現的目標沒有密切關係)。 相反,也許我們應該從一個帶我們到導航的鏈接開始?
<a href="#navigation">navigation</a> <!-- some content here perhaps --> <nav> <ul> <li><a href="/">Home</a></li> <li><a href="/about">About</a></li> <li><a href="/shop">Shop</a></li> <li><a href="/content">Content</a></li> </ul> </nav>
除非鏈接和導航之間有很多內容,否則擁有鏈接並沒有多大意義。 由於站點導航應該幾乎總是出現在源訂單的頂部附近,因此沒有必要。 所以,真的,沒有 JavaScript 的導航菜單應該只是……一些導航。
<nav> <ul> <li><a href="/">Home</a></li> <li><a href="/about">About</a></li> <li><a href="/shop">Shop</a></li> <li><a href="/content">Content</a></li> </ul> </nav>
您可以通過添加處於初始狀態的按鈕並隱藏導航(使用hidden
屬性)來增強此功能:
<nav> <button aria-expanded="false">Menu</button> <ul hidden> <li><a href="/">Home</a></li> <li><a href="/about">About</a></li> <li><a href="/shop">Shop</a></li> <li><a href="/contact">Contact</a></li> </ul> </nav>
一些較舊的瀏覽器——你知道哪些——不支持hidden
,所以記得在你的 CSS 中加入以下內容。 它解決了這個問題,因為display: none
具有從輔助技術隱藏菜單和從焦點順序中刪除鏈接的相同效果。
[hidden] { display: none; }
盡最大努力支持舊軟件當然是一種包容性設計行為。 有些無法或不願意升級。
放置
很多人出錯的地方是將按鈕放在區域之外。 這意味著使用快捷方式移動到<nav>
的屏幕閱讀器用戶會發現它是空的,這不是很有幫助。 在屏幕閱讀器隱藏列表的情況下,他們只會遇到以下情況:
<nav> </nav>
以下是我們如何切換狀態:
var navButton = document.querySelector('nav button'); navButton.addEventListener('click', function() { let expanded = this.getAttribute('aria-expanded') === 'true' || false; this.setAttribute('aria-expanded', !expanded); let menu = this.nextElementSibling; menu.hidden = !menu.hidden; });
詠嘆調控制
正如我在 Aria-controls Is Poop 中所寫,旨在幫助屏幕閱讀器用戶從控制元素導航到受控元素的aria-controls
屬性僅在 JAWS 屏幕閱讀器中受支持。 所以你根本不能依賴它。
如果沒有一種在元素之間引導用戶的好方法,您應該確保滿足以下條件之一:
- 展開列表的第一個鏈接按焦點順序位於按鈕之後(如前面的代碼示例中所示)。
- 第一個鏈接以編程方式集中在顯示列表上。
在這種情況下,我會推薦(1)。 這要簡單得多,因為您不必擔心將焦點移回按鈕以及要這樣做的事件。 此外,目前沒有任何東西可以警告用戶他們的注意力將轉移到不同的地方。 在我們稍後將討論的真正菜單中,這是aria-haspopup="true"
的工作。
使用aria-controls
並沒有真正造成太大的危害,只是它會使屏幕閱讀器中的讀數更加冗長。 但是,一些 JAWS 用戶可能會期待它。 以下是它的應用方式,使用列表的id
作為密碼:
<nav> <button aria-expanded="false" aria-controls="menu-list">Menu</button> <ul hidden> <li><a href="/">Home</a></li> <li><a href="/about">About</a></li> <li><a href="/shop">Shop</a></li> <li><a href="/contact">Contact</a></li> </ul> </nav>
menu 和 menuitem 角色
一個真正的菜單(在 WAI-ARIA 意義上)應該使用menu
角色(對於容器)和通常的menuitem
子角色(可能適用其他子角色)來標識自己。 這些父子角色協同工作,為輔助技術提供信息。 以下是如何擴充列表以具有菜單語義的方法:
<ul role="menu"> <li role="menuitem">Item 1</li> <li role="menuitem">Item 2</li> <li role="menuitem">Item 3</li> </ul>
既然我們的導航菜單開始表現得有點像一個“真正的”菜單,這些不應該出現嗎?
最簡潔的答案是不。 長答案是:不,因為我們的列表項包含鏈接,並且menuitem
元素不打算具有交互式後代。 也就是說,它們是菜單中的控件。
當然,我們可以使用role="presentation"
或role="none"
(它們是等效的)來抑制<li>
的列表語義,並將menuitem
角色放置在每個鏈接上。 但是,這會抑制隱式鏈接角色。 換句話說,要遵循的示例將被宣佈為“主頁,菜單項”,而不是“主頁,鏈接”或“主頁,菜單項,鏈接”。 ARIA 角色只是簡單地覆蓋 HTML 角色。
<!-- will be read as "Home, menu item" --> <li role="presentation"> <a href="/" role="menuitem">Home</a> </li>
我們希望用戶知道他們正在使用鏈接並且可以預期鏈接行為,所以這不好。 就像我說的,真正的菜單用於(JavaScript 驅動的)應用程序行為。
我們剩下的是一種混合組件,它不是一個真正的菜單,但至少告訴用戶鏈接列表是否打開,這要歸功於aria-expanded
狀態。 這是一個非常令人滿意的導航菜單模式。
旁注: <select>
元素
如果您從一開始就參與了響應式設計,您可能還記得一種模式,其中導航被壓縮為用於窄視口的<select>
元素。
與我們討論的基於復選框的切換按鈕一樣,使用在沒有額外腳本的情況下表現得有點像預期的本機元素是提高效率和(尤其是在移動設備上)性能的好選擇。 <select>
元素是各種菜單,其語義與我們即將構建的按鈕觸髮菜單相似。
然而,就像複選框切換按鈕一樣,我們使用與輸入相關的元素,而不是簡單地做出選擇。 這可能會導致許多用戶感到困惑——尤其是因為這種模式使用 JavaScript 來使選定的<option>
表現得像一個鏈接。 根據 WCAG 的 3.2.2 輸入(A 級)標準,由此引發的意外上下文變化被視為失敗。
真正的菜單
既然我們已經討論了假菜單和準菜單,現在是時候創建一個真正的菜單了,它由一個真正的菜單按鈕打開和關閉。 從這裡開始,我將按鈕和菜單統稱為“菜單按鈕”。
但是我們的菜單按鈕在哪些方面是真實的呢? 好吧,它將是一個菜單組件,用於在主題應用程序中選擇選項,它實現了所有預期的語義和相應的行為,這些語義和相應的行為被認為是此類工具的常規行為。
如前所述,這些約定來自桌面應用程序設計。 需要 ARIA 歸因和 JavaScript 控制的焦點管理來完全模仿它們。 ARIA 的部分目的是幫助 Web 開發人員創建豐富的 Web 體驗,而不會打破在原生世界中形成的可用性約定。
在此示例中,我們將假設我們的應用程序是某種遊戲或測驗。 我們的菜單按鈕將讓用戶選擇難度級別。 有了所有的語義,菜單看起來像這樣:
<button aria-haspopup="true" aria-expanded="false"> Difficulty <span aria-hidden="true">▾</span> </button> <div role="menu"> <button role="menuitem">Easy</button> <button role="menuitem">Medium</button> <button role="menuitem">Incredibly Hard</button> </div>
筆記
aria-haspopup
屬性只是表明按鈕隱藏了一個菜單。 它作為警告,當按下時,用戶將被移動到“彈出”菜單(我們將很快介紹焦點行為)。 它的價值不會改變——它始終true
不變。- 按鈕內的
<span>
包含黑色向下小三角形的 unicode 點。 這個約定在視覺上表明了aria-haspopup
在非視覺上做了什麼——按下按鈕會在它下面顯示一些東西。aria-hidden="true"
屬性可防止屏幕閱讀器宣布“向下三角形”或類似內容。 感謝aria-haspopup
,在非視覺環境中不需要它。 -
aria-haspopup
屬性由aria-expanded
補充。 這通過在true
值和false
值之間切換來告訴用戶菜單當前是處於打開(展開)還是關閉(折疊)狀態。 - 菜單本身俱有(恰當命名的)
menu
角色。 它需要具有menuitem
角色的後代。 它們不需要是menu
元素的直接子元素,但在這種情況下它們是——為簡單起見。
鍵盤和焦點行為
在使交互式控件鍵盤可訪問時,您能做的最好的事情就是使用正確的元素。 因為我們在這裡使用<button>
元素,所以我們可以確保點擊事件將在Enter和Space擊鍵時觸發,正如 HTMLButtonElement 界面中所指定的那樣。 這也意味著我們可以使用與按鈕相關的disabled
屬性來禁用菜單項。
不過,菜單按鈕鍵盤交互還有很多。 以下是我們將根據 WAI-ARIA 創作實踐 1.1 實現的所有焦點和鍵盤行為的摘要:
Enter , Space或↓在菜單按鈕上 | 打開菜單 |
↓在菜單項上 | 將焦點移至下一個菜單項,如果您在最後一個菜單項上,則將焦點移至第一個菜單項 |
↑在菜單項上 | 將焦點移至上一個菜單項,如果您在第一個菜單項上,則將焦點移至最後一個菜單項 |
↑在菜單按鈕上 | 如果打開則關閉菜單 |
Esc在菜單項上 | 關閉菜單並聚焦菜單按鈕 |
使用箭頭鍵在菜單項之間移動焦點的優點是保留Tab以移出菜單。 實際上,這意味著用戶不必遍歷每個菜單項即可退出菜單——這極大地提高了可用性,尤其是在有許多菜單項的情況下。
tabindex="-1"
的應用使菜單項無法通過Tab聚焦,但保留了在捕獲箭頭鍵上的擊鍵時以編程方式聚焦元素的能力。
<button aria-haspopup="true" aria-expanded="false"> Difficulty <span aria-hidden="true">▾</span> </button> <div role="menu"> <button role="menuitem" tabindex="-1">Easy</button> <button role="menuitem" tabindex="-1">Medium</button> <button role="menuitem" tabindex="-1">Incredibly Hard</button> </div>
打開方法
作為合理 API 設計的一部分,我們可以構建處理各種事件的方法。
例如, open
方法需要將aria-expanded
值切換為“true”,將菜單的menuitem
屬性更改為false
,並聚焦菜單中未禁用的第一個菜單項:
MenuButton.prototype.open = function () { this.button.setAttribute('aria-expanded', true); this.menu.hidden = false; this.menu.querySelector(':not(\[disabled])').focus(); return this; }
我們可以在用戶按下焦點菜單按鈕實例上的向下鍵時執行此方法:
this.button.addEventListener('keydown', function (e) { if (e.keyCode === 40) { this.open(); } }.bind(this));
此外,使用此腳本的開發人員現在可以通過編程方式打開菜單:
exampleMenuButton = new MenuButton(document.querySelector('\[aria-haspopup]')); exampleMenuButton.open();
旁注:複選框黑客
除非需要,否則最好不要使用 JavaScript。 在 HTML 和 CSS 之上引入第三種技術必然會增加系統的複雜性和脆弱性。 但是,並非所有組件都可以在沒有 JavaScript 的情況下令人滿意地構建。
在菜單按鈕的情況下,讓它們“在沒有 JavaScript 的情況下工作”的熱情導致了所謂的複選框 hack。 這是隱藏複選框的選中(或未選中)狀態用於使用 CSS 切換菜單元素的可見性的地方。
/* menu closed */ [type="checkbox"] + [role="menu"] { display: none; } /* menu open */ [type="checkbox"]:checked + [role="menu"] { display: block; }
對於屏幕閱讀器用戶來說,複選框角色和選中狀態在這種情況下是沒有意義的。 這可以通過將role="button"
添加到復選框來部分克服。
<input type="checkbox" role="button" aria-haspopup="true">
不幸的是,這抑制了隱式檢查狀態通信,剝奪了我們無 JavaScript 的狀態反饋(雖然在這種情況下它會被視為“檢查”)。
但是有可能欺騙aria-expanded
。 我們只需要為我們的標籤提供兩個跨度,如下所示。
<input type="checkbox" role="button" aria-haspopup="true" class="vh"> <label for="toggle" data-opens-menu> Difficulty <span class="vh expanded-text">expanded</span> <span class="vh collapsed-text">collapsed</span> <span aria-hidden="true">▾</span> </label>
這些都使用visually-hidden
,但是——取決於我們所處的狀態——只有一個對屏幕閱讀器是隱藏的。 也就是說,只有一個具有display: none
,這是由現存的(但未傳達的)檢查狀態決定的:
/* class to hide spans visually */ .vh { position: absolute !important; clip: rect(1px, 1px, 1px, 1px); padding: 0 !important; border: 0 !important; height: 1px !important; width: 1px !important; overflow: hidden; } /* reveal the correct state wording to screen readers based on state */ [type="checkbox"]:checked + label .expanded-text { display: inline; } [type="checkbox"]:checked + label .collapsed-text { display: none; } [type="checkbox"]:not(:checked) + label .expanded-text { display: none; } [type="checkbox"]:not(:checked) + label .collapsed-text { display: inline; }
這很聰明,但我們的菜單按鈕仍然不完整,因為我們一直在討論的預期焦點行為根本無法在沒有 JavaScript 的情況下實現。
這些行為是常規的和預期的,使按鈕更有用。 然而,如果你真的需要在沒有 JavaScript 的情況下實現一個菜單按鈕,這幾乎是你能得到的。 考慮到我之前提到的縮減導航菜單按鈕提供的菜單內容本身不依賴於 JavaScript(即鏈接),這種方法可能是一個合適的選擇。
為了好玩,這是一個實現無 JavaScript 導航菜單按鈕的 codePen。
請參閱 CodePen 上 Heydon (@heydon) 編寫的 Pen Navigation menu button example no JS。
(注意:只有Space打開菜單。)
“選擇”事件
執行一些方法應該發出事件,以便我們可以設置監聽器。 例如,我們可以在用戶單擊菜單項時發出choose
事件。 我們可以使用CustomEvent
進行設置,它允許我們將參數傳遞給事件的detail
屬性。 在這種情況下,參數(“choice”)將是所選菜單項的 DOM 節點。
MenuButton.prototype.choose = function (choice) { // Define the 'choose' event var chooseEvent = new CustomEvent('choose', { detail: { choice: choice } }); // Dispatch the event this.button.dispatchEvent(chooseEvent); return this; }
我們可以用這個機製做各種各樣的事情。 也許我們設置了一個活動區域,其id
為menuFeedback
:
<div role="alert"></div>
現在我們可以設置一個監聽器並使用事件中隱藏的信息填充活動區域:
exampleMenuButton.addEventListener('choose', function (e) { // Get the node's text content (label) var choiceLabel = e.details.choice.textContent; // Get the live region node var liveRegion = document.getElementById('menuFeedback'); // Populate the live region liveRegion.textContent = 'Your difficulty level is ${choiceLabel}'; });
選擇菜單項後,屏幕閱讀器用戶將聽到“您選擇了 [菜單項的標籤]” 。 實時區域(此處定義為role=“alert”
屬性)在內容髮生變化時在屏幕閱讀器中宣布其內容。 實時區域不是強制性的,但它是界面中可能發生的一個示例,作為對用戶做出菜單選擇的響應。
堅持選擇
並非所有菜單項都用於選擇持久設置。 許多只是像標準按鈕一樣,在按下時會在界面中發生某些事情。 然而,在我們的難度菜單按鈕的情況下,我們想指出哪個是當前難度設置——最後選擇的那個。
aria-checked="true"
屬性適用於代替menuitem
的項目,而不是menuitemradio
角色。 選中第二項 ( set ) 的增強標記如下所示:
<button aria-haspopup="true" aria-expanded="false"> Difficulty <span aria-hidden="true">▾</span> </button> <div role="menu"> <button role="menuitemradio" tabindex="-1">Easy</button> <button role="menuitemradio" aria-checked="true" tabindex="-1">Medium</button> <button role="menuitemradio" tabindex="-1">Incredibly Hard</button> </div>
許多平台上的本機菜單使用複選標記指示所選項目。 我們可以使用一點額外的 CSS 輕鬆做到這一點:
[role="menuitem"] [aria-checked="true"]::before { content: '\2713\0020'; }
在運行屏幕閱讀器的情況下遍歷菜單時,聚焦此選中項將提示類似“選中標記,中菜單項,選中”的通知。
打開帶有選中menuitemradio
的菜單的行為略有不同。 不是聚焦菜單中的第一個(啟用的)項目,而是選中的項目聚焦。
這種行為有什麼好處? 用戶(任何用戶)被提醒他們之前選擇的選項。 在具有許多增量選項(例如,一組縮放級別)的菜單中,通過鍵盤操作的人員被放置在最佳位置以進行調整。
將菜單按鈕與屏幕閱讀器一起使用
在本視頻中,我將向您展示在 Voiceover 屏幕閱讀器和 Chrome 中使用菜單按鈕的感受。 該示例使用帶有menuitemradio
、 aria-checked
和討論的焦點行為的項目。 在所有流行的屏幕閱讀器軟件中,都可以期待類似的體驗。
Github 上的包容性菜單按鈕
Kitty Giraudel 和我一起創建了一個菜單按鈕組件,其中包含我所描述的 API 功能等等。 你要感謝 Hugo 的許多功能,因為它們是基於他們在 a11y-dialog 上所做的工作——一個可訪問的模式對話框。 它在 Github 和 NPM 上可用。
npm i inclusive-menu-button --save
此外,Kitty 還為您創建了一個 React 版本。
清單
- 不要在導航菜單系統中使用 ARIA 菜單語義。
- 在內容繁重的網站上,不要在嵌套的下拉導航菜單中隱藏結構。
- 使用
aria-expanded
指示按鈕激活的導航菜單的打開/關閉狀態。 - 確保所述導航菜單位於打開/關閉按鈕之後的焦點順序中。
- 永遠不要為了追求無 JavaScript 的解決方案而犧牲可用性。 是虛榮。