Modální okno (nebo dialog) je skvělý pomocník, pokud ho použiješ správně. Můžeš s ním upozornit na důležitou informaci, požádat o potvrzení nebo nabídnout krátký formulář, aniž bys opustil aktuální stránku. Stejně snadno se ale může stát noční můrou, pokud je použito špatně nebo nadměrně.
Kdy vůbec použít modální okno
Modál má smysl, když jde o krátkou, navazující akci: potvrzení destruktivního kroku („Opravdu smazat?“), přihlášení, výběr jedné volby nebo krátký doplňkový formulář. Všude jinde je bezpečnější samostatná stránka – zachováš tok, adresu a historii. WCAG 2.2 sice neříká „kdy modál“, ale požaduje, aby byl ovladatelný klávesnicí, bez pasti a s logickým pořadím fokusu.
Struktura a značení
Dialog nech v DOMu skrytý a při otevření mu dej jasný název i popis. V praxi se osvědčuje role="dialog" a napojení titulku a popisu přes aria-labelledby a aria-describedby. Atribut aria-modal="true" dá čtečkám najevo, že zbytek stránky je dočasně mimo hru.
Doporučení: Tlačítku, které dialog otevírá, přidej aria-haspopup="dialog". Čtečka pak oznámí, že po aktivaci tlačítka se otevře dialog, nikoli nová stránka. Volitelně můžeš přidat i aria-controls="id-dialogu", které vyjadřuje vztah mezi tlačítkem a cílovým dialogem.
<button id="open" aria-haspopup="dialog" aria-controls="dialog">
Otevřít dialog
</button>
<div id="dialog"
role="dialog"
aria-modal="true"
aria-labelledby="dialog-title"
aria-describedby="dialog-desc"
hidden>
<h2 id="dialog-title" tabindex="-1">Opravdu smazat položku?</h2>
<p id="dialog-desc">Tuto akci už nepůjde vrátit.</p>
<button id="confirm">Smazat</button>
<button id="cancel">Zrušit</button>
</div>
Fokus a ovládání klávesnicí
WCAG 2.2 neurčuje, kam přesně máš po otevření dialogu přesunout fokus. Důležité je, aby uživatel hned pochopil, co se stalo, a měl kontrolu nad situací.
U informačních nebo formulářových dialogů je nejlepší zaměřit viditelný titulek – čtečka hned oznámí název a uživatel ví, kde se nachází. U krátkých potvrzovacích dialogů může být praktičtější zaměřit tlačítko Zrušit nebo Zavřít, aby měl člověk okamžitou možnost modal opustit.
V obou případech zůstává pohyb klávesami Tab/Shift+Tab uvnitř okna, Esc dialog zavře a po zavření se fokus vrátí na prvek, který ho otevřel.
Uzamčení pozadí
Když je dialog otevřený, neměl bys být schopen interagovat se zbytkem stránky. Nejjednodušší je použít atribut inert na kontejner, který obsahuje hlavní obsah, a zároveň zamknout posouvání pomocí body { overflow: hidden; }. Pokud inert není podporován, použij fallback s aria-hidden="true" a nezapomeň ho po zavření odstranit.
Kliknutí mimo a zavírání
Kliknutí mimo dialog může být užitečné, ale jen u informačních modálů. U formulářů nebo destruktivních akcí tím může dojít ke ztrátě dat, proto raději vyžaduj explicitní potvrzení tlačítkem Zrušit nebo Zavřít.
Vícekrokové dialogy
Pokud se dialogy řetězí, snaž se zachovat konzistentní orientaci a návrat fokusu. U delších procesů je ale lepší samostatná stránka. Pokud kroky v modálu opravdu dávají smysl, označ je například „Krok 2 ze 3“ přímo v titulku.
Jak řešit mobilní zobrazení a animace
Dialog se musí vejít na obrazovku bez horizontálního posuvu. Animace by měly respektovat nastavení sníženého pohybu. Pokud má uživatel v systému omezené animace, otevři a zavři dialog bez přechodů.
Testování přístupnosti dialogu
- Klávesnice: otevři dialog tlačítkem (Enter/mezerník), pohybuj se pomocí Tab/Shift+Tab, zavři ho Esc a ověř, že fokus se vrátil na spouštěcí prvek.
- Čtečky: v NVDA, JAWS nebo VoiceOver musí čtečka oznámit „dialog“ a jeho titulek, nesmí číst obsah webu na pozadí / za modalem.
- Mobil: zkontroluj reflow, čitelnost textu a možnost zavřít dialog i bez myši.
Nejčastější chyby
- Chybí titulek a
aria-labelledby. - Fokus po otevření zůstává na pozadí nebo se ztrácí.
- Tab prochází mimo dialog.
- Esc nefunguje.
- Kliknutí mimo způsobí ztrátu dat.
- Pozadí není uzamčené a zůstává interaktivní.
Checklist před nasazením
- Tlačítko pro otevření má
aria-haspopup="dialog"(a volitelněaria-controls). - Dialog má viditelný titulek propojený s
aria-labelledby. - Má nastaveno
aria-modal="true"a případněaria-describedby. - Při otevření se fokus přesune dovnitř, po zavření zpět na spouštěcí prvek.
- Tab/Shift+Tab cyklují jen uvnitř; Esc zavírá.
- Pozadí je uzamčené (
inertneboaria-hidden). - Chybové hlášky jsou textové a čitelné pro čtečky.
- Dialog je responzivní bez horizontálního posuvu.
- Animace respektují nastavení „snížený pohyb“.
- Otestováno klávesnicí, NVDA/VoiceOver a na mobilu.
Závěr
Přístupný dialog není složitý, pokud víš, co hlídat. Klíčem je předvídatelnost: čtečka má oznámit, co se děje, fokus se má držet uvnitř a uživatel má mít jistotu, že se dostane zpět tam, odkud přišel. Stačí pár atributů a trocha testování – a modální okna budou fungovat pro všechny.