blog na temat dostępności stron internetowych

Przejdź do głównej treści

Przyciski “Pokaż / Schowaj”

Jednymi z najczęściej używanych dynamicznych komponentów są przyciski umożliwiające pokazanie i schowanie fragmentów stron, na przykład przycisk kontrolujący główne menu strony (zwykle stosowany na mniejszych ekranach – telefonach i tabletach). Element ten, choć teoretycznie prosty w implementacji, to często prawdziwe pole minowe jeśli chodzi o dostępność. Przyjrzyjmy sie zatem na co należy zwracać uwagę tworząc tego typu komponent.

Jak stworzyć dostępny przycisk “Pokaż / Schowaj”?

Najważniejsze zalecenia można podsumować w ośmiu punktach. Poniżej opiszę je na przykładzie przycisku “Menu”, kontrolującego wyświetlanie głównego menu strony.

1. Zastosuj semantycznie poprawny element HTML

Jak zawsze – semantyka to podstawa. Choć logiczne wydaje się, że do oznaczenia przycisku najlepiej użyć element <button>, często spotyka się przyciski zbudowane na bazie niesemantycznych elementów, takich jak <span> czy <div>. Czasem deweloperzy stosują też element <a>, pomimo że przycisk nie pełni roli odnośnika.

<!-- Zalecany element HTML -->
<button type="button"></button>

Jeśli jest to konieczne, można posłużyć się elementem <span> bądź <div> z nadanym mu atrybutem ARIA role="button" (zgodnie z pierwszą zasadą stosowania ARIA nie jest to jednak zalecane rozwiązanie):

<!-- Alternatywne rozwiązanie -->
<span role="button"></span>

Powiązane kryteria sukcesu WCAG 2.0: 1.3.1 Informacje i relacje, 4.1.2. Nazwa, rola, wartość.

2: Przycisk musi posiadać etykietę

Etykieta jest to tekst opisujący funkcję danego elementu interaktywnego, takiego jak przycisk czy pole formularza. Pozbawiony tekstu element <button> zostanie przedstawiony osobom korzystającym z czytnika ekranu jako… przycisk – bliżej nieokreślony, pełniący zagadkową funkcję. Komponent ten będzie też trudniej dostępny dla osób posługujących się oprogramowaniem do rozpoznawania mowy.

Tekst opisujący funkcję przycisku najlepiej umieścić pomiędzy otwierającym i zamykającym tagiem elementu <button>:

<button type="button">Menu</button>

lub ewentualnie zamieścić  go w atrybucie aria-label:

<button aria-label="menu" type="button"></button>

Etykieta może też się dynamicznie zmieniać, w zależności od aktualnego stanu menu, np:

<!-- Menu jest schowane -->
<button type="button">Otwórz menu</button>

<!-- Menu jest pokazane -->
<button type="button">Zamknij menu</button>

Często na stronach spotyka się przyciski menu, które nie posiadają widocznego tekstu, a jedynie ikonkę (z reguły 3 poziome linie, tzw. hamburger). Jest to dopuszczalne rozwiązanie pod warunkiem, że przycisk posiada etykietę, jest ona jedynie w odpowiedni sposób ukryta (więcej informacji na ten temat można znaleźć w artykule “Ukrywanie elementów strony”).

Warto jednak pamiętać, że choć dla większości młodych osób (bądź osób często korzystających z internetu) jest to dobrze znana konwencja, istnieją grupy użytkowników, które mogą mieć problem ze zrozumieniem funkcji przycisku pozbawionego widocznej etykiety. Wszystko zależy od docelowej grupy odbiorców; jeśli tworzymy stronę skateparku, ikonka zapewne wystarczy. W przypadku strony osiedlowego klubu seniorów, warto będzie pokazać również tekst.

Powiązane kryteria sukcesu WCAG 2.0: 2.4.6 Nagłówki i etykiety, 4.1.2 Nazwa, rola, wartość.

3. Przycisk musi otrzymywać fokus klawiatury

Innymi słowy musi być możliwy dostęp do niego bez myszy, za pomocą klawisza Tab.
Jeśli, zgodnie z zaleceniami z punktu 1., posłużyliśmy się elementem <button>, nie musimy się o to martwić – każdy <button> otrzymuje fokus klawiatury. Jeśli natomiast oznaczyliśmy przycisk za pomocą nieinteraktywnego elementu takiego jak <span> czy <div>, musimy dodać do niego atrybut tabindex="0":

<span role="button" tabindex="0">Menu</span>

Powiązane kryteria sukcesu WCAG 2.0: 2.1.1 Klawiatura.

4: Widoczny fokus klawiatury

Gdy przycisk “Pokaż / Schowaj” posiada fokus klawiatury, musi być to dla osób korzystających z klawiatury oczywiste; przycisk ten powinien znacząco zmienić swój wygląd, np. poprzez zmianę koloru czy też obramowania.

// Przycisk bez fokusa ma szare obramowanie i tło oraz biały tekst
.button--show-hide {
  background-color: #555;
  border: 2px solid #555;
  color: #fff;
}

// Przycisk z fokusem zmienia kolor tła na biały a kolor tekstu na szary
.button--show-hide:focus {
 background-color: #fff;
 color: #555;
}

Powiązane kryteria sukcesu WCAG 2.0: 2.4.7 Widoczny fokus.

5. Musi być możliwe aktywowanie przycisku za pomocą klawiatury

Gdy przycisk posiada fokus klawiatury i użytkownik naciśnie klawisz Spacji, Enter lub Return, przycisk ten musi zostać aktywowany (kontrolowana przez niego sekcja musi zmienić swój stan – zostać pokazana lub schowana). Innymi słowy cokolwiek dzieje się, gdy klikniemy na przycisk, musi się też wydarzyć, gdy aktywujemy przycisk z poziomu klawiatury.

Powiązane kryteria sukcesu WCAG 2.0: 2.1.1 Klawiatura.

6. Zapewnij logiczną kolejność fokusa klawiatury

Gdy użytkownik aktywuje przycisk i odpowiednia sekcja zostaje pokazana, pierwszym elementem, który otrzyma fokus klawiatury jako następny, musi być pierwszy interaktywny element wewnątrz nowo pokazanego obszaru. Brzmi skomplikowanie? Posłużmy się przykładem przycisku Menu. Jeśli osoba posługująca się klawiaturą  aktywuje ten przycisk, a następnie naciśnie klawisz Tab, fokus klawiatury powinien zostać przeniesiony do pierwszego linku wewnątrz menu.

Może być to osiągnięte na dwa sposoby :

  •  przycisk kontrolujący dany obszar może znajdować się bezpośrednio przed tym obszarem (wtedy automatycznie pierwszy interaktywny element zawarty w nim będzie pierwszym w kolejności fokusa) – zalecane rozwiązanie
  • fokus klawiatury może być odpowiednio kontrolowany przy pomocy JavaScript. Czyli: kiedy menu zostaje pokazane, fokus klawiatury musi zostać do niego przeniesiony, a gdy zostaje ukryte, fokus musi zostać przeniesiony z powrotem do przycisku “Menu”.

Dlaczego jest to istotne? Gdy warunek ten nie jest spełniony, osobom niewidomym i tym korzystającym z klawiatury trudno będzie uzyskać dostęp do zawartości menu. Widziałam kilka stron, w których kodzie HTML menu zamieszczone jest na samym końcu, pomimo że kontrolujący je przycisk znajduje się na początku strony. Gdy osoba posługująca się klawiaturą aktywuje przycisk “Menu”, główna nawigacja zostaje wyświetlona na górze strony, choć w kodzie występuje za stopką. Gdy użytkownik naciśnie klawisz Tab, będzie oczekiwać, że fokus klawiatury zostanie przeniesiony do pierwszego linku w menu. Tak się jednak nie dzieje. Osoba ta będzie zmuszona do “przejścia” przez wszystkie interaktywne elementy na stronie zamieszczone pomiędzy przyciskiem a menu, zanim dotrze do nawigacji. Jest to jeszcze gorsze rozwiązanie dla osób niewidomych. Osoby te, gdy nie napotkają menu bezpośrednio za kontrolującym je przyciskiem, będą mieć problem ze znalezieniem go.

Powiązane kryteria sukcesu WCAG 2.0: 2.4.3 Kolejność fokusa.

7. Dodaj atrybuty ARIA

Stworzony zgodnie z powyższymi zasadami przycisk “Pokaż / Schowaj” spełnia niemal wszystkie zasady dostępności. Jest jednak wciąż mało przyjazny osobom korzystającym z czytników ekranu. Dlaczego?

Choć osoby te będą w stanie przenieść fokus do przycisku, zostaną poinformowane o jego roli (“Otwórz menu”) i będą w stanie go aktywować (otworzyć menu), czytniki ekranu nie ogłoszą automatycznie zmiany stanu dynamicznego obszaru z nawigacją. Choć przycisk zadziałał i na ekranie pojawiło się menu, czytnik ekranu nie poinformuje o tym użytkownika.

Aby lepiej przekazać osobom niewidomym, jaka jest funkcja tego typu przycisku i aktualny stan kontrolowanej przez niego sekcji, możemy posłużyć się następującymi atrybutami ARIA:

aria-expanded

Atrybut aria-expanded informuje użytkownika, jaki jest aktualny stan sekcji kontrolowanej przez przycisk z tym atrybutem. aria-expanded="true" oznacza, że sekcja jest widoczna, zaś aria-expanded="false" że sekcja jest ukryta. Wartość tego atrybutu musi być zatem dynamicznie aktualizowana za pomocą JavaScript.

<!-- Menu jest schowane -->
<button aria-expanded="false" type="button">Otwórz menu</button>
<ul class="hidden">
  [...]
</ul>

<!-- Menu jest widoczne -->
<button aria-expanded="true" type="button">Zamknij menu</button>
<ul>
 [...]
</ul>

Choć może wydawać się to dość nielogiczne, atrybut ten powinien byc zamieszczony na przycisku, a nie odpowiadającej mu sekcji (choć to nie stan przycisku się zmienia). Dzięki temu aktualny stan dynamicznego obszaru jest automatycznie ogłaszany przez czytniki ekranu ilekroć ulega on zmianie. Np. gdy osoba korzystająca z czytnika ekranu przejdzie do przycisku “Menu”, zostanie automatycznie poinformowana, że sekcja jest schowana. Gdy przycisk zostanie aktywowany, czytnik ekranu natychmiast poinformuje użytkownika, że sekcja jest teraz widoczna.

aria-controls

Atrybut ten stwarza w kodzie strony związek pomiędzy przyciskiem a kontrolowanym przez niego obszarem. Choć większość czytników ekranu nie przekazuje niestety tej zależności użytkownikom, niektóre (JAWS) to robią, dlatego nie zaszkodzi go mimo wszystko dodać:

<button aria-controls="main-menu" aria-expanded="false" type="button">Otwórz menu</button>
<ul id="main-menu">
  [...]
</ul>

Powiązane kryteria sukcesu WCAG 2.0: 4.1.2. Nazwa, rola, wartość.

8. Nie polegaj na JavaScript

Nie chodzi mi tu o to, że nie należy stosować JavaScript do zbudowania tego typu komponentu. Chodzi o to, by (zgodnie z zasadą “Progressive enhancement“) upewnić się, że zawartość dynamicznie pokazywanej sekcji jest dostępna również wtedy, gdy obsługa JavaScript jest wyłączona bądź nie działa.

Gdy JavaScript (z tego czy innego powodu) nie działa, przycisk powinien być nieobecny na stronie (gdyż będzie wtedy bezużyteczny), a zawartość menu musi być pokazana – w przeciwnym wypadku użytkownicy nie będą mieć do niego dostępu.
Najlepiej, by przycisk ten nie istniał w HTML dopóki nie zostanie dodany przy pomocy skryptu użytego do implementacji przycisku “Pokaż / Schowaj”. Gdy JavaScript nie działa, przycisk ten nie zostanie po prostu dodany do DOM. Również menu powinno być widoczne dopóki nie zostanie schowane przez tenże skrypt.
Alternatywnym rozwiązaniem jest zamieszczenie przycisku bezpośrednio w kodzie HTML i ukrycie go przy pomocy CSS, gdy JavaScript nie działa.

Końcowy przykład

Oto przykładowa implementacja przycisku Menu. Warto zwrócić uwagę na położenie przycisku: jest on zamieszczony wewnątrz elementu <nav>, który jest zawsze widoczny – chowana jest jedynie lista zawierająca linki. Dzięki temu nawet wtedy, gdy menu jest ukryte, osoby niewidome korzystające z nawigacji przy pomocy regionów będą w stanie łatwo znaleźć przycisk kontrolujący główne menu strony.

<!-- Menu jest schowane -->
<nav class="main-menu">
  <button aria-controls="main-menu" aria-expanded="false" class="main-menu__button" type="button">Otwórz menu</button>
  <ul class="main-menu__items main-menu__items--hidden" id="main-menu">
    [...]
  </ul>
</nav>

<!-- Menu jest widoczne -->
<nav class="main-menu">
  <button aria-controls="main-menu" aria-expanded="true" class="main-menu__button" type="button">Zamknij menu</button>
  <ul class="main-menu__items" id="main-menu">
    [...]
  </ul>
</nav>

Kod:

HTML

<nav class="main-menu">
  <ul class="main-menu__items" id="main-nav">
    <li><a href="#">Link 1</a></li>
    <li><a href="#">Link 2</a></li>
    <li><a href="#">Link 3</a></li>
  </ul>
</nav>

CSS:

.main-menu__button {
  background-color: #555;
  border: 2px solid #555;
  color: #fff;
}

.main-menu__button:focus {
  background-color: #fff;
  color: #555;
}

.main-menu__items--hidden {
  display: none;
}

JavaScript:

const menu = document.querySelector('#main-nav');
const showHideButton = document.createElement('button');

showHideButton.setAttribute('type', 'button');
showHideButton.setAttribute('aria-controls', 'main-nav');
showHideButton.classList.add('main-menu__button');
menu.parentNode.insertBefore(showHideButton, menu);

function showMenu() {
  showHideButton.innerHTML = 'Zamknij menu';
  showHideButton.setAttribute('aria-expanded', 'true');
  menu.classList.remove('main-menu__items--hidden');
}
 
function hideMenu() {
  showHideButton.setAttribute('aria-expanded', 'false');
  showHideButton.innerHTML = 'Otwórz menu';
  menu.classList.add('main-menu__items--hidden');
}
 
showHideButton.addEventListener('click', function() {
  if (menu.classList.contains('main-menu__items--hidden')) {
    showMenu();
  } else {
    hideMenu();
  }
}, hideMenu());

Przykłady z Internetu

Bootstrap 4

Jednym z najpopularniejszych front-endowych frameworków jest Bootstrap. Sprawdźmy zatem jak zaimplementowany został tam przycisk Menu (komponent navbar) i czy spełnia opisane powyżej zasady.

  1. Czy przycisk jest oznaczony przy pomocy poprawnego elementu?
    Tak, <button>.
  2. Czy posiada etykietę?
    Tak. Poprawnie ukryty tekst “Toggle navigation“.
    Przy okazji warto wspomnieć, że często na stronach nieanglojęzycznych etykieta ta nie jest przetłumaczona na język strony. Jako że tekst ten nie jest widoczny, wielu autorów stron zapomina o przetłumaczeniu go – “Toggle navigation” nie jest poprawną etykietą dla polskich użytkowników!
  3. Czy otrzymuje fokus klawiatury?
    Tak, jak każdy element <button>.
  4. Czy posiada widoczny fokus klawiatury?
    Tak. Przycisk zmienia kolor tła gdy otrzymuje fokus klawiatury (choć warto by wzmocnić ten efekt, na przykład poprzez dodanie wyraźnego obramowania).
  5. Czy jest możliwe aktywowanie przycisku za pomocą klawiatury?
    Tak. Za pomoca Spacji i przycisku Enter / Return.
  6. Czy jest zachowana logiczna kolejność fokusa?
    Nie.
    Element wyświetlony przed przyciskiem otrzymuje fokus po nim. Gdy osoba korzystająca z klawiatury aktywuje przycisk i menu pojawia się na ekranie, kolejnym elementem otrzymującym fokus klawiatury jest element Brand, a nie pierwszy link w nowo pokazanym obszarze.
  7. Czy przycisk posiada odpowiednie atrybuty ARIA?
    Tak, “aria-expanded“, dynamicznie zmieniający swą wartość w zależności od aktualnego stanu menu.
  8. Czy menu jest dostępne, gdy JavaScript jest wyłączone?
    Nie. Menu jest niewidoczone, a przycisk “Pokaż / Schowaj” (oczywiście) nie działa.

Ogólnie rzecz biorąc nie jest zatem źle, ale warto naprawić błędy z punktu 7 i 8.

Przykład niedostępnego przycisku “Pokaż / Schowaj”

Dla porównania przycisk “Menu” na stronie Empiku łamie absolutnie wszystkie wymienione wyżej zasady…

<div class="empikNav__mobile" data-ng-click="showMobileMenu = !showMobileMenu">
  <i class="fa fa-bars" aria-hidden="true"></i>
  <i class="fa fa-times" aria-hidden="true"></i>
</div>

Dodaj komentarz

Należy wypełnić wszystkie pola oznaczone asteryskiem (*).
Zanim komentarz zostanie opublikowany będzie musiał zostać zatwierdzony przez moderatora.

Możesz wykorzystać następujące elementy HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>