Benutzerorientierter Zustand

Avatar of Scott O'Hara
Scott O'Hara am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Sprechen wir über den *Zustand*. Genauer gesagt, wie wir den Zustand dem Benutzer mitteilen, nicht wie Anwendungsspeicher den Zustand in JavaScript-Objekten oder localStorage speichern. Wir werden darüber sprechen, wie wir unseren Benutzern den Zustand mitteilen können (denken Sie daran: ob ein Button deaktiviert ist oder nicht, oder ob ein Panel aktiv ist oder nicht) und wie wir CSS dafür verwenden können. Wir werden keine Inline-Stile oder, soweit möglich, Klassenselektoren verwenden, aus Gründen, die im Laufe des Artikels klar werden.

Immer noch hier? Cool. Legen wir los.

Alle dynamischen Komponenten einer Anwendung haben einen standardmäßigen benutzersichtbaren Zustand, und dieser Zustand muss gespeichert und aktualisiert werden, während Benutzer mit diesen Komponenten interagieren.

Wenn beispielsweise ein Button gedrückt wird, passieren Dinge (dafür sind Buttons da). Wenn diese *Dinge* passieren, werden sie typischerweise *visuell* in der Benutzeroberfläche dargestellt. Der Hintergrund des Buttons kann sich ändern, um anzuzeigen, dass er gedrückt wurde. Wenn der Button andere Komponenten in der Benutzeroberfläche steuert, ändern sich diese Komponenten wahrscheinlich visuell im Stil, oder in manchen Fällen wird ihre Sichtbarkeit umgeschaltet. Ein Element wird gelöscht, eine Benachrichtigung erscheint, ein Fehlerstil wird angewendet, usw.

Sie haben vielleicht bemerkt, dass wir den "visuellen" Zustand von Komponenten ziemlich oft erwähnt haben. Genau das ist die Art von Problem, die ich bei vielen Tutorials, Artikeln und allgemeinen Diskussionen über Zustand festgestellt habe.

In den meisten Fällen verwenden Entwickler "zustandsbehaftete" Klassen, um den Zustand einer Komponente zu verwalten. Aber das ist sehr unzureichend, da **eine Komponente aus mehr besteht als nur aus ihrem Aussehen.** Es gibt zugrunde liegende Semantik, die zusammen mit der visuellen Darstellung der Komponente verwaltet werden muss. Das Versäumnis, diese zugrunde liegenden Semantiken zu verwalten, wird sofort offensichtlich, sobald Sie über Tastatur und/oder Screenreader damit interagieren.

Dies ist ein Artikel darüber, wie der Zustand angemessen vermittelt werden kann, damit Benutzer, über sehende, mausnutzende Benutzer hinaus, mit unseren Benutzeroberflächen interagieren können.

Der Zustand ist mehr als nur das Aussehen

Abgesehen davon, dass CSS verwendet wird, um Inhalte für sehende Benutzer und assistierende Technologien angemessen zu verbergen, hat CSS kaum absichtliche Auswirkungen auf die Semantik oder den zugänglichen Zustand eines Elements. Was ich damit meine, ist, dass CSS außerhalb von Eigenschaften wie dem nicht unterstützten 'speak' ('speak'), Pseudo-Inhalten vor/nach und Medienabfragen zur spezifischen Neugestaltung von Komponenten basierend auf Benutzereinstellungen, wie der Reduced Motion Media Query und anderen vorgeschlagenen User Queries, **CSS allein nicht dazu gedacht ist, die Semantik eines Elements zu ändern**.

Warum bringe ich das alles zur Sprache? Weil die Verwaltung des Zustands allein mit CSS-Klassen größtenteils unzureichend ist, um den Zustand allen Benutzern zu vermitteln. Als Sprache für präsentationszwecke hat das Hinzufügen einer Klasse wie .has-error zu einem Eingabefeld, um die Rahmenfarbe zu einer Rottönung zu ändern, keinen semantischen Wert. Soweit CSS betroffen ist, *Das ist, wie Sie dieses Eingabefeld gestalten wollten. Cool. Ich unterstütze Sie! Bitten Sie mich nur nicht, nach oben im DOM zu gestalten. Da ziehe ich die Grenze, Kumpel...*

Stattdessen sollten wir, um den Zustand zu verwalten und zu vermitteln, Attribute auf den entsprechenden Elementen aktualisieren. Und nein, ich meine nicht data-Attribute. Die bedeuten auch nichts. Wenn wir diesen Ansatz verfolgen, brauchen wir in vielen Fällen keine zustandsbehafteten Klassen mehr, abgesehen von Klassen, die die display-Eigenschaft eines Elements umschalten.

Haben wir vergessen, dass wir mit Attributselektoren gestalten können?

HTML und ARIA haben eine ganze Reihe von Attributen, die verwendet werden sollten, um den aktuellen Zustand einer Komponente angemessen zu vermitteln.

Denken Sie daran, eine Klasse .is-disabled auf Ihrem <button></button> oder <input type="text" /> zu verwenden? Das wird ihn nur visuell deaktivieren. Sie müssen immer noch programmgesteuert Klick- und Tastaturereignisse für dieses Element deaktivieren. Verwenden Sie stattdessen das [disabled]-Attribut, und Sie haben einen CSS-Selektor, um Ihr Element zu gestalten, und der Browser erledigt die gesamte angemessene Arbeit, um dieses Element für Sie zu deaktivieren!

Also statt

input.is-disabled { 
  opacity: .65; 
}

was nur ein Eingabefeld visuell verändert, verwenden Sie

input[disabled] { 
  opacity: .65; 
}

Dies erzielt den gleichen visuellen Effekt wie die Verwendung der Klasse .is-disabled, aber stattdessen nutzen wir den Attributselektor aus dem Attribut, das wir **setzen müssen**, um den aktuellen Zustand an den Browser und die Benutzer zu vermitteln. Alles, ohne die oben genannten zusätzlichen Arbeiten mit JavaScript ausführen zu müssen, um die Eingabe zu deaktivieren, wenn wir nur eine Klasse umschalten würden.

Beispiel: "Aktiv" sein

Um einige tiefere Einblicke zu geben, betrachten wir eine Situation, in der Sie eine Klasse .is-active verwenden könnten. Für verschiedene Komponenten kann "aktiv" etwas völlig anderes bedeuten, weshalb ich den Wunsch nach einer einzelnen, wiederverwendbaren Klassennamen nachvollziehen kann, anstatt zu bestimmen, welches Attribut verwaltet werden muss, um den Zustand angemessen zu vermitteln. Aber die Zustandsverwaltung für Entwickler einfacher zu machen, hilft nicht unbedingt den Benutzern, also machen wir es richtig.

Aktive Navigationslinks

Betrachten wir zunächst die Anzeige des aktuell aktiven Links in einer Navigation. Der folgende Pen enthält zwei Beispiele. Das erste verwendet eine Klasse .is-active, um das aktuelle Navigationselement anzuzeigen. Das zweite verwendet aria-current="page".

Sehen Sie den Pen .is-active vs aria-current=’page’ von Scott (@scottohara) auf CodePen.

Obwohl sie alle genau gleich aussehen, werden Sie, wenn Sie Jaws 18, Voice Over oder NVDA 2017.2 (wenn es veröffentlicht wird) verwenden, um durch das Beispiel zu navigieren, etwas hören wie: "Features, current page." bei der Interaktion mit dem Beispiel, das aria-current verwendet. Schauen Sie sich den Artikel von Léonie Watson über [aria-current] an, um viele weitere Beispiele zu finden, wo dieses Attribut zur Gestaltung anstelle einer Klasse .is-active verwendet werden könnte.

Aktive Buttons

Je nach Zweck des Buttons muss der aktive Zustand des Buttons für Screenreader-Benutzer möglicherweise durch eines der folgenden ARIA-Attribute erweitert werden

  • aria-expanded – gibt an, dass der Button eine andere Komponente in der Benutzeroberfläche steuert und den aktuellen Zustand dieser Komponente wiedergibt.
  • aria-pressed – gibt an, dass der Button ähnlich wie ein Kontrollkästchen funktioniert, in dem sein Zustand zwischen gedrückt und ungedrückt wechselt.

Ohne eines der oben genannten Attribute hat ein Button keine inhärente Möglichkeit, zu kommunizieren, ob er interagiert wurde oder nicht. Das ist völlig in Ordnung, wenn eine Situation es nicht erfordert, aber wenn Sie kommunizieren müssen, dass ein Button aktiviert wurde, dann können wir das hier mit aria-pressed tun

Sehen Sie den Pen Toggle Button Example von Scott (@scottohara) auf CodePen.

Im obigen Beispiel haben wir einen Button, mit dem ein Artikel in einen Warenkorb gelegt werden kann. Um anzuzeigen, wann ein Artikel hinzugefügt wurde, schalten wir anstelle einer Klasse den booleschen Wert des Attributs aria-pressed um und verwenden [aria-pressed="true"] als unseren Styling-Hook, um den aktiven Zustand visuell zu vermitteln. Bei der Interaktion mit dem Button über einen Screenreader wird er als "checked" oder "unchecked", add to cart, toggle button angekündigt.

Für eine eingehende Betrachtung der Überlegungen, die man bei der Entwicklung von zugänglichen Toggle-Buttons berücksichtigen sollte, muss man nicht weiter suchen als Heydon Pickerings Artikel über Toggle Buttons. Heydon legt detailliert dar, warum es keine gute Idee ist, das sichtbare Label des Buttons zu ändern, und weist sogar darauf hin, dass Sie möglicherweise keinen Toggle-Button benötigen, sondern stattdessen einen Schalter in Betracht ziehen sollten.

Zustandsverwaltung für Akkordeons

Für unser letztes Beispiel werfen wir einen Blick darauf, wie wir den Zustand in einer Akkordeon-Komponente verwalten würden.

Sehen Sie den Pen ARIA Accordion Example von Scott (@scottohara) auf CodePen.

Wenn Sie die Kommentare im CSS und JavaScript lesen, werden Sie feststellen, dass diese Demo einige Dinge tut.

Erstens ist das Markup-Muster des Akkordeons so aufgebaut, dass, wenn JavaScript aus irgendeinem Grund deaktiviert wird, kein Inhalt für diese Benutzer unzugänglich ist, da die Panels nur versteckt werden, wenn die Klasse .js vorhanden ist.

Zweitens, um die Notwendigkeit von tatsächlichen <button></button>-Elementen innerhalb jeder Akkordeon-Panel-Überschrift zu umgehen, wandeln wir stattdessen ihre verschachtelten <a>s in "Buttons" um, indem wir die ARIA-Eigenschaft role="button" anwenden und dann die gesamte erwartete Tastaturfunktionalität über den Keydown-Event-Listener hinzufügen. Zusätzlich wurde jedem ARIA-Button ein tabindex="0" zugewiesen, um sicherzustellen, dass der "Button" für Tastaturbenutzer zugänglich ist.

Schließlich verwenden wir hier das Attribut aria-expanded, um den aktuellen Zustand des Akkordeon-Panels zu vermitteln, so dass, wenn ein Benutzer den Akkordeon-Trigger mit einem Screenreader fokussiert, dieser "Accordion Heading, collapsed (or expanded) button" ansagt.

Sie werden feststellen, dass die Akkordeon-Panels eine Klasse .is-active verwenden, um ihren sichtbaren Zustand umzuschalten. Igitt! Aber Moment mal, das ist das Einzige, worauf wir uns bei CSS allein verlassen können, um uns zu helfen. Wenn wir uns die beteiligten Selektoren genauer ansehen

.js .accordion__panel {
  border-bottom: 1px solid;
  overflow: hidden;
  padding: 0 1em;
  max-height: 0px;
  transition:
    max-height .2s ease-in-out,
    visibility .2s ease-in-out;
  visibility: hidden;
}

.js .accordion__panel.is-active { 
  max-height: 100vh;
  visibility: visible;
}

Der erste Selektor, der von der Verfügbarkeit von JavaScript abhängt, verwendet visibility: hidden, um die Inhalte des Panels sowohl für sehende Benutzer als auch für Benutzer assistierender Technologien inklusiv zu verstecken. Die Eigenschaften overflow, max-height und transition werden dann gesetzt, um das Panel zu komprimieren und es auf seine erweiterte Form vorzubereiten, sobald die Klasse .is-active zum Akkordeon-Panel hinzugefügt wird. Ich hätte stattdessen display: none umschalten oder das Attribut hidden programmgesteuert zu den Panels hinzufügen und entfernen können, aber wir hätten die Möglichkeit verloren, das Panel zu animieren. Und jeder mag eine gute Animation, oder?

Zum Abschluss

Das Wichtigste, was Sie von all dem mitnehmen sollten, ist, dass Sie den Zustand wahrscheinlich nicht angemessen an Benutzer assistierender Technologien vermitteln, wenn Sie nur Klassen umschalten, um den Zustand Ihrer Komponenten visuell zu verwalten.

Sie müssen die entsprechenden Elemente verwenden (<button></button> sind Ihr Freund!) und die entsprechenden Attribute und ihre Werte verwalten, um wirklich zugängliche Benutzererlebnisse zu schaffen. Sicher, Sie könnten diese Dinge tun *und* weiterhin zustandsbehaftete Klassen umschalten, um Ihr Styling zu steuern. Aber wenn wir Attribute und ihre Werte aktualisieren müssen und diese ebenfalls gültige CSS-Selektoren sind, warum sollten wir dann mehr Arbeit leisten, als nötig ist, indem wir auch Klassen umschalten?