Към съдържанието
wannacode

Достъпност

Накратко: Достъпността (accessibility) значи страницата да работи и с клавиатура, и с екранен четец: браузърът строи от HTML дърво с роля, име и състояние на всеки елемент.

Проблем

Бутонът, който не е бутон

Страницата на продукта работи: кликаш „Добави в кошницата“ и продуктът влиза в кошницата. Клиент пише, че не може да купи нищо. Той не ползва мишка: движи се с клавиатурата, а екранният четец му казва какво има на страницата.

HTML, CSS, JS

Кликни в полето „Количество“ и натисни Tab. Фокусът прескача „Добави в кошницата“ и отива на сърцето. Сърцето е бутон, но екранният четец казва само „бутон“: какво прави, не се знае. Двата елемента изглеждат наред. Грешката е в това, което браузърът не рисува.

Идеята

Второто дърво

От DOM браузърът строи още едно дърво: дървото на достъпността (accessibility tree). То е страницата, както я виждат хората и програмите, които не гледат екрана:

  • екранният четец чете страницата на глас на човек, който не вижда;
  • гласовото управление: човек казва „натисни Добави в кошницата“ и програмата търси бутон с това име;
  • клавиатурата: Tab минава по елементите, които могат да се натиснат;
  • автоматичните тестове като Testing Library търсят по роля и име. Така проверява и пясъчникът тук.

Всеки възел в това дърво има три неща:

  • роля: какво е елементът: бутон, връзка, заглавие, поле, картинка;
  • достъпно име: как се казва: „Добави в кошницата“;
  • състояние: натиснат ли е, отметнат ли е, изключен ли е.

Ролята идва от тага: <button> е бутон, <a href> е връзка, <h1> е заглавие. <div> и <span> нямат роля. В дървото от тях остава само текстът.

Виж го

Виж двете дървета

Карта на продукт с три „бутона“. Гледай какво остава от всеки вдясно.

<body>
<article>
<h2>Хляб</h2>
<img src="bread.svg" alt="Хляб">
<label>Брой <input type="number"></label>
<div onclick="add()">Добави</div>
<button><svg>…</svg></button>
<button>Добави</button>
</article>
</body>
Стъпка по стъпка, с думи
  1. Вляво е DOM-ът. От него браузърът строи второ дърво: дървото на достъпността. Него четат екранният четец, гласовото управление и проверките по роля. <article> става ролята article.
  2. <h2> става heading (заглавие). Името му идва от текста вътре: „Хляб“.
  3. Картинката няма текст вътре. Името ѝ идва от alt. Без alt картинката е без име.
  4. Полето получава роля spinbutton (числово поле), а името му идва от <label>: „Брой“. Самият <label> го няма в дървото, има само текста му.
  5. <div> няма роля. onclick не променя това. Остава само текстът „Добави“. Не е бутон и клавиатурата не стига до него.
  6. Това е истински бутон, но вътре има само икона без текст. Името е празно. Екранният четец казва само „бутон“.
  7. <button> с текст: роля button и име „Добави“. Tab стига до него, а Enter и интервалът го натискат.
  8. getByRole('button', { name: 'Добави' }) търси в дясното дърво. Намира един бутон: истинския. <div>-ът и бутонът без име за нея не съществуват.

Дървото на достъпността се вижда в браузъра: в инструментите за разработчици (F12) отвори Elements и панела Accessibility. Там е ролята и името на избрания елемент.

Идеята

Откъде идва името

Името на елемента се взима по правила. Най-честите източници:

  • текстът вътре: <button>Добави в кошницата</button>, <a href="/cart">Кошница</a>, <h1>;
  • alt на картинката: <img src="logo.svg" alt="Магазинът">. Връзка, в която има само картинка, взима името си от alt;
  • <label> на полето: „Количество“ е името на полето до него;
  • aria-label: име, записано направо в атрибута, когато няма видим текст: <button aria-label="Добави в любими"> с икона вътре;
  • aria-labelledby: името е текстът на друг елемент, посочен по id.

aria-label и aria-labelledby са по-силни от текста вътре. Името казва какво прави елементът, а не как изглежда: „Добави в кошницата“, а не „количка“. За картинка във връзка името казва къде води връзката: „Магазинът“, а не „лого“.

Пробвай

Дай им имена

Логото горе води към началото, но е картинка без alt. Бутонът с количката е само икона. Дай име на връзката („Магазинът“) и на бутона („Добави в кошницата“). Проверката ги търси по роля и име и натиска бутона.

HTML, CSS, JS

Идеята

Клавиатурата и фокусът

Не всеки ползва мишка. Някой има треперещи ръце, някой не вижда курсора, някой пише по-бързо с клавиатура. Той минава по страницата с Tab и натиска с Enter.

Фокусът е елементът, който в момента получава клавишите. Tab го мести напред, Shift+Tab назад. Tab стига само до елементите, които могат да се натиснат: връзки с href, бутони, полета, падащи менюта. Редът е редът в DOM, отгоре надолу.

<button> идва с всичко: роля, фокус, Enter и интервал го натискат. <div> няма нищо от това. Да му добавиш фокус и Enter значи да напишеш наново <button>.

tabindex сменя кой елемент получава фокус:

  • tabindex="0": елементът влиза в реда на Tab, на мястото си в DOM;
  • tabindex="-1": Tab го прескача, но кодът може да му даде фокус с focus();
  • tabindex="1" и нагоре: елементът отива най-отпред, преди всички останали. Редът по страницата се чупи. Не го ползвай.

Браузърът рисува рамка около елемента с фокус. Без нея човек с клавиатура не знае къде е. Не я махай. Ако не ти харесва, смени я с друга.

Пробвай

С клавиатурата

„Добави в кошницата“ е <div>. Направи така, че Tab да стига до него след полето „Количество“ и Enter да добавя в кошницата. Проверката натиска Tab два пъти и после Enter.

HTML, CSS, JS

Пробвай

Намери грешката

С мишката бутонът работи. Човек с клавиатура натиска Tab и не вижда къде е. Кой ред е виновен?

Избери реда с грешката.

Идеята

Заглавия и първото правило на ARIA

Заглавията са съдържанието на страницата. Човек с екранен четец не я чете от началото. Той иска списък на заглавията или скача между тях с клавиш (в NVDA и JAWS е H). Затова нивата казват плана: един <h1> за страницата, <h2> за разделите, <h3> в тях. Не избирай <h4>, защото е по-малко. Размерът се сменя с CSS.

ARIA (Accessible Rich Internet Applications) е набор от атрибути, които сменят дървото на достъпността: role, aria-label, aria-expanded, aria-live и други. Те казват на екранния четец нещо, което HTML не може да каже сам.

ARIA сменя само това, което се обявява, а не поведението. role="button" на <div> кара четеца да каже „бутон“, но Tab пак не стига до него, а Enter пак не прави нищо. Човекът чува „бутон“, натиска и нищо не става. Това е по-лошо от <div> без роля.

Затова първото правило на ARIA е: ако има HTML елемент, който прави това, ползвай него. <button>, а не role="button". <a href>, а не role="link". <nav>, а не role="navigation". ARIA е за случаите, в които HTML няма такъв елемент или няма видим текст, от който да дойде име.

Пробвай

Попълни

Кой атрибут дава име на бутон, в който има само икона?

<button type="button" ="Добави в любими">
  <svg viewBox="0 0 24 24">…</svg>
</button>
По-точно, за любопитните

Съобщения, които се появяват. След „Добави в кошницата“ страницата пише „Добавено: Хляб „Добруджа““. Който гледа екрана, го вижда. Екранният четец не забелязва промяната, защото фокусът не е там. <p role="status"> (или aria-live="polite") казва: когато текстът тук се смени, прочети го, щом свършиш с текущото. За спешни грешки е role="alert", който прекъсва. Елементът трябва да е в страницата преди съобщението: сменя се само текстът му.

Езикът. <html lang="bg"> казва на екранния четец с какъв глас да чете. С грешен lang българският текст се чете с английско произношение и е неразбираем. Дума на друг език в текста получава свой: <span lang="en">Dobrudzha</span>.

Скриване. display: none и атрибутът hidden махат елемента и от екрана, и от дървото на достъпността. aria-hidden="true" маха само от дървото: за икони до текст, които четецът няма защо да казва. Елемент с фокус в aria-hidden е грешка: Tab стига до него, а четецът мълчи.

Редът на имената. Пълният алгоритъм е стандартът Accessible Name and Description Computation. Накратко: aria-labelledby, после aria-label, после това, което дава HTML (<label>, alt, текстът вътре), и накрая title. title и placeholder не са добри имена: title се вижда само при задържане с мишката, а placeholder изчезва, щом пишеш.

:focus и :focus-visible. :focus важи винаги, когато елементът има фокус, и след клик с мишката. :focus-visible важи, когато браузърът реши, че рамката трябва да се вижда: при Tab, но не и след клик на бутон. Затова, ако искаш да сменяш рамката, ползвай :focus-visible. За CSS е следващият модул.

Скок до съдържанието. Първата връзка на много страници е „Към съдържанието“ (<a href="#main">). Тя е скрита, докато не получи фокус. Така човек с клавиатура не минава всеки път през цялото меню.

Упражнение

Кошницата

Кошницата е направена набързо. „Кошница“ е само удебелен текст, бутоните „×“ са <span>, а връзката „Към поръчката“ има tabindex="1", „за да е първа“. Поправи я: заглавие от първо ниво; бутони, до които Tab стига и които Enter натиска, с имена „Премахни Хляб „Добруджа““ и „Премахни Прясно мляко 3%“; Tab по реда на страницата: първият бутон, вторият, връзката. Проверката минава кошницата само с клавиатурата.

HTML, CSS, JS

Тест

Провери се

1. Какво липсва на <div onclick="add()">Добави</div> в сравнение с <button>?
2. Какво е достъпното име на <button type="button" aria-label="Затвори">×</button>?
3. На връзката „Към поръчката“ има tabindex="3". Какво става?
4. Какво прави role="button" на <div>?
5. Подзаглавието на продукта трябва да е по-малко. Кое е правилно?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·