-
Используйте атрибут
lang=ru
для тега<html>
. Он помогает скринридерам и парсерам понять, на каком языке страница, и как её озвучивать. Так жеlang
можно указывать для блоков разметки на другом языке, например, если абзац текста на английском. -
Следите за иерархией заголовков. На странице должен быть только один заголовок 1-го уровня, остальные заголовки должны быть вложенными в зависимости от уровня.
<header> <h1>Страница отчёта</h1> </header> <main> <article> <h2>Отчёт 1</h2> </article> <article> <h2>Отчёт 2</h2> <div> <h3>Подпункт 1</h3> </div> </article> </main>
-
Используйте для разметки списков теги
<ul>
,<ol>
,<li>
; для таблиц<table>
,<tr>
,<th>
,<td>
.<ul> <li>Первый пункт</li> <li>Второй пункт</li> </ul>
<table> <tr> <th>Заголовок первого столбца</th> <th>Заголовок второго столбца</th> </tr> <tr> <td>Данные</td> <td>Данные</td> </tr> <tr> <td>Данные</td> <td>Данные</td> </tr> </table>
-
Если компонент ведёт на другую страницу, используйте элемент
<a>
независимо от его внешнего вида. Если интерактивный компонент оставляет пользователя на странице, используйте элемент<button>
. -
Для разметки областей страницы используйте теги
<header>
,<main>
,<article>
,<nav>
,<section>
,<footer>
. В отличии от тега<h1>
элемент<header>
можно использовать несколько раз на странице, например, для заголовка модального окна. Так же, несколько раз можно использовать<footer>
,<article>
,<section>
,<main>
. -
Не забывайте использовать редкие теги:
<abbr>
для аббревиатур,<blockquote>
для цитат, группу тегов<dl>
,<dd>
,<dt>
для определений.<abbr title="Индивидульный налоговый номер">ИНН</abbr>
<dl> <dt>Кадровый учёт</dt> <dd>Это учёт кадров</dd> </dl>
-
Добавляя атрибут
title
для страницы, помимо названия сервиса дополняйте описанием страницы, например: «Экстерн — создание отчёта». -
Не скрывайте элементы при помощи
display:none
, а информативные элементы не добавляйте через:before
или:after
— скринридер не читает элементы скрытые черезdisplay:none
или добавленные через стили. Для визуального скрытия элемента лучше используйте следующие стили:.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; }
-
Если вы не используете необходимый тэг, но хотите придать элементу семантичное значение, не используйте атрибут
role
. Скринридер прочитает элемент как кнопку, но вам придётся добавлять управление фокусом, обработку нажатий клавиш с клавиатуры. Используйте необходимый тэг вместо этого:/** Плохо */ <div role="button">Нажми на меня!</div> /** Хорошо! */ <Button>Нажми на меня!</Button>
-
Если у инпута нет лейбла, задайте его при помощи
aria-label
:<Input aria-label="Введите имя (обязательно)" />
-
Если нет возможности обернуть
<Input />
в<label>
, используйтеaria-labeledby
:<label id="label">Я лейбл для поля!</label> <Input aria-labeledby="label" />
-
Если хотите добавить описание элементу, используйте
aria-describedby
:<span id="button-description">По нажатию на кнопку отправится отчёт, после чего вам придёт уведомление на почту</span> <Button aria-describedby="button-description">Нажми!</Button>
-
Для дизейбла элемента используйте
aria-disabled
, а визуально и интерактивно блокируйте элемент при помощи стилей и JS. Скринридер остановится на этом элементе, тогда как атрибутdisabled
скринридер проигнорирует:<Input aria-disabled="true"/>
-
Некоторые контролы предоставляют встроенные
aria-label
. Все встроенные доступные описания имеют перевод на русский и английский языки. Вы можете воспользоватьсяLocaleContext
для изменения этих описаний:<LocaleContext.Provider value={{ locale: { Calendar: { dayCellChooseDateAriaLabel: 'Дата в фокусе' } } }}> <Calendar value={date} onValueChange={setDate} /> </LocaleContext.Provider>
-
Все элементы управления и ввода (те, с которыми может взаимодействовать пользователь) должны быть фокусируемые. Если элемент по умолчанию не имеет фокуса - можно сделать его фокусируемым при помощи атрибута
tabindex=”0”
. -
Если при взаимодействии с контентом страницы открывается
<Modal>
или<SidePage>
, то при нажатии наtab
должен фокусироваться контент модального окна\сайдпейджа в первую очередь, на первый интерактивный элемент или на заголовок в случае скрин-ридера. -
Тестируйте выполнение основных сценариев с клавиатуры: перемещение при помощи
tab
,shift+tab
, управление контролами при помощи стрелочек клавиатуры,enter
,escape
. -
Не блокируйте зум на сайте. Делайте странички сайта адаптивными для изменений масштабирования. Поддерживайте масштабирование минимум до 150%.
-
При переключении клавишей
tab
элементы формы должны переключаться в нужной последовательности, один за одним. -
Не злоупотребляйте атрибутом tabindex. Если на странице появятся новые элементы, легко запутаться в установленных значениях. При ошибках использования, например, повторяющихся значениях tabindex, навигация по DOM путается — пользователю скринридера будет сложно перемещаться по странице.
-
Старайтесь чтобы визуальное расположение элементов на сайте повторялось в разметке, DOM-дереве. Есть стили (например,
flex-direction: row-reverse
), которые меняют визуальный порядок элементов на странице. При это вDOM
порядок остаётся прежним, и скринридер прочитает его как вDOM
, но на странице эти элементы будут расположены по-другому. Это же правило касается блоков сposition: absolute | sticky | fixed
– стилей, выносящих блоки из потока.
-
Оставляйте пустой атрибут
alt
для декоративных картинок и заполняйте его для информативных элементов. Не стоит писать вalt
большие объёмы текста, в общих чертах описывайте самое важное. -
Для сложных изображений\графиков дублируйте информацию в текстовом варианте.
<picture> <img src=".../images/graph.png" alt="На графике изображено количество принятых отчётов с первого раза. С первого раза принимаются 80% отчётов за год, 10% со второй попытки и оставшиеся 10% с третьей или больше."> </picture>
-
Не используйте символ * как обозначение обязательности поля:
Ваше имя*:
Лучше явно подписать обязательность:
Ваше имя (обязательно):
Либо явно написать «обязательные поля обозначены символом *»
Дело в том, что символ * голосовые ассистенты читают как «star» или «звёздочка», в зависимости от языка
-
Лучше всего валидировать формы по нажатию на кнопку
submit
, в случае ошибки переносить фокус на текст ошибки. Если же валидация формы происходит после потери фокуса, пользователь скринридера не узнает об этом. Ему придётся искать ошибку наугад. -
Текст об ошибке лучше размещать рядом с полем, визуально отделять его цветом и иконкой. Так же, текст ошибки лучше размещать в
<label>
этого поля, для того, чтобы после фокусировки на поле при помощи скринридера, он прочитал ошибку и пользователь мог исправить проблему.<h1>Пользовательские данные</h1> <span>Обязательные поля обозначены символом *</span> <form> <label htmlFor="name">Имя* {isNameError && "содержит ошибку"}</label> <Input id="name" placeholder="Введите ваше имя" /> <label htmlFor="lastname">Фамилия {isLastNameError && "содержит ошибку"}</label> <Input id="lastname" placeholder="Введите вашу фамилию" /> <Button onClick={checkErrors}>Отправить</Button> </form>
-
В кнопку-иконку добавьте текст с описанием действия и скройте его с помощью специального класса. Этим же способом можно подписывать отдельно стоящие иконки.
<Button> <Icon /> <span className="visually-hidden">Нажми на меня!</span> </Button>
.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; }
-
Указывайте
<label>
для каждого поля формы. Если поле нельзя обернуть в<label>
, используйте атрибутыhtmlFor
иid
:<label htmlFor="input-id">Имя Фамилия</label> <input id="input-id"/>
-
Либо используйте
aria-label
:<input aria-label="Имя Фамилия" id="input-id" />
Основывайте собственные компоненты на встроенных компонентах браузера — они учитывают все кейсы и сложности работы с компонентом, особенно работу с клавиатуры. Реализуйте стандартные паттерны.
-
Браузеры имеют встроенные инструменты проверки вёрстки на контрастность и семантику разметки. Используйте их при вёрстке.
-
Расширение браузера aXe проверит и выведет ошибки в панель разработчика браузера. Также его можно внедрить как шаг в CI.
-
Расширение Lighthouse от Google позволяет провести аудит сайта на основные правила доступности.
-
Расширение WAVE Evaluation Tool позволяет найти многие ошибки WCAG и цифровой доступности.
-
@storybook/addon-a11y — аддон для Storybook, находящий ошибки в вёрстке, а также позволяющий имитировать некоторые заболевания глаз.
-
eslint-plugin-jsx-a11y — плагин для ESLint, проверяющий код на доступность.
-
jest-axe — инструмент для написания тестов на доступность.