Wpadki i wypadki #14

A oto i kolejne Wpadki i wypadki! Dzisiaj o tzw. off-canvas menu, czyli menu schowanym za krawędzią ekranu, które wjeżdża po naciśnięciu przycisku.

Wyobraźmy sobie, że mamy proste menu schowane za krawędzią ekranu. I wszystko niby działa, ale, niestety, nasze menu ma dwa problemy związane z dostępnością – jeden duży i jeden mniejszy. Przyjrzyjmy się im pokrótce.

Mniejszym problemem jest zdecydowanie to, że nasze menu nie uwzględnia preferencji użytkownika co do ograniczania ruchu. Na szczęście można to bardzo łatwo rozwiązać, rozbijając pierwotny kod CSS dla elementu .container na dwie części. Tę drugą część otoczymy w media query prefers-reduced-motion i włączymy w niej animację:

.container {
	min-height: 100vw;
	display: grid;
	grid-template-columns: 300px 1fr;
	transform: translateX( -300px );
}

@media ( prefers-reduced-motion: no-preference ) {
	.container {
		transition: transform 0.5s ease-in-out;
    }
}

Dzięki takiemu zapisowi animacja wjeżdżania menu pokaże się tylko tym użytkownikom, którym animacje nie przeszkadzają. Pozostałym użytkownikom, którzy animacji sobie nie życzą, menu pokaże się natychmiast po naciśnięciu przycisku.

Zdecydowanie większym problemem jest fakt, że tego typu menu psuje nawigację klawiaturą, co spowodowane jest sposobem, w jaki zostało ukryte. Istnieje kilka sposobów na ukrywanie treści. Przesunięcie elementu poza ekran schowa go wyłącznie dla użytkowników, którzy nie posługują się technologią asystującą (np. czytnikami ekranu) oraz nawigują przy pomocy dotyku lub myszki. Niemniej dla użytkowników czytników ekranowych i osób nawigujących klawiaturą menu będzie cały czas dostępne. Można to prosto sprawdzić na przykładowym menu: sfocusowanie przycisku otwierającego menu, a następnie naciśnięcie Tab przerzuci focus do menu – nawet wówczas, gdy jest ono zwinięte. Najprostszym rozwiązaniem byłaby zmiana sposobu ukrywania menu, ale to z kolei mogłoby sprawiać problemy przy próbie animacji jego wjeżdżania. Dlatego można zastosować dwa hacki. Pierwszy z nich polega na ukryciu zwiniętego menu przed technologią asystującą przy pomocy [aria-hidden=true]. Drugi hack polega na dodaniu do wszystkich linków w menu [tabindex=-1], dzięki czemu nie będzie się można do nich dostać przy pomocy nawigacji klawiaturą. Oczywiście obydwa hacki należy wyłączać w chwili, gdy menu będzie pokazane na ekranie.

Wypada się też zastanowić, co w przypadku, gdy użytkownikowi nie zadziała JS. Można wówczas posłużyć się techniką z klasą .no-js i po prostu na stałe pokazać menu. Innym sposobem jest zastąpienie przycisku linkiem, który stawałby się przyciskiem dopiero po wczytaniu się JS-a.

Na CodePenie można zobaczyć przykładowe menu z dodanymi opisanymi wyżej usprawnieniami. W komentarzu jest też ulepszona wersja.

17 komentarzy do “Wpadki i wypadki #14”

  1. Niestety bez JS ta nawigacja w ogóle nie działa, nie rozsuwa się. Należałoby stworzyć nawigację działajaca w czystym CSS a JS jako ulepszenie dla czytników a nie konieczność.

        1. Ok, ale ono powinno być wysunięte domyślnie i wyłączenie JS-a nie powinno tego zmienić. Rozumiem, że po prostu skopiowałeś kod HTML i CSS, bez kopiowania kodu JS?

          Można nieco zmienić podejście i przerzucić wywalanie klasy .container_no-js bezpośrednio do kodu menu (https://codepen.io/Comandeer/pen/OJjONBz ) – co może jednak powodować mały FOUC.

          Co do wersji z :target, o której wspominam w poście, wyglądałaby mniej więcej tak: https://codepen.io/Comandeer/pen/jOLaqXe – tutaj pojawia się problem z tym, że focus zaczyna działać niewłaściwie (po otwarciu menu się „gubi”), dlatego też wersja z rozwiniętym menu jest IMO lepsza. Dodatkowo dochodzi do tego fakt, że trzeba dodać drugi element do zamknięcia menu, bo przy :target można je tylko otworzyć.

          Można też spróbować z :checked → https://codepen.io/Comandeer/pen/KKvyMPE – chyba działa lepiej niż :target. Chociaż ma gorszą semantykę + trzeba się trochę napocić, żeby focus był widoczny.

          Obydwa sposoby jednak mają jedną, poważną wadę: menu jest dostępne także, gdy jest zwinięte. Prawdopodobnie trzeba by się pobawić dodatkowo animowaniem visibility: hidden, żeby nawigacja klawiaturą była poprawna. Coś typu https://codepen.io/Comandeer/pen/oNeoLLW – co pozwala uprościć kod JS i wywalić całą obsługę dla [tabindex=-1] i [aria-hidden=true].

          1. „Rozumiem, że po prostu skopiowałeś kod HTML i CSS, bez kopiowania kodu JS?”

            Skopiowałem wszystko, łacznie z JS. I działa OK. Po wyłaczeniu JS – nie działa nic.

            Powiem Ci szczerze że nawigacja która spełnia wszystkie standardy jest bardzo trudna do stworzenia.

            Ostatnio przestudiowałem ten art.: https://css-tricks.com/in-praise-of-the-unambiguous-click-menu/

            Włosy na głowie siwieją ile nawigacja musi spełnić warunków aby zadowolić wszystkich.

            Ni emam pojęcia kto obecnie ma perfekcyjna nawigację, to jest złozony temat.

            Nawet Ty się w tym, jak widać, miotasz tworząc kilka wersji i licząc się z plusami i minusami danego rozwiązania.

          2. Nawet Ty się w tym, jak widać, miotasz tworząc kilka wersji i licząc się z plusami i minusami danego rozwiązania.

            Bo nie ma idealnego rozwiązania, każde będzie w czymś lepsze od innych, a równocześnie w czymś będzie gorsze. Trzeba po prostu dobrać rozwiązanie najlepiej pasujące do konkretnego projektu, np. w aplikacji, która i tak nie działa bez JS-a, nie ma sensu się bawić w dodanie rozwiązania pod case, gdy user nie ma/nie działa mu JS.

  2. „(…) nie ma sensu się bawić w dodanie rozwiązania pod case, gdy user nie ma/nie działa mu JS.”

    Przeciez niejednokrotnie przytaczasz link do artykułu w którym jest wyjaśnione że JS może zawieśc.

    Ja myślę tak. Oczywiście aplikacja bez JS i tak nie będzie działać, ale przynajmniej można miec dobre podstawy i nawigacja bez JS mimo wszystko MOŻE działać.

    Tak wiec nie widzę powodu dla którego trzeba ja od JS uzależnić.

    ZAŁOZENIA:
    1) Nawigacja musi mieć JS aby zadowolić czytniki ekranu (zwinieta/rozwinieta) i obsłuzyć działanie klawiatury, np. przeskakiwanie działów z podlinkami bo bez JS lecimy po wszystkim jak leci co jest uciążliwe.

    2) Nawigacja mimo że musi mieć JS to powinna działać RÓWNIEŻ bez niego. bo dlaczego by od takiego załozenia jako podstawy nie zacząć?

    Drobna uwaga co do Twojego PENA https://codepen.io/Comandeer/pen/oNeoLLW

    Nie jest dobre uzaleznienie tej nawigacji od takich powszechnie znanych klas jak: .content .container – wstawiłem tą nawigację do swojego projektu w którym mam te klasy i zgadnij co sie z nim stało? No właśnie 😉

    1. Nie jest dobre uzaleznienie tej nawigacji od takich powszechnie znanych klas jak: .content .container – wstawiłem tą nawigację do swojego projektu w którym mam te klasy i zgadnij co sie z nim stało? No właśnie 😉

      To jest przykład, nie gotowy komponent do wstawienia na stronę.

      Przeciez niejednokrotnie przytaczasz link do artykułu w którym jest wyjaśnione że JS może zawieśc.

      Tak. Ale są aplikacje, które bez JS-a nie mają sensu, np. edytor grafiki (Photopea choćby). Więc skoro i tak taka aplikacja nie będzie działała bez JS-a, nie ma sensu dodawać małego elementu, który będzie. Z biznesowego punktu widzenia to jest strata czasu, zwłaszcza, że i tak prawdopodobnie użytkownik dostanie komunikat, że aplikacja nie działa bez JS-a.

      Ja myślę tak. Oczywiście aplikacja bez JS i tak nie będzie działać, ale przynajmniej można miec dobre podstawy i nawigacja bez JS mimo wszystko MOŻE działać.

      Tak, pod warunkiem, że będzie odsyłać usera do czegokolwiek działającego. Bo jeśli nic innego nie będzie działać, to sama nawigacja IMO średnio ma sens.

        1. Trochę sztuka dla sztuki jak dla mnie i VoiceOver nieco się gubi przy focusie (np. nie czyta ponownie sfocusowanego przycisku po otwarciu menu). Nie użyłbym też [role=menu], bo ono nie służy do tworzenia menu nawigacyjnych a aplikacyjnych – co zresztą Heydon dokładnie opisuje w artykule, z którego pochodzi ten kod. Wspomina też o tym, że to rozwiązanie jest hakiem, który m.in. nie pozwala na poprawną implementację nawigacji klawiaturą:

          This is clever and all, but our menu button is still incomplete since the expected focus behaviors we’ve been discussing simply cannot be implemented without JavaScript.

          [To sprytne i wgl, ale nasz przycisk menu jest wciąż niekompletny, ponieważ oczekiwane przez użytkownika zachowania związane z focusem po prostu nie mogą być zaimplementowane bez JS-a.]

          1. „który m.in. nie pozwala na poprawną implementację nawigacji klawiaturą:”

            Mógłbys proszę wyjaśnić dlaczego?

            Ja normalnie używam na tej nawigacji TAB, zaznacza mi się MENU po czym wciskam spację i się wysuwa i dalej TABem jadę.

            Jaki byś kawałek JS do tego dopisał aby zaspokoic klawiaturę? Mógłbyś prosze dopisac ten kodzik?

  3. Dzięki za poświecony czas i odpowiedzi.

    Mam jeszcze pytanie co dop prefers-reduced-motion.

    Czy nie wygodniej użyć jednego ogólnego zapisu?

    @media screen and (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
    scroll-behavior: auto !important;
    animation: none !important;
    transition: 0s !important;
    animation-delay: -1ms !important;
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0s !important;
    transition-delay: 0s !important;
    }
    }

    1. Zależy w sumie od przypadku i podejścia. Taka ogólna regułka się sprawdzi, jeśli nie mamy aplikacji podzielonej na komponenty. Ale gdy już mamy komponenty (albo wgl design system), to wówczas takie rzeczy powinny być ustalane dla każdego komponentu osobno.

      No i zauważ, że w tym przykładzie usuwasz animacje, gdy user sobie ich nie życzy, a ja dodaję je, gdy user nie ma nic przeciwko. I jak dla mnie takie podejście (dodawanie) jest nieco bardziej czytelne – nawet jeśli wymaga dodanie nieco większej ilości kodu.

      1. To się nam watek rozwinął 🙂

        Wziąłem na warsztat tą konkretną propozycje Twojego menu:

        https://codepen.io/Comandeer/pen/oNeoLLW
        i jeżeli się nie mylę to chyba nie działa to za pomocą strzałek.

        Czyli nawigację mogę za pomocą Taba zaznaczyć następnie rozsunąć jaą Enterem lub Spacją ale po linkach nie można nawigować strzałakmi tylko Tabem.

        Czy to jest właściwe rozwiązanie?

        1. To jest menu nawigacyjne, przeznaczone do nawigowania po stronie – a takie menu nie musi (czy wręcz nie powinno) być obsługiwane przy pomocy strzałek. Przy pomocy strzałek obsługiwane są menu aplikacyjne ([role=menu]) – czyli np. menu w przeglądarce. To są dwie różne rzeczy.

          1. Wielkie dzięki za wiedzę którą tutaj przekazujesz.

            Sporo dzięki Tobie sie nauczyłem.

            Mam nadzieję że inni z tej dyskusji również wyniosą dla siebie wartość i sieć dzięki temu bedzie lepsza!

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.