CSS и JavaScript доступность - лучшие практики

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

Необходимые знания: Базовая компьютерная грамотность, базовое понимание HTML, CSS и JavaScript, а также понимание, что такое доступность.
Цель: Ознакомиться со способами использования CSS и JavaScript таким образом, чтобы не вредить доступности интерфейсов, а даже улучшать её.

Влияют ли CSS и JavaScript на доступность?

По сравнению с HTML, влияние CSS и JavaScript на доступность веб-страниц не столь велико, однако они всё равно способны внести свой вклад — как положительный, так и отрицательный. Иными словами, важно ознакомиться с рекомендациями по использованию CSS и JavaScript без риска случайно сломать доступность ваших веб-страниц.

CSS

Начнём наш обзор с CSS.

Правильная семантика и ожидаемое пользователем поведение

При помощи CSS мы можем заставить любой HTML-элемент выглядеть как угодно, однако это не означает, что следует так делать. Как мы часто упоминали в статье HTML: Хорошая основа для доступности, для каждой задачи всегда стоит использовать соответствующий семантический элемент. Если не следовать данному правилу, то могут возникнуть проблемы и неудобства для всех, а в особенности для людей с ограниченными возможностями здоровья. Правильная семантика также тесно связана с ожидаемым пользователями поведением — элементы выглядят и ведут себя определенным образом, в соответствии с задачами, которые они решают. Внешний вид и поведение элементов интерфейса часто универсальны между разными сайтами и даже браузерами — пользователи привыкают к этому и им проще ориентироваться в интерфейсах.

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

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

«Стандартная» структура текстового контента

Заголовки, параграфы и списки — это основа текстового контента веб-страницы:

html
<h1>Заголовок</h1>

<p>Параграф</p>

<ul>
  <li>В моём списке</li>
  <li>два элемента.</li>
</ul>

Типичные стили для такого контента могут выглядеть следующим образом:

css
h1 {
  font-size: 5rem;
}

p,
li {
  line-height: 1.5;
  font-size: 1.6rem;
}

Следует обратить внимание, что:

  • У шрифтов выбраны разумные размер, интерлиньяж, межбуквенное расстояние и прочее, чтобы сделать текстовый контент разборчивым и комфортным для чтения.
  • Заголовки заметно отличаются от основного текста, например имеют такой же крупный размер шрифта и полужирное начертание, как стандартная стилизация. Списки выглядят как списки.
  • Цвет текста должен быть достаточно контрастен относительно цвета фона.

Для более подробной информации смотрите Основы редактирования текста в HTML и Стилизация текста.

Акцентированный текст

Строчный элемент, который акцентирует особое внимание на содержащемся в нём тексте:

html
<p>Вода <em>очень горячая</em>.</p>

<p>
  Собирающиеся на поверхности капельки воды называются
  <strong>конденсатом</strong>.
</p>

Возможно, нам понадобится добавить цвета акцентированному тексту:

css
strong,
em {
  color: #a60000;
}

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

Аббревиатуры

Элемент, который позволяет разметить аббревиатуру или акроним и указать их расшифку:

html
<p>
  Веб-контент верстается с помощью
  <abbr title="Hypertext Markup Language">HTML</abbr>.
</p>

И вновь, возможно, нам понадобится немного стилизовать наши аббревиатуры:

css
abbr {
  color: #a60000;
}

Стандартная стилизация для аббревиатур — это пунктирное подчёркивание, не стоит от этого сильно отклоняться. Для более подробной информации смотрите Аббревиатуры.

Ссылки

Гиперссылки помогают нам переходить из одного места в сети в другое:

html
<p>Посетите <a href="https://www.mozilla.org">домашнюю страницу Mozilla</a>.</p>

Ниже приведена базовая стилизация для ссылок:

css
a {
  color: #ff0000;
}

a:hover,
a:visited,
a:focus {
  color: #a60000;
  text-decoration: none;
}

a:active {
  color: #000000;
  background-color: #a60000;
}

Стандартные соглашения по стилизации ссылок — подчёркивание и отличный от стандартного цвет (по умолчанию, синий) в обычном состоянии, другой цвет в случае, если ссылка ранее была посещена (по-умолчанию, фиолетовый) и еще один цвет для состояния нажатости (по-умолчанию, красный). Кроме того при наведении указателя мыши на ссылку указатель начинает отображаться в виде руки, а также ссылка выделяется, когда находится в фокусе (при перемещении по странице с помощью клавиши tab) или активна. Изображение ниже показывает выделение в Firefox (пунктирная обводка) и Chrome (синяя обводка):

До тех пор, пока пользователь получает обратную связь при взаимодействии со ссылками, можно проявлять определённую креативность относительно их стилизации. Что-нибудь обязательно должно происходить при изменении состояния. Не следует также отключать стандартную иконку указателя мыши или обводку — она вносит очень важный вклад в доступность для тех, кто при навигации по сайту использует клавитуру.

Формы

Формы - это DOM-элементы, которые позволяют пользователям вводить данные на веб-страницах:

html
<div>
  <label for="name">Введите имя</label>
  <input type="text" id="name" name="name" />
</div>

Мы подготовили пример хорошей стилизации формы, который вы можете найти здесь: form-css.html (а также посмотреть вживую).

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

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

Таблицы

Таблицы используются для отображения табличных данных.

Вы можете найти хороший образец HTML и CSS для таблицы в нашем примере: table-css.html (а также посмотреть вживую).

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

Цвета и контраст

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

К счастью, проверить, всё ли в порядке с уровнем контрастности, весьма просто. Существует множество онлайн-инструментов, которые помогут сравнить пару цветов и вычислить значение контрастности. Например, Color Contrast Checker от WebAIM. Это довольно простой в использовании инструмент, который также предоставляет инструкции, как можно добиться соответствия критериям WCAG по контрастности цветов.

Примечание: Высокий уровень контрастности также позволяет пользователям мобильных устройств с глянцевыми экранами комфортнее читать текст при ярком освещении.

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

Скрытые элементы

Существует множество ситуаций, когда не нужно показывать сразу весь контент. Например, в нашем прототипе панели вкладок (здесь можно посмотреть исходный код) имеется три секции с информацией, но мы располагаем их одна над другой и предоставляем набор вкладок, которые позволяют при кликах по ним переключаться между секциями (они также доступны при помощи клавиатуры — их можно выбирать клавишами Таб и Enter/Return).

При этом, такой подход никак не мешает пользователям скринридеров — до тех пор, пока контент в вёрстке расположен в правильном порядке, у них не будет никаких проблем. Именно поэтому абсолютное позиционирование (как в примере выше) считается одним из лучших механизмов для визуального скрытия контента.

А вот visibility:hidden и display:none, напротив, использовать для скрытия контента не следует, потому что они скрывают его не только визуально, но и от скринридеров. Поэтому применять эти свойства стоит, только если совсем скрыть контент и является вашей задачей.

Примечание: В статье Невидимый контент только для пользователей скринридеров (EN) содержится множество полезной информации по этой теме.

Помните, что пользователи могут переопределять стили

Помните, что пользователи могут переопределять ваши стили

У пользователей есть возможность переопределять ваши стили своими, например:

Пользователи могут переопределять стили по множеству причин. Людям со слабым зрением может понадобиться сделать текст крупнее на всех сайтах, которыми они пользуются. А пользователю с монохромазией потребуется перекрасить все сайты в высококонтрастные цвета, которые ему проще различать. Какая бы ни была причина, следует оставлять дизайн сайта достаточно гибким, чтобы подобные изменения в нём работали. Например, важно убедиться, что область с главным контентом будет нормально обрабатывать более крупные размеры шрифта — она не должна просто обрезать текст или совсем ломаться.

JavaScript

При неправильном использовании JavaScript тоже может вредить доступности.

Современный JavaScript — это очень мощный язык, с помощью которого можно делать кучу разных вещей: от простых обновлений пользовательского интерфейса до полноценных 2D- и 3D-игр. Понятно, что нельзя сделать абсолютно любой тип контента доступным на 100% для всех — нужно просто прилагать все усилия, чтобы делать веб-приложения доступными настолько, насколько это возможно.

Сравнительно несложно сделать доступным простой контент — например, текст, изображения, таблицы, формы и кнопки. Вспомним основные тезисы из статьи HTML: Хорошая основа для доступности:

  • Хорошая семантика: использование каждого элемента для своей задачи. Следует убедиться, что используются различные типы элементов: заголовки, параграфы, <button>, <a> и так далее.
  • Контент должен быть доступен либо как обычный текст, либо как альтернативный текст, например атрибут alt для изображений.

Мы также показали пример использования JavaScript для добавления некоторым элементам возможностей, которых они лишены по умолчанию — смотрите в разделе Добавление доступности с клавиатуры. Такое решение стоит рассматривать только в случае, когда по какой-то причине у вас нет доступа к изменению разметки. Намного надежнее и универсальнее будет использовать для каждой задачи соответствующий элемент. Также улучшить доступность несемантичных JavaScript-виджетов можно использованием WAI-ARIA атрибутов — с их помощью можно добавить некоторой искусственной семантики для пользователей скринридеров. В следующей статье мы рассмотрим эту тему подробнее.

Сложные приложения, например 3D-игры, сделать доступными непросто — написанная с использованием WebGL игра рендерится в элементе <canvas>, который в настоящее время не предоставляет никаких возможностей для использования людьми с дефектами зрения. Можно успокаивать себя тем, что данная группа пользователей не входит в целевую аудиторию, поэтому не стоит ожидать полной доступности для них. Тем не менее, вы можете реализовать управление с помощью клавиатуры (en-US) для игроков, которые не могут использовать мышь, а также сделать цветовую схему достаточно контрастной для игроков с дальтонизмом.

Проблема с чрезмерным использованием JavaScript

Данная проблема возникает, когда разработчики слишком сильно полагаются на JavaScript. Порой вы можете встретить сайт, на котором всё генерируется с помощью JavaScript — HTML, CSS и так далее. Такое решение точно так же требует поддержания всех видов доступности и решения связанных с ними проблем.

Для каждой задачи хорош свой инструмент — это правило справедливо как для выбора HTML-элементов, так и для используемой технологии в целом. Стоит хорошенько обдумать, нужно ли использовать тот сияющий, написанный на JavaScript информационный 3D-блок или же воспользоваться старым добрым и обыкновенным текстовым блоком? Нужен ли тот сложный нестандартный виджет в форме, или будет достаточно простого поля для ввода текста? Не следует генерировать весь HTML при помощи JavaScript, если это возможно.

Ненавязчивый JavaScript

При создании веб-страниц, стоит помнить об идее ненавязчивого JavaScript. Идея гласит, что JavaScript следует использовать только для дополнительного улучшения пользовательского опыта — базовые возможности должны работать без JavaScript. Здесь мы говорим в основном об использовании стандартных браузерных возможностей, когда это возможно.

Хорошие примеры использования подхода с ненавязчивым JavaScript:

  • Валидация формы на клиенте, которая показывает пользователю сообщение о проблемах в заполнении формы без необходимости отправлять данные для проверки на сервер. Если скриптов не будет, то форма продолжит работать, просто валидация будет несколько медленнее.
  • Создание нестандартных элементов управления для HTML5 <video>, которыми возможно пользоваться с клавиатуры. И в то же самое время, указание прямой ссылки на видео, которой можно воспользоваться, если JavaScript отключён (стандартные элементы управления <video> недоступны с клавиатуры в большинстве браузеров).

Для примера, мы подготовили простенькую черновую версию клиентской валидации формы — form-validation.html (здесь можно посмотреть вживую). Если попытаться нажать на кнопку отправки, не заполнив одно из полей, то форма на сервер отправлена не будет, зато появится сообщение с информацией об ошибках.

Такая валидации формы соответствует принципам ненавязчивости — форму можно будет абсолютно полноценно использовать вообще без JavaScript. Форма всё равно будет провалидирована. Так как злонамеренные пользователи могут очень просто обойти клиентскую валидацию (например, отключив JavaScript в браузере), для важных форм её всегда дублируют на сервере. При этом валидация на стороне клиента всё ещё остаётся очень полезной для показа ошибок — пользователи узнают о проблемах с заполнением формы мгновенно, вместо того, чтобы ждать цикла: отправка на сервер - валидация - перезагрузка страницы. Это очень хорошее улучшение пользовательского опыта.

Примечание: В этом простом примере серверная валидация не реализована.

Также мы постарались сделать эту форму довольно доступной. Мы использовали элементы <label> и связали их со своими полями ввода, таким образом скринридеры смогут корректно их озвучить:

html
<label for="name">Введите ваше имя:</label>
<input type="text" name="name" id="name" />

Чтобы избежать слишком частого обновления пользовательского интерфейса и возможных проблем для пользователей скринридеров, мы производим валидацию только при попытке отправки формы:

js
form.onsubmit = validate;

function validate(e) {
  errorList.innerHTML = "";
  for (let i = 0; i < formItems.length; i++) {
    const testItem = formItems[i];
    if (testItem.input.value === "") {
      errorField.style.left = "360px";
      createLink(testItem);
    }
  }

  if (errorList.innerHTML !== "") {
    e.preventDefault();
  }
}

Примечание: В данном примере мы скрываем и показываем сообщение об ошибке с помощью абсолютного позиционирования, а не каким-то иным методом, потому что такой способ не мешает скринридерам озвучивать контент.

В реальности валидация формы будет в разы сложнее. Возможно, вы захотите проверить, что введённое имя действительно выглядит как имя, а возраст — как число, да ещё и реалистичное (например, не отрицательное и состоящее не более чем из 3 цифр). Здесь мы реализовали лишь примитивную проверку на наличие хоть какого-то текста в полях ввода (if (testItem.input.value === '')).

Когда валидация проходит успешно, форма отправляется на сервер. Если же были обнаружены ошибки (if (errorList.innerHTML !== '')), то мы прерываем отправку формы (при помощи preventDefault()) и отображаем сообщения обо всех найденных ошибках (смотрите далее).

Для каждого пустого на момент отправки формы поля ввода мы создаем элемент списка с ссылкой и добавляем в errorList.

js
function createLink(testItem) {
  const listItem = document.createElement("li");
  const anchor = document.createElement("a");

  anchor.textContent =
    "Поле ввода " +
    testItem.input.name +
    " пусто: введите " +
    testItem.input.name +
    ".";
  anchor.href = "#" + testItem.input.name;
  anchor.onclick = function () {
    testItem.input.focus();
  };
  listItem.appendChild(anchor);
  errorList.appendChild(listItem);
}

Каждая ссылка выполняет две задачи — она рассказыает, какая ошибка случилась, а также при клике на неё можно перейти прямо в связанное поле ввода и скорректировать введённое значение.

Примечание: Часть этого примера, связанная с focus(), довольно хитрая. Браузеры Chrome и Edge (а также новые версии IE) не нуждаются в коде onclick/focus() — они сами переведут фокус на элемент после клика по ссылке. Safari лишь выделит нужный элемент, так что этот блок кода нужен для корректной установки фокуса. В Firefox же трюк с выделением элемента при клике по ссылке не работает вообще. Данная ошибка должна быть вскоре исправлена — тогда Firefox придет к паритету с прочими браузерами (смотрите Firefox bug 277178).

Дополнительно, то что errorField расположено в вёрстке в самом начале (тогда как визуально в интерфейсе расположено иначе при помощи CSS), позволяет пользователям увидеть, что именно не так с заполнением формы, и легко вернуться обратно к нужным полям ввода в начале страницы.

Кроме того, мы использовали несколько WAI-ARIA атрибутов в нашем примере, чтобы решить проблемы с доступностью, вызванные обновлением контента без перезагрузки страницы (по умолчанию, скринридеры не могут подхватить эти изменения и уведомить об этом пользователя):

<div class="errors" role="alert" aria-relevant="all">
  <ul>
  </ul>
</div>

Мы разберем данные атрибуты в следующей статье, которая подробно описывает WAI-ARIA (en-US).

Примечание: Кто-то из читателей мог задуматься о том факте, что в HTML5 формах есть встроенные механизмы валидации, такие как атрибуты required, min/minlength и max/maxlength (смотрите подробнее на странице элемента <input>). Мы не использовали их в примере, потому что поддержка браузерами у HTML5 валидации весьма варьируется (например, только IE10 и новее).

Примечание: Статья Удобная и доступная валидация форм (EN) от WebAIM содержит дополнительную полезную информацию по этой теме.

Прочие проблемы доступности, связанные с JavaScript

Здесь мы собрали прочие аспекты, о которых стоит задумываться при написании JavaScript, если вы беспокоитесь о доступности. Мы будем обновлять данный раздел.

Специфичные события мыши

Большая часть интерактивности в пользовательских интерфейсах реализуется в JavaScript при помощи обработчиков событий, которые позволяют запускать код, когда происходит определённое событие. У некоторых таких событий имеются проблемы с доступностью. Хороший пример — это специфичные события мыши, например mouseover (en-US), mouseout (en-US), dblclick (en-US) и так далее. Функции, которые активируются при срабатывании этих событий, не будут доступны при использовании других устройств ввода, например клавиатуры.

Чтобы избежать проблем подобного рода, следует дублировать функциональность при помощи независимых от устройств событий. Например, события focus (en-US) и blur будут работать как для мыши, так и для клавиатуры.

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

Мы подготовили простой пример, который можно посмотреть здесь mouse-and-keyboard-events.html (а здесь можно посмотреть исходный код). В коде представлены две функции, которые показывают и скрывают увеличенное изображение. Они устанавливаются в качестве обработчиков событий следующими строками кода:

js
imgThumb.onmouseover = showImg;
imgThumb.onmouseout = hideImg;

imgThumb.onfocus = showImg;
imgThumb.onblur = hideImg;

Первые две строки кода запускают функцию, когда указатель мыши наводится на изображение или уходит с него, соответственно. Такая реализация не позволит открыть увеличенное изображение при помощи клавиатуры. Чтобы решить эту проблему, мы добавили последние две строки, которые запускают функции в случаях, если фокус перешёл на изображение или же ушёл с него. Таким образом, увеличенное изображение будет открываться при переходе на миниатюру при помощи клавиши tab (это возможно, потому что мы добавили миниатюрам tabindex="0").

Событие click довольно любопытное — выглядит так, будто бы оно специфичное для мыши, однако большинство браузеров при нажатии клавиши Enter/Return активируют обработчики событий onclick на находящихся в фокусе ссылках или элементах формы, а также при тапе по элементам на сенсорном экране. Впрочем, это не работает автоматически, если вы позволяете принимать фокус неинтерактивному по умолчанию элементу при помощи tabindex — в таких случаях нужно специально прослушивать нажатие нужных кнопок (смотрите Добавление доступности с клавиатуры).

Проверьте ваши навыки!

Вы дочитали до конца, но хорошо ли вы усвоили материал? Перед тем как двигаться дальше, вы можете пройти тесты, которые мы специально подготовили — смотрите Проверьте ваши навыки: CSS и JavaScript доступность (EN) (en-US).

Заключение

Надеемся, что данная статья даст вам хорошую базу в решении проблем с доступностью, которые могут возникнуть при использовании CSS и JavaScript на веб-страницах.

Далее WAI-ARIA!