Erstellen barrierefreier Menüsysteme

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ Es gibt viele verschiedene Arten von Menüs im Internet. Bei der Schaffung integrativer Erlebnisse geht es darum, die richtigen Menümuster an den richtigen Stellen mit dem richtigen Markup und Verhalten zu verwenden.

Anmerkung des Herausgebers : Dieser Artikel erschien ursprünglich auf Inclusive Components. Wenn Sie mehr über ähnliche Artikel zu inklusiven Komponenten erfahren möchten, folgen Sie @inclusicomps auf Twitter oder abonnieren Sie den RSS-Feed. Indem Sie inclusive-components.design auf Patreon unterstützen, können Sie dazu beitragen, es zur umfassendsten verfügbaren Datenbank robuster Schnittstellenkomponenten zu machen.

Eine Einordnung ist schwierig. Nehmen Sie zum Beispiel Krabben. Einsiedlerkrebse, Porzellankrebse und Pfeilschwanzkrebse sind – taxonomisch gesehen – keine echten Krebse. Aber das hindert uns nicht daran, das Suffix „Krabbe“ zu verwenden. Es wird noch verwirrender, wenn sich im Laufe der Zeit und dank eines Prozesses namens Karzinierung unechte Krebse entwickeln, um echten Krebsen ähnlicher zu werden. Dies ist bei Königskrabben der Fall, von denen angenommen wird, dass sie in der Vergangenheit Einsiedlerkrebse waren. Stellen Sie sich die Größe ihrer Muscheln vor!

Im Design machen wir oft den gleichen Fehler, verschiedenen Dingen den gleichen Namen zu geben. Sie sehen ähnlich aus, aber der Schein kann trügen. Dies kann sich ungünstig auf die Übersichtlichkeit Ihrer Bauteilbibliothek auswirken. In Bezug auf die Einbeziehung kann es auch dazu führen, dass Sie eine semantisch und verhaltensmäßig unangemessene Komponente wiederverwenden. Benutzer werden das eine erwarten und das andere bekommen.

Der Begriff „Dropdown“ nennt ein klassisches Beispiel. Viele Dinge „fallen herunter“ in Schnittstellen, einschließlich des Satzes von <option> s aus einem <select> -Element und der durch JavaScript angezeigten Liste von Links, die ein Navigations-Untermenü bilden. Gleicher Name; ganz verschiedene Dinge. (Manche Leute nennen das natürlich „Pulldowns“, aber darauf gehen wir nicht ein.)

Dropdowns, die eine Reihe von Optionen darstellen, werden oft als "Menüs" bezeichnet, und ich möchte hier darüber sprechen. Wir werden ein wahres Menü entwerfen, aber es gibt viel zu sagen über nicht wirklich wahre Menüs auf dem Weg dorthin.

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

Beginnen wir mit einem Quiz. Ist das Linkfeld, das in der Abbildung von der Navigationsleiste herabhängt, ein Menü?

Eine Navigationsleiste mit einem Shop-Link, darunter ein Satz von drei weiteren Links zu Hundekostümen, Waffeleisen bzw. magischen Kugeln.
Eine Navigationsleiste mit einem Shop-Link, darunter ein Satz von drei weiteren Links zu Hundekostümen, Waffeleisen bzw. magischen Kugeln. (Große Vorschau)

Die Antwort ist nein, kein echtes Menü.

Es ist eine langjährige Konvention, dass Navigationsschemata aus Linklisten bestehen. Eine fast ebenso lange Konvention schreibt vor, dass Unternavigation als verschachtelte Linkliste bereitgestellt werden sollte. Wenn ich das CSS für die oben abgebildete Komponente entfernen würde, würde ich ungefähr Folgendes sehen, außer blau und in Times New Roman.

Semantisch sind in diesem Zusammenhang verschachtelte Linklisten richtig. Navigationssysteme sind eigentlich Inhaltsverzeichnisse und so sind Inhaltsverzeichnisse aufgebaut. Das einzige, was uns wirklich an „Menü“ denken lässt, ist das Styling der verschachtelten Listen und die Art und Weise, wie sie beim Bewegen oder Fokussieren angezeigt werden.

Hier gehen einige schief und beginnen, WAI-ARIA-Semantik hinzuzufügen: aria-haspopup="true" , role="menu" , role="menuitem" usw. Es gibt einen Platz dafür, wie wir behandeln werden, aber nicht hier . Hier sind zwei Gründe dafür:

  1. ARIA-Menüs dienen nicht der Navigation, sondern dem Anwendungsverhalten. Stellen Sie sich das Menüsystem für eine Desktop-Anwendung vor.
  2. Der Top-Level-Link sollte als Link verwendbar sein, sich also nicht wie eine Menüschaltfläche verhalten.

Bezüglich (2): Beim Durchqueren eines Navigationsbereichs mit Untermenüs würde man erwarten, dass jedes Untermenü erscheint, wenn man mit der Maus über den „oberste Ebene“-Link („Shop“ in der Abbildung) schwebt oder darauf fokussiert. Dadurch wird sowohl das Untermenü angezeigt als auch die eigenen Links in der Fokusreihenfolge platziert. Mit ein wenig Hilfe von JavaScript, das Fokus- und Unschärfeereignisse erfasst, um das Erscheinungsbild der Untermenüs bei Bedarf beizubehalten, sollte jemand, der die Tastatur verwendet, in der Lage sein, nacheinander durch jeden Link jeder Ebene zu navigieren.

Menüschaltflächen, die die Eigenschaft aria-haspopup="true" , verhalten sich nicht so. Sie werden per Klick aktiviert und haben keinen anderen Zweck, als ein geheimes Menü zu enthüllen.

E-Book-Rabatte bieten wir
Links: eine Menüschaltfläche mit der Bezeichnung „Menü“ mit einem nach unten zeigenden Pfeilsymbol und dem Zustand „aria-expanded = false“. Rechts: Dieselbe Menüschaltfläche, aber mit geöffnetem Menü. Diese Schaltfläche befindet sich im Zustand aria-expanded = true. (Große Vorschau)

Wie abgebildet, sollte mit aria-expanded mitgeteilt werden, ob dieses Menü geöffnet oder geschlossen ist. Sie sollten diesen Status nur beim Klicken ändern, nicht beim Fokussieren. Benutzer erwarten normalerweise keine explizite Zustandsänderung bei einem reinen Fokusereignis. In unserem Navigationssystem ändert sich der Zustand nicht wirklich; Es ist nur ein Styling-Trick. Verhaltensmäßig können wir durch die Navigation blättern, als ob es keine solchen Ein-/Ausblenden-Tricks gäbe.

Das Problem mit Navigations-Untermenüs

Navigations-Untermenüs (oder "Dropdowns" für einige) funktionieren gut mit einer Maus oder Tastatur, aber sie sind nicht so heiß, wenn es um Berührung geht. Wenn Sie in unserem Beispiel zum ersten Mal auf den Top-Level-Link „Shop“ klicken, sagen Sie ihm, dass er sowohl das Untermenü öffnen als auch dem Link folgen soll.

Hier gibt es zwei mögliche Auflösungen:

  1. Verhindern des Standardverhaltens von Top-Level-Links ( e.preventDefault() ) und Skripts in vollständiger WAI-ARIA-Menüsemantik und -verhalten.
  2. Stellen Sie sicher, dass jede Zielseite der obersten Ebene ein Inhaltsverzeichnis als Alternative zum Untermenü hat.

(1) ist unbefriedigend, da diese Art von Semantik und Verhalten in diesem Kontext, in dem Links die Subjektkontrollen sind, nicht erwartet wird, wie ich bereits erwähnt habe. Plus-Benutzer konnten nicht mehr zu einer Seite der obersten Ebene navigieren, falls vorhanden.

Nebenbemerkung: Welche Geräte sind Touch-Geräte?

Es ist verlockend zu denken: „Das ist keine großartige Lösung, aber ich werde sie nur für Touch-Oberflächen hinzufügen“. Das Problem ist: Wie erkennt man, ob ein Gerät einen Touchscreen hat?

„Kleiner Bildschirm“ darf man auf keinen Fall mit „Touch aktiviert“ gleichsetzen. Da ich im selben Büro wie Leute gearbeitet habe, die Touch-Displays für Museen herstellen, kann ich Ihnen versichern, dass einige der größten Bildschirme Touchscreens sind. Laptops mit zwei Tastaturen und Berührungseingabe werden ebenfalls immer produktiver.

Aus dem gleichen Grund sind viele, aber nicht alle kleineren Geräte Touch-Geräte. Beim inklusiven Design können Sie es sich nicht leisten, Annahmen zu treffen.

Auflösung (2) ist umfassender und robuster, da sie einen "Fallback" für Benutzer aller Eingaben bereitstellt. Aber die erschreckenden Anführungszeichen rund um den Fallback-Begriff hier sind ziemlich absichtlich, weil ich tatsächlich denke, dass In-Page-Inhaltsverzeichnisse eine überlegene Art der Bereitstellung von Navigation sind.

Das preisgekrönte Team von Government Digital Services scheint dem zuzustimmen. Vielleicht haben Sie sie auch schon auf Wikipedia gesehen.

Gov.uk-Inhaltsverzeichnisse sind minimal mit Bindestrichen als Listenstile. Wikipedia bietet ein umrandetes graues Kästchen mit nummerierten Einträgen. Beides sind gekennzeichnete Inhalte.
Gov.uk-Inhaltsverzeichnisse sind minimal mit Bindestrichen als Listenstile. Wikipedia bietet ein umrandetes graues Kästchen mit nummerierten Einträgen. Beides sind gekennzeichnete Inhalte.

Inhaltsverzeichnisse

Inhaltsverzeichnisse dienen der Navigation für verwandte Seiten oder Seitenabschnitte und sollten den Hauptnavigationsbereichen der Website semantisch ähnlich sein, indem sie ein <nav> -Element, eine Liste und einen Gruppenbezeichnungsmechanismus verwenden.

 <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 -->

Anmerkungen

  • In diesem Beispiel stellen wir uns vor, dass jeder Abschnitt eine eigene Seite ist, wie es im Dropdown-Untermenü der Fall gewesen wäre.
  • Es ist wichtig, dass jede dieser „Shop“-Seiten die gleiche Struktur hat, wobei das Inhaltsverzeichnis „Produkte“ an derselben Stelle vorhanden ist. Konsistenz unterstützt das Verständnis.
  • Die Liste gruppiert die Elemente und listet sie in der Ausgabe von Hilfstechnologien auf, wie z. B. der synthetischen Stimme eines Bildschirmlesers.
  • Das <nav> wird rekursiv durch die Überschrift mit aria-labelledby . Das bedeutet, dass in den meisten Screenreadern beim Betreten der Region per Tab "Produktnavigation" angesagt wird. Das bedeutet auch, dass die „Produktnavigation“ in den Screenreader-Elementoberflächen aufgeschlüsselt wird, von denen aus die Benutzer direkt zu den Regionen navigieren können.

Alles auf einer Seite

Wenn Sie alle Abschnitte auf einer Seite unterbringen können, ohne dass das Scrollen zu lang und mühsam wird, umso besser. Verlinken Sie einfach auf die Hash-ID jedes Abschnitts. Beispielsweise sollte href="#waffle-irons" darauf verweisen .

 <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 -->

( Hinweis: Einige Browser sind schlecht darin, den Fokus tatsächlich auf verknüpfte Seitenfragmente zu lenken. Das Platzieren tabindex="-1" auf dem Zielfragment behebt dies.)

Wenn eine Website viele Inhalte hat, ist eine sorgfältig konstruierte Informationsarchitektur, die sich durch die großzügige Verwendung von Inhaltsverzeichnissen „Menüs“ ausdrückt, einem prekären und unhandlichen Dropdown-System unendlich vorzuziehen. Es ist nicht nur einfacher, reaktionsschnell zu sein, und erfordert weniger Code, sondern macht die Dinge auch klarer: Wo Dropdown-Systeme die Struktur verbergen, legen Inhaltsverzeichnisse sie offen.

Einige Websites, einschließlich gov.uk des Government Digital Service, enthalten Indexseiten (oder „Themenseiten“), die nur Inhaltsverzeichnisse sind. Es ist ein so mächtiges Konzept, dass der beliebte Static-Site-Generator Hugo solche Seiten standardmäßig generiert.

Diagramm im Stil eines Stammbaums mit Themenzielseite oben mit zwei einzelnen Seitenzweigen. Jeder der einzelnen Seitenzweige hat mehrere Seitenabschnittszweige
Diagramm im Stil eines Stammbaums mit Themenzielseite oben mit zwei einzelnen Seitenzweigen. Jeder der einzelnen Seitenableger hat mehrere Seitenabschnittsableger (Große Vorschau)

Informationsarchitektur ist ein großer Teil der Inklusion. Eine schlecht organisierte Website kann technisch so konform sein, wie Sie möchten, wird aber dennoch viele Benutzer abschrecken – insbesondere diejenigen mit kognitiven Beeinträchtigungen oder solche, die unter Zeitdruck stehen.

Schaltflächen des Navigationsmenüs

Wo wir gerade beim Thema faux navigationsbezogene Menüs sind, wäre es nachlässig von mir, nicht über Navigationsmenütasten zu sprechen. Sie haben diese mit ziemlicher Sicherheit schon einmal gesehen, die durch ein dreizeiliges „Hamburger“- oder „Navicon“-Symbol gekennzeichnet sind.

Selbst bei einer reduzierten Informationsarchitektur und nur einer Ebene mit Navigationslinks ist der Platz auf kleinen Bildschirmen knapp. Das Ausblenden der Navigation hinter einer Schaltfläche bedeutet, dass im Ansichtsfenster mehr Platz für den Hauptinhalt vorhanden ist.

Eine Navigationsschaltfläche kommt einer echten Menüschaltfläche am nächsten, was wir bisher untersucht haben. Da es den Zweck hat, die Verfügbarkeit eines Menüs beim Klicken umzuschalten, sollte es:

  1. Identifiziert sich als Schaltfläche, nicht als Link;
  2. Identifizieren Sie den erweiterten oder reduzierten Zustand des entsprechenden Menüs (das streng genommen nur eine Liste von Links ist).

Progressive Enhancement

Aber greifen wir nicht vor. Wir sollten auf die progressive Erweiterung achten und überlegen, wie dies ohne JavaScript funktionieren würde.

In einem nicht erweiterten HTML-Dokument können Sie nicht viel mit Schaltflächen tun (außer Senden-Schaltflächen, aber das hat nicht einmal eng mit dem zu tun, was wir hier erreichen wollen). Vielleicht sollten wir stattdessen einfach mit einem Link beginnen, der uns zur Navigation führt?

 <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>

Es macht nicht viel Sinn, den Link zu haben, es sei denn, es gibt viel Inhalt zwischen dem Link und der Navigation. Da die Seitennavigation fast immer ganz oben in der Quellreihenfolge erscheinen sollte, besteht keine Notwendigkeit. Ein Navigationsmenü ohne JavaScript sollte also wirklich nur … eine Art Navigation sein.

 <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>

Sie erweitern dies, indem Sie die Schaltfläche in ihrem ursprünglichen Zustand hinzufügen und die Navigation ausblenden (mit dem Attribut 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>

Einige ältere Browser – Sie wissen welche – unterstützen kein hidden , denken Sie also daran, Folgendes in Ihr CSS einzufügen. Es behebt das Problem, weil display: none den gleichen Effekt hat, das Menü vor Hilfstechnologien zu verbergen und die Links aus der Fokusreihenfolge zu entfernen.

 [hidden] { display: none; }

Sein Bestes zu tun, um ältere Software zu unterstützen, ist natürlich ein Akt des inklusiven Designs. Einige sind nicht in der Lage oder nicht bereit, ein Upgrade durchzuführen.

Platzierung

Viele Leute machen einen Fehler, indem sie den Button außerhalb der Region platzieren. Dies würde bedeuten, dass Screenreader-Benutzer, die mit einer Verknüpfung zu <nav> wechseln, dieses leer finden würden, was nicht sehr hilfreich ist. Da die Liste vor Screenreadern verborgen war, würden sie nur auf Folgendes stoßen:

 <nav> </nav>

So könnten wir den Zustand umschalten:

 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; });

Arie-Steuerung

Wie ich in Aria-controls Is Poop geschrieben habe, wird das Attribut aria-controls , das Screenreader-Benutzern helfen soll, von einem steuernden Element zu einem gesteuerten Element zu navigieren, nur im JAWS-Screenreader unterstützt. Darauf kann man sich also einfach nicht verlassen.

Ohne eine gute Methode zum Weiterleiten von Benutzern zwischen Elementen sollten Sie stattdessen sicherstellen, dass eine der folgenden Bedingungen erfüllt ist:

  1. Der erste Link der erweiterten Liste ist der nächste in Fokusreihenfolge nach der Schaltfläche (wie im vorherigen Codebeispiel).
  2. Der erste Link wird programmgesteuert auf das Aufdecken der Liste fokussiert.

In diesem Fall würde ich (1) empfehlen. Es ist viel einfacher, da Sie sich keine Gedanken darüber machen müssen, den Fokus zurück auf die Schaltfläche zu verschieben und auf welche Ereignisse dies zu tun ist. Außerdem gibt es derzeit keine Möglichkeit, die Benutzer zu warnen, dass ihr Fokus an einen anderen Ort verschoben wird. In den wahren Menüs, die wir gleich besprechen werden, ist dies die Aufgabe von aria-haspopup="true" .

Der Einsatz von aria-controls schadet nicht wirklich, außer dass es das Vorlesen in Screenreadern ausführlicher macht. Einige JAWS-Benutzer können dies jedoch erwarten. So würde es angewendet werden, wobei die id der Liste als Chiffre verwendet würde:

 <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>

Die Menü- und Menüelementrollen

Ein echtes Menü (im WAI-ARIA-Sinn) sollte sich selbst als solches identifizieren, indem es die menu (für den Container) und typischerweise menuitem Kinder verwendet (andere untergeordnete Rollen können zutreffen). Diese übergeordneten und untergeordneten Rollen arbeiten zusammen, um Informationen für Hilfstechnologien bereitzustellen. So könnte eine Liste um eine Menüsemantik erweitert werden:

 <ul role="menu"> <li role="menuitem">Item 1</li> <li role="menuitem">Item 2</li> <li role="menuitem">Item 3</li> </ul>

Da sich unser Navigationsmenü allmählich wie ein „echtes“ Menü verhält, sollten diese nicht vorhanden sein?

Die kurze Antwort lautet: nein. Die lange Antwort lautet: nein, da unsere Listenelemente Links enthalten und menuitem keine interaktiven Nachkommen haben sollen. Das heißt, sie sind die Steuerelemente in einem Menü.

Wir könnten natürlich die Listensemantik der <li> s mit role="presentation" oder role="none" (die äquivalent sind) unterdrücken und den menuitem role auf jeden Link setzen. Dies würde jedoch die Rolle des impliziten Links unterdrücken. Mit anderen Worten, das folgende Beispiel würde als „Home, Menüpunkt“ und nicht als „Home, Link“ oder „Home, Menüpunkt, Link“ angesagt. ARIA-Rollen überschreiben einfach HTML-Rollen.

 <!-- will be read as "Home, menu item" --> <li role="presentation"> <a href="/" role="menuitem">Home</a> </li>

Wir möchten, dass der Benutzer weiß, dass er einen Link verwendet und ein Linkverhalten erwarten kann, daher ist dies nicht gut. Wie ich schon sagte, sind echte Menüs für (JavaScript-gesteuertes) Anwendungsverhalten.

Was bleibt, ist eine Art Hybridkomponente, die kein echtes Menü ist, aber dank des aria-expanded Zustands dem Benutzer zumindest mitteilt, ob die Linkliste geöffnet ist. Dies ist ein vollkommen zufriedenstellendes Muster für Navigationsmenüs.

Nebenbemerkung: Das <select> -Element

Wenn Sie von Anfang an mit responsivem Design zu tun hatten, erinnern Sie sich vielleicht an ein Muster, bei dem die Navigation in einem <select> -Element für schmale Ansichtsfenster komprimiert wurde.

Mobilteil mit ausgewähltem Element, das oben im Ansichtsfenster „Zuhause“ anzeigt
Mobilteil mit ausgewähltem Element, das oben im Ansichtsfenster „Zuhause“ anzeigt.

Wie bei den von uns besprochenen Kontrollkästchen-basierten Umschaltflächen ist die Verwendung eines nativen Elements, das sich ohne zusätzliches Skripting einigermaßen wie beabsichtigt verhält, eine gute Wahl für die Effizienz und – insbesondere auf Mobilgeräten – die Leistung. Und <select> -Elemente sind eine Art Menü mit ähnlicher Semantik wie das durch Schaltflächen ausgelöste Menü, das wir bald konstruieren werden.

Allerdings verwenden wir, genau wie bei der Schaltfläche zum Umschalten des Kontrollkästchens, ein Element, das mit der Eingabe von Eingaben verbunden ist, und nicht nur, um eine Auswahl zu treffen. Dies wird wahrscheinlich bei vielen Benutzern Verwirrung stiften – insbesondere, da dieses Muster JavaScript verwendet, damit sich die ausgewählte <option> wie ein Link verhält. Die unerwartete Änderung des Kontexts, die dies hervorruft, wird gemäß dem WCAG-Kriterium 3.2.2 On Input (Level A) als Fehler angesehen.

Wahre Menüs

Nachdem wir nun die Diskussion über falsche Menüs und Quasi-Menüs geführt haben, ist die Zeit gekommen, ein echtes Menü zu erstellen, das durch eine echte Menüschaltfläche geöffnet und geschlossen wird. Von hier an bezeichne ich die Schaltfläche und das Menü zusammen einfach als „Menütaste“.

Aber in welcher Hinsicht wird unser Menü-Button wahr sein? Nun, es wird eine Menükomponente sein, die zum Auswählen von Optionen in der betreffenden Anwendung bestimmt ist, die alle erwarteten Semantiken und entsprechenden Verhaltensweisen implementiert, die für ein solches Werkzeug als konventionell angesehen werden.

Wie bereits erwähnt, stammen diese Konventionen aus dem Design von Desktop-Anwendungen. ARIA-Zuordnung und JavaScript-gesteuertes Fokusmanagement sind erforderlich, um sie vollständig zu imitieren. Ein Teil des Zwecks von ARIA besteht darin, Webentwicklern dabei zu helfen, reichhaltige Weberlebnisse zu erstellen, ohne mit den in der nativen Welt geschmiedeten Usability-Konventionen zu brechen.

In diesem Beispiel stellen wir uns vor, dass unsere Anwendung eine Art Spiel oder Quiz ist. Unsere Menüschaltfläche lässt den Benutzer einen Schwierigkeitsgrad auswählen. Mit der gesamten Semantik sieht das Menü wie folgt aus:

 <button aria-haspopup="true" aria-expanded="false"> Difficulty <span aria-hidden="true">&#x25be;</span> </button> <div role="menu"> <button role="menuitem">Easy</button> <button role="menuitem">Medium</button> <button role="menuitem">Incredibly Hard</button> </div>

Anmerkungen

  • Die Eigenschaft aria-haspopup gibt einfach an, dass die Schaltfläche ein Menü absondert. Es fungiert als Warnung, dass der Benutzer beim Drücken zum „Popup“-Menü bewegt wird (wir werden das Fokusverhalten in Kürze behandeln). Sein Wert ändert sich nicht – er bleibt immer so true .
  • Das <span> innerhalb der Schaltfläche enthält den Unicode-Punkt für ein schwarzes, nach unten zeigendes kleines Dreieck. Diese Konvention zeigt visuell an, was aria-haspopup nicht-visuell tut – dass das Drücken der Taste etwas darunter zeigt. Die Zuordnung aria-hidden="true" verhindert, dass Screenreader „nach unten zeigendes Dreieck“ oder ähnliches ankündigen. Dank aria-haspopup wird es im nicht-visuellen Kontext nicht benötigt.
  • Die Eigenschaft aria-haspopup wird durch aria-expanded ergänzt. Dadurch wird dem Benutzer mitgeteilt, ob sich das Menü derzeit in einem geöffneten (erweiterten) oder geschlossenen (reduzierten) Zustand befindet, indem zwischen true und false Werten umgeschaltet wird.
  • Das Menü selbst übernimmt die (passend benannte) menu . Es nimmt Nachkommen mit der menuitem Rolle an. Sie müssen nicht direkt dem menu untergeordnet sein, sind es aber in diesem Fall – der Einfachheit halber.

Tastatur- und Fokusverhalten

Wenn es darum geht, interaktive Steuerelemente für die Tastatur zugänglich zu machen, ist das Beste, was Sie tun können, die richtigen Elemente zu verwenden. Da wir hier <button> -Elemente verwenden, können wir sicher sein, dass Klickereignisse bei Eingabe- und Leertaste ausgelöst werden, wie in der HTMLButtonElement -Schnittstelle angegeben. Das bedeutet auch, dass wir die Menüpunkte mit der der Schaltfläche zugeordneten Eigenschaft disabled können.

Es gibt jedoch noch viel mehr über die Tastaturinteraktion mit Menütasten. Hier ist eine Zusammenfassung aller Fokus- und Tastaturverhalten, die wir implementieren werden, basierend auf WAI-ARIA Authoring Practices 1.1:

Geben Sie , Leertaste oder auf der Menütaste ein Öffnet das Menü
auf einen Menüpunkt Verschiebt den Fokus auf das nächste Menüelement oder das erste Menüelement, wenn Sie sich auf dem letzten befinden
auf einem Menüpunkt Verschiebt den Fokus auf das vorherige Menüelement oder das letzte Menüelement, wenn Sie sich auf dem ersten befinden
auf der Menütaste Schließt das Menü, falls geöffnet
Esc auf einem Menüpunkt Schließt das Menü und fokussiert die Menüschaltfläche

Der Vorteil des Verschiebens des Fokus zwischen Menüpunkten mit den Pfeiltasten besteht darin, dass die Tabulatortaste zum Verlassen des Menüs erhalten bleibt. In der Praxis bedeutet dies, dass Benutzer sich nicht durch jeden Menüpunkt bewegen müssen, um das Menü zu verlassen – eine enorme Verbesserung der Benutzerfreundlichkeit, insbesondere bei vielen Menüpunkten.

Die Anwendung von tabindex="-1" macht die Menüelemente für Tab nicht fokussierbar, behält jedoch die Möglichkeit bei, die Elemente programmgesteuert zu fokussieren, wenn Tastenanschläge auf den Pfeiltasten erfasst werden.

 <button aria-haspopup="true" aria-expanded="false"> Difficulty <span aria-hidden="true">&#x25be;</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>

Die offene Methode

Als Teil eines soliden API-Designs können wir Methoden zur Behandlung der verschiedenen Ereignisse konstruieren.

Zum Beispiel muss die open -Methode den aria-expanded Wert auf „true“ ändern, die versteckte Eigenschaft des Menüs auf „ false “ ändern und den Fokus auf das erste menuitem im Menü legen, das nicht deaktiviert ist:

 MenuButton.prototype.open = function () { this.button.setAttribute('aria-expanded', true); this.menu.hidden = false; this.menu.querySelector(':not(\[disabled])').focus(); return this; }

Wir können diese Methode ausführen, wenn der Benutzer die Abwärtstaste auf einer fokussierten Menüschaltflächeninstanz drückt:

 this.button.addEventListener('keydown', function (e) { if (e.keyCode === 40) { this.open(); } }.bind(this));

Darüber hinaus kann ein Entwickler, der dieses Skript verwendet, das Menü jetzt programmgesteuert öffnen:

 exampleMenuButton = new MenuButton(document.querySelector('\[aria-haspopup]')); exampleMenuButton.open();

Nebenbemerkung: Der Checkbox-Hack

Soweit möglich, ist es besser, JavaScript nicht zu verwenden, es sei denn, dies ist erforderlich. Die Einbeziehung einer dritten Technologie zusätzlich zu HTML und CSS bedeutet zwangsläufig eine Zunahme der systemischen Komplexität und Anfälligkeit. Allerdings können nicht alle Komponenten zufriedenstellend ohne JavaScript im Mix gebaut werden.

Im Falle von Menüschaltflächen hat die Begeisterung dafür, dass sie „ohne JavaScript funktionieren“, zu etwas geführt, das als Checkbox-Hack bezeichnet wird. Hier wird der aktivierte (oder deaktivierte) Zustand eines versteckten Kontrollkästchens verwendet, um die Sichtbarkeit eines Menüelements mithilfe von CSS umzuschalten.

 /* menu closed */ [type="checkbox"] + [role="menu"] { display: none; } /* menu open */ [type="checkbox"]:checked + [role="menu"] { display: block; }

Für Benutzer von Screenreadern sind die Checkbox-Rolle und der aktivierte Status in diesem Zusammenhang unsinnig. Dies kann teilweise überwunden werden, indem das Kontrollkästchen role="button" hinzugefügt wird.

 <input type="checkbox" role="button" aria-haspopup="true">

Leider unterdrückt dies die implizite geprüfte Zustandskommunikation und beraubt uns des JavaScript-freien Status-Feedbacks (trotzdem wäre es in diesem Kontext schlecht gewesen, wenn es als „geprüft“ bezeichnet worden wäre).

Aber es ist möglich, aria-expanded zu fälschen. Wir müssen unser Etikett nur mit zwei Spannen wie unten angeben.

 <input type="checkbox" role="button" aria-haspopup="true" class="vh"> <label for="toggle" data-opens-menu> Difficulty <span class="vh expanded-text">expanded&lt;/span> <span class="vh collapsed-text">collapsed</span> <span aria-hidden="true">&#x25be;</span> </label>

Diese werden beide mithilfe der visually-hidden Klasse visuell ausgeblendet, aber – je nachdem, in welchem ​​​​Zustand wir uns befinden – nur einer ist auch für Bildschirmleser ausgeblendet. Das heißt, nur einer hat display: none , und dies wird durch den vorhandenen (aber nicht kommunizierten) aktivierten Zustand bestimmt:

 /* 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; }

Das ist clever und alles, aber unsere Menüschaltfläche ist immer noch unvollständig, da das erwartete Fokusverhalten, das wir besprochen haben, einfach nicht ohne JavaScript implementiert werden kann.

Diese Verhaltensweisen sind konventionell und erwartet, wodurch die Schaltfläche benutzerfreundlicher wird. Wenn Sie jedoch wirklich eine Menüschaltfläche ohne JavaScript implementieren müssen, ist dies ungefähr so ​​​​nah wie möglich. In Anbetracht der Tatsache, dass die zuvor beschriebene reduzierte Navigationsmenüschaltfläche Menüinhalte anbietet, die selbst nicht JavaScript-abhängig sind (dh Links), kann dieser Ansatz eine geeignete Option sein.

Zum Spaß ist hier ein codePen, der eine JavaScript-freie Navigationsmenüschaltfläche implementiert.

Siehe das Beispiel für die Stiftnavigationsmenüschaltfläche no JS von Heydon (@heydon) auf CodePen.

( Hinweis: Nur die Leertaste öffnet das Menü.)

Das „Auswählen“-Ereignis

Das Ausführen einiger Methoden sollte Ereignisse ausgeben, damit wir Listener einrichten können. Beispielsweise können wir ein choose Ereignis ausgeben, wenn ein Benutzer auf einen Menüpunkt klickt. Wir können dies mit CustomEvent , wodurch wir ein Argument an die Eigenschaft detail des Ereignisses übergeben können. In diesem Fall wäre das Argument („choice“) der DOM-Knoten des ausgewählten Menüpunkts.

 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; }

Es gibt alle möglichen Dinge, die wir mit diesem Mechanismus tun können. Vielleicht haben wir eine Live-Region mit der id menuFeedback :

 <div role="alert"></div>

Jetzt können wir einen Listener einrichten und die Live-Region mit den im Ereignis geheimen Informationen füllen:

 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}'; });
Wenn ein Benutzer eine Option auswählt, wird das Menü geschlossen und der Fokus kehrt zur Menüschaltfläche zurück. Es ist wichtig, dass Benutzer nach dem Schließen des Menüs zum auslösenden Element zurückkehren.
Wenn ein Benutzer eine Option auswählt, wird das Menü geschlossen und der Fokus kehrt zur Menüschaltfläche zurück. Es ist wichtig, dass Benutzer nach dem Schließen des Menüs zum auslösenden Element zurückkehren. (Große Vorschau)

Wenn ein Menüelement ausgewählt wird, hört der Screenreader-Benutzer „Sie haben [Beschriftung des Menüelements] ausgewählt“ . Eine Live-Region (hier mit der Zuordnung role=“alert” definiert) kündigt ihren Inhalt in Screenreadern an, wenn sich dieser Inhalt ändert. Die Live-Region ist nicht obligatorisch, aber sie ist ein Beispiel dafür, was in der Benutzeroberfläche als Reaktion darauf passieren kann, dass der Benutzer eine Menüauswahl trifft.

Beständige Wahlmöglichkeiten

Nicht alle Menüpunkte dienen der Auswahl dauerhafter Einstellungen. Viele verhalten sich einfach wie Standardtasten, die beim Drücken etwas in der Benutzeroberfläche auslösen. Im Fall unserer Schwierigkeitsmenüschaltfläche möchten wir jedoch angeben, welches die aktuelle Schwierigkeitseinstellung ist – die zuletzt gewählte.

Das Attribut aria-checked="true" funktioniert für Elemente, die anstelle von menuitem die Rolle menuitemradio übernehmen. Das erweiterte Markup mit dem zweiten aktivierten Element ( set ) sieht folgendermaßen aus:

 <button aria-haspopup="true" aria-expanded="false"> Difficulty <span aria-hidden="true">&#x25be;</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>

Native Menüs auf vielen Plattformen zeigen ausgewählte Elemente mit Häkchen an. Wir können das problemlos mit ein wenig zusätzlichem CSS tun:

 [role="menuitem"] [aria-checked="true"]::before { content: '\2713\0020'; }

Beim Durchlaufen des Menüs mit einem laufenden Screenreader wird beim Fokussieren dieses aktivierten Elements eine Ansage wie „Häkchen, mittleres Menüelement, aktiviert“ angezeigt.

Das Verhalten beim Öffnen eines Menüs mit menuitemradio weicht geringfügig ab. Anstatt das erste (aktivierte) Element im Menü zu fokussieren, wird stattdessen das markierte Element fokussiert.

Die Menütaste startet mit dem ungeöffneten Menü. Auf das Öffnen konzentriert man sich auf die zweite (mittlere) Schwierigkeitsstufe. Ihm ist ein Häkchen vorangestellt, das auf dem Vorhandensein des aria-checked-Attributs basiert.
Die Menütaste startet mit dem ungeöffneten Menü. Auf das Öffnen konzentriert man sich auf die zweite (mittlere) Schwierigkeitsstufe. Ihm ist ein Häkchen vorangestellt, das auf dem Vorhandensein des aria-checked-Attributs basiert. (Große Vorschau)

Was ist der Vorteil dieses Verhaltens? Der Benutzer (jeder Benutzer) wird an seine zuvor ausgewählte Option erinnert. In Menüs mit zahlreichen inkrementellen Optionen (z. B. einer Reihe von Zoomstufen) werden Personen, die über die Tastatur arbeiten, in die optimale Position gebracht, um ihre Anpassung vorzunehmen.

Verwenden der Menüschaltfläche mit einem Screenreader

In diesem Video zeige ich Ihnen, wie Sie die Menüschaltfläche mit dem Voiceover-Screenreader und Chrome verwenden. Das Beispiel verwendet Elemente mit menuitemradio , aria-checked und das diskutierte Fokusverhalten. Ähnliche Erfahrungen können in der gesamten Bandbreite gängiger Screenreader-Software erwartet werden.

Inklusive Menü-Button auf Github

Kitty Giraudel und ich haben zusammengearbeitet, um eine Menüschaltflächenkomponente mit den von mir beschriebenen API-Funktionen und mehr zu erstellen. Sie haben Hugo viele dieser Funktionen zu verdanken, da sie auf der Arbeit basierten, die sie an a11y-dialog – einem zugänglichen modalen Dialog – geleistet haben. Es ist auf Github und NPM verfügbar.

 npm i inclusive-menu-button --save

Darüber hinaus hat Kitty eine React-Version für Ihren Hochgenuss erstellt.

Checkliste

  • Verwenden Sie keine ARIA-Menüsemantik in Navigationsmenüsystemen.
  • Verstecken Sie auf inhaltsintensiven Websites die Struktur nicht in verschachtelten Dropdown-gesteuerten Navigationsmenüs.
  • Verwenden Sie aria-expanded , um den geöffneten/geschlossenen Zustand eines durch Schaltflächen aktivierten Navigationsmenüs anzuzeigen.
  • Stellen Sie sicher, dass das Navigationsmenü das nächste in der Fokusreihenfolge nach der Schaltfläche ist, die es öffnet/schließt.
  • Opfern Sie niemals die Benutzerfreundlichkeit im Streben nach JavaScript-freien Lösungen. Es ist Eitelkeit.