Для разработчиков

Помните про семантику и избегайте дивной вёрстки

Семантика важна для вспомогательных технологий и доступности. Она отражает функции, которые выполняют элементы на странице, и делает их поведение более предсказуемым для пользователей.

Не забывайте, что для каждого вида контента есть разные теги, как семантические, так и нет. Про теги можно больше узнать на HTML5 Doctor.

Не путайте кнопки и ссылки

Кнопки

  • Фокус с клавиатуры по умолчанию.
  • Клик по нажатию на пробел.
  • Подсказка для скринридера с помощью role="button".
  • Состояния ∶focus, ∶hover, ∶active и ∶disabled.
  • Блокировка с помощью атрибута disabled.

Для чего нужны кнопки:

  • отправить данные формы на сервер;
  • очистить форму;
  • открыть модальное окно или скрытый блок в аккордеоне;
  • вызвать всплывающее меню;
  • переключить интерфейс;
  • проиграть медиа-контент.

Ссылки

  • Принимают фокус по умолчанию с помощью атрибута href="".
  • Клик с помощью клавиши Enter.
  • Имеют встроенную роль link.
  • Состояния ∶link, ∶visited, ∶focus, ∶hover и ∶active.
  • Могут открываться в новых окнах.
  • Можно сделать неактивными с помощью tabindex="-1" и aria-hidden="true".

Для чего нужны ссылки:

  • изменить URL и перейти на новую страницу, к окну или к определённой части страницы;
  • связать разные части приложения с отрисовкой на клиенте;
  • вызвать браузерную перерисовку/перезагрузку.

Ссылка со значением href="#", href="javascript:void(0);" или с другим похожим значением атрибута — уже не ссылка, а кнопка.

Используйте списки

Скринридеры превращают списки в оглавления, с помощью которых пользователи могут быстрее перемещаться по странице.

Где использовать списки:

  • для навигации в меню;
  • для списка вкладок;
  • для списка ссылок. Например, на социальные сети;
  • когда обычный текст должен быть списком;
  • для списка языков интерфейса, когда их на сайте несколько.
<ul>
	<li lang="ru">Русский</li>
	<li lang="en">English</li>
</ul>

Не забывайте про атрибут lang=""

Благодаря атрибуту языка скринридеры понимают, на каком языке объявлять контент документа.

Если на странице встречаются слова на языке, отличающимся от основного, то можно оборачивать их в тег <span> с нужным значением атрибута языка.

<p>Проснувшись однажды утром после беспокойного сна, <span lang="de">Gregor Samsa</span> обнаружил, что он у себя в постели превратился в страшное насекомое.</p>

Добавляйте для каждого тега img атрибут alt

С помощью alt="" мы:

  • описываем содержимое или функции изображений (часть ссылки, кнопка, превью, аватарка);
  • называем картинки, которые не загрузились;
  • предоставляем поисковым системам контекст картинок вне сайта, на котором они расположены.

Основные правила альтернативного текста

  • Текст зависит от контекста.
  • Точность и краткость, если это не изображение с большим количеством деталей, например, скриншот.
  • Если на картинке есть текст, то его можно использовать в качестве альтернативного.
  • Логотип в хедере — название компании или продукта.
  • В картинках у кнопок дублируйте её функцию.
    <button>
    	<img src="/" alt="Искать по сайту">
    <button>
  • Не используйте слова «изображение», «картинка». Это дублирует их роль, и скринридер произнесёт два одинаковых слова подряд.
  • Для длинных описаний можно использовать longdesc="" со ссылкой на страницу с полным описанием картинки, но его поддерживают не все скринридеры.
  • В конце можно ставить точку, чтобы скринридер делал паузу.
Мем с собакой, у которой всё в порядке.
alt="Собака сидит на табуретке рядом со столом в горящей комнате, пьёт кофе и говорит, что всё в порядке.".

Когда и как использовать атрибут

  • Атрибут не нужен для аватарок и декоративных изображений. В этих случаях нужно оставлять его пустым.
  • Заворачивайте иконки и текст в одну ссылку. В этом случае alt="" не может быть пустым или отсутствовать.
  • Для изображений в <figure> и c <figcaption> всё равно нужно задать альтернативное описание для картинки.

Соблюдайте иерархию заголовков и используйте на каждой странице один <h1>

Не пропускайте уровни заголовков. Начинайте всегда с <h1, а за ним размещайте заголовки более низких уровней.

<h1></h1>
<h2></h2>
<h3></h3>
<h4></h4>
<h5></h5>
<h6></h6>

Если у заголовка есть подзаголовки, то тоже соблюдайте правильную иерархию.

<h1></h1>
<h2></h2>
	<h3></h3>
	<h4></h4>
<h3></h3>
	<h4></h4>
	<h5></h5>

Добавляйте ссылку с переходом к основному контенту — skip link

Это полезно, когда в меню много пунктов. Такая ссылка поможет пользователям вспомогательных технологий сразу же переходить к основному контенту без утомительного перемещения от первого пункта меню к последнему.

Можно проверить как она работает на сайте The New York Times при навигации с клавиатуры.

Реализацию можно посмотреть в демке на CodePen.

Связывайте поля форм с лейблами или задавайте полям атрибут aria-label

Связывайте <label> с <input> при помощи атрибута for="".

<label for="input">Лейбл</label>
<input type="text" class="form-control" id="input">

Если в дизайне лейблы к полям не предусмотрены, делайте их скрытыми для обычных пользователей при помощи вспомогательного класса .visually-hidden.

Ещё один способ описать поле — задать ему атрибут aria-label="". Значение атрибута будет доступно только вспомогательным технологиям. Но этот способ подходит для случаев, когда это не полноценная форма, а одиночное поле с кнопкой, например, поиск.

<input type="text" name="search" aria-label="Search">
<button type="submit">Поиск</button>

На способ выше похож способ с aria-labelledby="". Значение атрибута также будет доступно только для пользователей вспомогательных технологий. Его стоит применять, когда это одиночное поле с кнопкой.

<input type="text" name="search" aria-labelledby="search-button">
<button id="search-button" type="submit">Поиск</button>

Метод с атрибутом title="" работает только в случае пользователей, которые не пользуются вспомогательными технологиями. Значение атрибута не доступно для таких технологий, поэтому его не рекомендуется использовать.

Подробнее о лейблах к полям можно узнать из тьюториала W3C.

Используйте title только тогда, когда это действительно нужно

Атрибут можно использовать для фреймов и иногда для изображений. Но, в большинстве случаев, достаточно aria-label="". Есть несколько причин для того, чтобы не использовать title:

  • плохие SEO-практики привели к тому, что вспомогательные технологии и браузеры теперь игнорируют содержимое этого атрибута;
  • его содержимое не переводится автоматически;
  • не все пользователи поймут, что у элемента есть этот атрибут;
  • если контент действительно важен, то его вообще не нужно скрывать, тем более так.

Больше подробностей в статье «The a11y Monthly: Why you shouldn’t rely on the title attribute».

title для iframe и frame

В случае фреймов задавайте атрибуту понятное значение.

<iframe src="https://api-maps.yandex.ru/services/inception/..." width="0" height="0" scrolling="no" frameborder="0" ... title="Яндекс.Карты">

Не отменяйте outline для интерактивных элементов при фокусе

Это важно для пользователей, которые пользуются клавиатурой для навигации по сайту. Просите дизайнеров проработать стили, можно заменить outline на box-shadow.

Плохой пример — Яндекс.Маркет. Попробуйте пройти по странице с помощью клавиш.

Хороший пример — сайт Xbox.

Увеличивайте область клика с помощью padding

Оптимальный размер кликабельной области — 48×48 px.

Особенно это важно делать в меню. Между пунктами меню не должно быть неактивных зазоров, поэтому задавайте одинаковые padding для ссылок слева и справа от них. Для последних и первых можно отменить отступ слева или справа соответственно.

Используйте ARIA-роли только тогда, когда это действительно нужно

  • Лучше никакой ARIA, чем плохая ARIA.
  • Если используете ARIA, то не дублируйте уже встроенные в элементы роли.

role="none" и role="presentation"

Обе роли выполняют одинаковую функцию — отменяют семантику элемента.

ARIA 1.1: «До тех пор, пока не будет хорошо поддерживаться role="none", рекомендуется использовать только role="presentation" или избыточный вариант с фоллбеком role="none presentation"».

Можно использовать при табличной вёрстке. Чтобы смягчить негативные последствия такого выбора, у таблиц можно удалить семантическое свойство при помощи role="presentation" для элемента table. Это сделает такой контент более понятным для пользователей скринридеров.

Можно использовать для прогрессивного улучшения разметки, если JavaScript недоступен, и при приведении разметки в соответствие с требованиями ARIA. Например, для панели вкладок.

<ul role="tablist">
	<li role="presentation">
		<a href="#panel_1"
		role="tab"
		aria-selected="true/false"
		aria-controls="panel_1">
		</a>
	</li>
</ul>

Подробнее в статье Скотта О’Хары «Know your ARIA: 'Hidden' vs 'None'».

aria-label

Этот атрибут используется для элементов без текстового содержимого. Например, в ссылках на иконочных шрифтах, в ссылках-изображениях и в кнопках без текста. Сами иконки скрываются от вспомогательных технологий при помощи aria-hidden="true".

<a href="{{this.url}}" target="_blank" class="socials__link" title="{{this.title}}" aria-label="{{this.title}}">
	<i class="fa fa-{{@key}}" aria-hidden="true">
</a>
  • В атрибуте кратко описывайте то, что делает кнопка или ссылка.
  • Атрибут не переводится сервисами автоматического перевода.

Отсутствие текста у кнопок и ссылок — критическая ошибка.

aria-labelledby

Атрибут создаёт связь между объектами и их метками.

Вспомогательные технологии используют его, чтобы собирать все метки в каталог документа, из которого пользователь может перемещаться между ними.

Можно использовать атрибут в дополнение к элементам форм, чтобы связать статический текст с виджетами, группами элементов, панелями, областями, которые имеют заголовок, определения и другие типы объектов.

Несколько значений атрибута перечисляются через пробел.

Пример с несколькими лейблам:

<div id="billing">Биллинг</div>
<div>
	<div id="name">Имя</div>
	<input type="text" aria-labelledby="billing name"/>
</div>
<div>
	<div id="address">Адрес</div>
	<input type="text" aria-labelledby="billing address"/>
</div>

Пример с группой переключателей:

<div id="radio_label">Переключатели</div>
<ul role="radiogroup" aria-labelledby="radio_label">
	<li role="radio">Пункт 1</li>
	<li role="radio">Пункт 2</li>
	<li role="radio">Пункт 3</li>
</ul>

Больше примеров использования можно найти в статье про aria-labelledby на MDN.

Делайте интерактивные области Live Region

Если на странице есть часть, содержимое которой изменяется, то нужно сделать её live region. Тогда скринридеры смогут держать своих пользователей в курсе всех изменений.

Сделать такую часть страницы интерактивной можно с помощью нескольких ролей.

role="alert" — важные ошибки, предупреждения. Для большей совместимости добавляйте её к нужным элементам вместе с атрибутом aria-live="assertive".

aria-live="" — атрибут для определения срочности сообщения об изменениях. Там, где об изменениях не нужно объявлять, используйте aria-live="off". В большинстве случаев не нужно срочно сообщать об изменениях, поэтому в них пригодится aria-live="polite". Иногда, когда речь идёт о важном сообщении, например, серверной ошибке, можно использовать aria-live="assertive".

role="status" — сообщения о менее важных ошибках и предупреждениях. Например, сообщение об автосохранении, неправильно заполненном поле и тому подобном. Для совместимости следует сочетать эту роль с атрибутом aria-live="polite".

role="log" — история сообщений, список ошибок и другое, где важна последовательность обновления информации. Для большей совместимости используйте вместе с ней атрибут aria-live="polite".

role="timer" — таймер, счётчик или секундомер. Не забудьте об aria-live="off" для лучшей совместимости.

Подробнее о ролях и значениях атрибута aria-live в статье «Всё, что нужно знать про ARIA Live Regions».

Учитывайте какие CSS-свойства попадают в дерево доступности

Их не так много:

  • display: none и display: block.
  • visibility: hidden, visibility: collapse (для строк и ячеек таблиц) и visibility: visible.
  • list-style: noneSafari).

list-style: none

В Safari этот атрибут отменяет семантику списков.

Если важно сохранить семантику списка во всех браузерах, то есть три варианта решения.

Первый вариант — явно задать роль для тегов <ul> или <ol> с помощью атрибута role="list". Однако WAI-ARIA не рекомендует дублировать роли элементов.

Можно использовать пробел нулевой ширины.

.list li {
	list-style-type: none;
}

.list li::before {
	content: "\200B";
	position: absolute;
}

Наконец, есть хак с url(#!).

ul {
	list-style: none;
}

ol, ul {
	list-style: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'/%3E");
}

Этот хак может привести к появлению лишнего отступа сверху у элементов навигации в IE11 и Edge 42.

Думайте о том, как лучше скрыть элемент

Учтите, важен ли элемент для пользователей, в том числе для тех, кто пользуется скринридерами. Есть несколько возможных сценариев:

  • всем пользователям нужен элемент;
  • ни обычным пользователям, ни пользователям вспомогательных технологий не нужен элемент;
  • элемент нужно оставить на странице, но скрыть от вспомогательных технологий;
  • элемент нужно убрать со страницы, но оставить его доступным для вспомогательных технологий.

В зависимости от сценария выберите подходящий метод скрытия элемента.

display: none;: элемент не попадает в дерево доступности, скрыт от вспомогательных технологий и пользователей. Работает везде.

Атрибут hidden: элемент не попадает в дерево доступности, скрыт от вспомогательных технологий и пользователей. Не работает в версиях IE, ниже 11. Лучше делать фоллбек с помощью display: none;.

visibility: hidden;: элемент не попадает в дерево доступности, скрыт от вспомогательных технологий и пользователей. Работает везде.

aria-hidden="true": неинтерактивный элемент не попадает в дерево доступности и скрыт от вспомогательных технологий. Другие пользователи видят элемент. Не работает в версиях IE, ниже 11.

Класс .visually-hidden или .vh: элемент попадает в дерево доступности и к нему имеют доступ вспомогательные технологии. Другие пользователи не видят элемент. Работает везде.

alt="": скрывает картинку из дерева доступности. Другие пользователи видят изображение. Работает везде.

Класс .visually-hidden или .vh

.visually-hidden {
	position: absolute;
	left: 0;
	padding: 0;
	list-style: none;
	height: 1px;
	width: 1px;
	overflow: hidden;
	clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
	clip: rect(1px, 1px, 1px, 1px);
}

body:hover .visually-hidden a,
body:hover .visually-hidden input,
body:hover .visually-hidden button {
	display: none !important;
}

aria-hidden=""

  • aria-hidden="true" удаляет элемент из дерева доступности, но не скрывает на странице.
  • aria-hidden="false" возвращает элемент в дерево доступности.

Используйте атрибут тогда, когда элемент декоративный, дублирующийся или эквивалентный тому, который уже есть на странице. Например, если у вас есть иконочный шрифт.

<a href="/">
	<span class="font-icon font-icon--home" aria-hidden="true"></span>
	Главная
</a>

Избегайте авто­воспроизведения видео и фоновых медиа

Во-первых, это раздражает, во-вторых, вызывает чувство тревоги и даже паники у некоторых пользователей.

Золотое правило медиа-контента: дать пользователям возможность самим управлять им.