Хабр, я снова пришёл к вам с практическими советами про доступность вместе с Ильёй. Мы показываем, как HTML и CSS могут улучшить или ухудшить её. Напоминаю, что Илья мой незрячий знакомый, который помогает мне найти наши косяки в вёрстке. Мы уже написали первую, вторую и третью часть. Как думаете, нужно ли уже делать отдельный хэштег?
Сегодня мы рассмотрим следующие аспекты:
- Как сверстать поиск, чтобы им мог воспользоваться пользователь скринридера;
- Чем полезно свойство
outline-offset
; - Зачем вам нужно использовать медиа-функцию
prefers-color-scheme
; - Где должен находиться заголовок в разметке блока с новостью.
Давайте начнём!
▍ Поиск — неотъемлемая часть пользовательского опыта для пользователя скринридера
В каждом интерфейсе привычно видеть поиск. Я очень редко пользуюсь им. Проще использовать навигацию. Но без него тоже плохо. Всё же он всегда приходит ко мне на помощь, если навигация слишком запутанная. В общем, неоднозначное у меня впечатление. Вроде полезный, а вроде и нет.
А вот для пользователей скринридера роль поиска однозначно определена. Он важный элемент интерфейса. Без него вообще никак. Вот, что говорит Илья:
«Для меня поиск имеет огромное значение на сайтах с большим количеством разделов и со сложными интерфейсами. Это зрячий человек может быстро охватить глазами пункты навигации и конкретные страницы. А для человека без зрения поиск чего-то методом исследования всего и вся становится пыткой. Абсолютно нереалистичный для жизни сценарий».
Я не буду обижать Илью и рассмотрю, как же правильно разметить поиск. Во-первых, поиск является формой. Следовательно, мне необходимо использовать элемент <form>
.
<form class="search">
<div class="field">
<label for="search-field" class="field__label">Поиск</label>
<input type="search" id="search-field" class="field__input">
</div>
<button class="search__button">Найти</button>
</form>
К сожалению, скринридер JAWS не найдёт наш поиск при текущей разметке. Но это не беда. Атрибут role
поможет решить проблему.
Для него можно определить значение search
, которое объяснит скринридеру, что данный элемент является поиском. Добавлю его к элементу <form>
в своей разметке.
<form role="search" class="search">
<div class="field">
<label for="search-field" class="field__label">Поиск</label>
<input type="search" id="search-field" class="field__input">
</div>
<button class="search__button">Найти</button>
</form>
Вот другое дело! Теперь Илья сможет найти поиск.
▍ Хорошо заметная обводка — ключ к успешному опыту пользователя клавиатуры
Для пользователя клавиатуры обводка при фокусировки на элементе является единственным способом понять текущее местонахождение на странице. От того, насколько она видна зависит всё.
Для пример рассмотрим следующее изображение:
Мне интересно, сколько людей видят обводку вокруг кнопки «Каталог». Лично я, когда первый раз посмотрел, не увидел. Искал её какое-то время, а когда нашел, жутко буйствовал. А часто на моём месте пользователи без технического опыта её так и не найдут. По этой причине важно делать обводку заметной. Как этого достичь? Свойство outline-offset
поможет!
С помощью него мы можем установить отступ между элементом и обводкой, созданной с помощью свойства outline
. Для примера установлю расстояние в 4px
, чтобы обводка вокруг кнопки стала лучше заметна.
.button:focus-visible {
outline: 2px solid;
outline-offset: 4px;
}
Так лучше? Надеюсь, что вы думаете, что да. В этом случае есть шанс, что я буду гораздо чаще видеть обводку!
▍ Медиа-функция prefers-color-scheme
сбережёт зрение
Мне кажется, что очень много людей используют интерфейсы ночью. У меня лично в это время уже устают глаза. Чувствую небольшой дискомфорт, и тяжело читать со светлого фона. По этой причине я фанат тёмной темы интерфейсов. Только тут есть нюанс. Многие продукты забывают её добавить.
Наверное, разработчики таких продуктов не слышали, про полезную медиа-функцию prefers-color-scheme
. Она помогает разработчикам адаптировать интерфейс с учётом визуальной темы интерфейса, которая выбрана пользователем.
Как он выбирает тему? Да легко. Либо в настройках операционной системы, либо в браузере. А вот как правильно написать стили я сейчас покажу.
Первым делом надо выбрать состояние, которое будет отображаться, когда тема не выбрана. Я привык, что интерфейсы по умолчанию светлые, поэтому для фона буду использовать значение #fcfcfc
, а для текста — #222
.
:root {
--main-mode-color: #fcfcfc;
--accent-mode-color: #222;
}
body {
color: var(--accent-mode-color);
background-color: var(--main-mode-color);
}
На следующем шаге мне нужно объявить медиа-функции prefers-color-scheme
со значением dark
. Оно может браузеру определить, что пользователь включил тёмную тему интерфейса.
А дальше всё просто. Добавлю значение #1e2229
для фона, и значение #ebecef
для текста.
:root {
--main-mode-color: #fcfcfc;
--accent-mode-color: #222;
}
@media (prefers-color-scheme: dark) {
:root {
--main-mode-color: #1e2229;
--accent-mode-color: #ebecef;
}
}
body {
color: var(--accent-mode-color);
background-color: var(--main-mode-color);
}
А как быть со светлой темой? А я уже ее сделал! По умолчанию я выбрал светлый фон, который замечательно подходит.
Но если вы захотите, отдельно стилизовать её, то вместо значения dark
используйте light
.
▍ Заголовок является первым элементом в блоке с новостью
При просмотре лент новостей мы, как зрячие пользователи, привыкли искать заголовки. Они действительно очень полезны. Прочитал его, понял смысл новости и решил, что дальше делать. А как дело обстоит для пользователей скринридера?
Значимость самого заголовка у них такая же, как у нас. Вот что думает Илья:
«Заголовок является компонентом, по которому я идентифицирую контент, принимаю решение, взаимодействовать с ним или нет».
А важна ли позиция заголовка в блоке с новостью? Зрячие пользователи находят его по оформлению. Обычно он жирный и размер шрифта у него больше, чем у основного текста. Соответственно, его позиция не важна. Но пользователи скринридера не смогут найти его по оформлению. И как быть? Илья предлагает озвучивать заголовок первым элементом в блоке:
«Допустим, есть блок с новостью. Он содержит заголовок, дату и аннотацию.
Вариант 1. Заголовок является первым элементом, а дата вторым. Пришёл к заголовку. Нажимаю клавишу ↓
, пришёл к дате. Ещё раз ↓
, пришёл к аннотации. Ещё раз ↓
, пришёл от аннотации к заголовку. Всё понятно. Есть заголовок, дата, аннотации.
Вариант 2. Дата является первым элементом, а заголовок вторым. Пришёл к дате. Нажимаю клавишу ↓
, пришёл к заголовку. Ещё раз ↓
, пришёл к аннотации. Ещё раз ↓
, пришёл от аннотации к дате. Тут я уже не помню, была ли дата перед заголовком. Сижу и думаю, относится ли дата к предыдущей или уже к следующей новости».
Мне кажется задумка Ильи полезной. Реализуем её. Только с одним нюансом. Мы сделаем так, чтобы скринридеры озвучивали заголовок первым, но визуально он будет отображаться вторым элементом.
Первым делом нужно правильно расположить элементы в разметке. Поскольку Илья хочет, чтобы заголовок озвучивался первым элементом, то в разметке он должен быть также первым. Потом идёт дата, а затем аннотация. В итоге получится следующий код:
<article class="news-card">
<h3 class="news-card__heading">
<a href="article.html">Рублёв проиграл Алькарасу на Итоговом! Впереди противостояние Медведев — Зверев</a>
</h3>
<time class="news-card__date" datetime="2023-11-15">15 ноября</time>
<div class="news-card__description">
<p>Лидеры мирового тенниса сражаются на чемпионате ATP в Турине. В Красной группе сегодня важные битвы. Подробности — в трансляции.</p>
</div>
</article>
Сейчас элементы будут озвучиваться одним за другим, начиная с заголовка. Всё как хотел Илья. Теперь сделаем так, чтобы визуально дата стала первым элементом. Я покажу два способа.
Первый основан на использовании свойства order
. Его не рекомендуют использовать для интерактивных элементов. А для статичного текста, как мне кажется, оно идеально подходит.
Суть способа заключается в том, что я поменяю порядок с помощью отрицательного значения для свойства.
.news-card {
display: flex;
flex-direction: column;
}
.news-card__date {
order: -1;
}
Во втором способе я буду использовать CSS Grids. С помощью данной техники мы можем расположить элементы, как нам нужно с помощью свойств grid-row
и grid-column
.
В нашем примере полезным будет grid-row
.
.news-card {
display: grid;
}
.news-card__date {
grid-row: 1 / 1;
}
Мне кажется, отлично получилось! Илья будет доволен. Я часто использую трюк с перестановкой элементов местами, чтобы пользователям скринридера было проще. Но ещё раз скажу. В блоках с интерактивными элементами нужно быть внимательными, чтобы не запутать пользователей клавиатуры.
▍ Заключение
С помощью этой статьи я с Ильёй хотел призвать вас:
- Разметить поиск элементом
<form>
и добавить к нему атрибутrole
со значениемsearch
; - Следить за тем, чтобы обводка при фокусе была отчётливо видна;
- Адаптировать интерфейс для любителей тёмной темы интерфейса;
- Подумать о том, в каком порядке располагать элементы при вёрстке блока с новостью.
Ещё раз оставлю ссылки на первую, вторую и третью часть. Также нам будет интересен и ваш опыт. Делитесь своими кейсами в комментариях. Спасибо за чтение.
Автор: Стас Мельников