Многие разработчики думают, что доступность реализуется только с помощью aria-атрибутов. Если их не добавить, то всё, доступности нет. Конечно, aria-атрибуты нужны, но HTML и CSS такая же важная часть процесса создания доступных интерфейсов. Эти технологии непросто несут в себе кучу скрытых моментов, влияющих на доступность. Они напрямую позволяют её улучшить. В статье хочу показать это.


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

Поехали!


▍ Псевдоэлементы помогают пользователям не целиться по кнопке


В интерфейсах постоянно встречаются интерактивные элементы, которые сделаны в виде иконок. Например, кнопки переключения слайдов в слайдере, кнопки крестик и т.п. По рекомендациям WCAG минимальный размер таких элементов должен быть 24 на 24 пикселя, а лучше 44 на 44 пикселей. Но мы можем проявить больше заботы о пользователе без изменения дизайна, сделав область ещё больше.


Достаточно расширить область кликабельности интерактивного элемента. Например, при помощи прозрачного псевдоэлемента.


.button {
  width: 44px;
  height: 44px;
  position: relative;
  isolation: isolate;
}

.button::before {
  content: "";
  position: absolute;
  inset: -10px;
  z-index: -1;
}

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


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


.button {
  width: 16px;
  height: 16px;
  position: relative;
  isolation: isolate;
}

.button::before {
  content: "";
  position: absolute;
  inset: -8px;
  z-index: -1;
}

Область кликабельности кнопки теперь не 16 на 16 пикселей, а 24 на 24.


▍ Медиафункция (prefers-reduced-motion: no-preference) защищает наших пользователей от головокружения


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


Хорошо, что существует медиафункция prefers-reduced-motion. Она помогает браузерам определить, запретил ли пользователь анимацию в настройках операционной системы. Если он это сделал, то медиафункция поможет браузерам не показывать её, и пользователю не будет плохо.


Есть два случая, когда нам нужно её использовать. Первый, плавная прокрутка с помощью свойства scroll-behavior.


@media (prefers-reduced-motion: no-preference) {

  html {
    scroll-behavior: smooth;
  }
}

Второй, анимация движения объекта, созданная с помощью свойств animation и transition.


@media (prefers-reduced-motion: no-preference) {

  .container {
    animation: zoomIn 1s ease-out alternate infinite both;
  }

  @keyframes zoomIn {
    0% { transform: scale(0); }
    100% { transform: scale(1); }
  }
}

@media (prefers-reduced-motion: no-preference) {

  .button {
    transform: scale(1);
    transition: transform 1s ease-out;
  }

  .button:focus {
    transform: scale(1.5);
  }
}

▍ Свойство background-color помогает сохранить доступность текста


Есть проблема доступности текста. Он сливается с белым фоном страницы в момент, когда фоновый медиаконтент загружается. Для демонстрации я буду использовать блок приветствия на странице портфолио разработчика:


<div class="welcome">
  <div class="container welcome__container">
    <h1 class="welcome__intro">
      <span class="welcome__name">I'am Bernard Balfour</span>
      <span class="welcome__caption">Front-end developer from Denmark</span>
     </h1>
  </div>
</div>

.welcome {
  box-sizing: border-box;
  height: 100dvh;
  padding-block: 1rem;

  display: flex;

  background-image: url("bernard.jpg");
  color: #fefefe;
}

.welcome__container {
  margin-block: auto;
}

.container {
  box-sizing: border-box;
  width: min(100%, 87.5rem);
  padding-inline: 0.75rem;
  margin-inline: auto;
}

В этом примере, чтобы прочитать текст, нужно дождаться загрузки фото разработчика. В итоге мы ждём от несколько секунд до несколько минут. А бывает так, что и вовсе не дождёмся. Соответственно, текст останется недоступным. Решение — создать подложку с контрастирующим фоном.


.welcome {
  box-sizing: border-box;
  height: 100dvh;
  padding-block: 1rem;

  display: flex;

  background-image: url("example.jpg");
  background-color: #0B5982;
  color: #fefefe;
}

Таким образом, текст теперь всегда доступен. Обожаю этот трюк за его простоту.


▍ Не всегда подробная информация в атрибуте alt полезна пользователю


Казалось бы, что про атрибут alt написано в каждом гайде по доступности. В результате появилось правило заключающиеся в том, что надо добавлять альтернативный текст. Вот в очередной раз убеждаюсь в том, что "простые" вещи вовсе непростые. Если бездумно следовать этому правилу, то получается криво. Почему я так думаю? Вот комментарий моего незрячего знакомого:


"Мне подробные описания изображений в стиле "Молодая женщина с вьющимися волосами в синем платье. Улыбается" не нужны. Меня просто бесят. Это чисто моя специфика. За день поглощаю тонны информации. И для меня — это спам. То есть, конечно, с помощью горячих клавиш можно проскакивать мимо изображений, но, когда фото с такими альтами стоят внутри текста, напрягает. У меня весь день ридер болтает в телефоне и ноуте. Бывает, к вечеру едет крыша. Да и слух притупляется. Поэтому всё лишнее стараюсь отбрасывать.".


Какой совет здесь можно дать? Я не могу претендовать на истину, но лично я учитываю контекст, в котором находится изображение. Что бесит Илью. В момент, когда он пролистывает список новостей, ему попадаются такие описания. Соответственно, ему такое подробное описание просто не нужно в этой ситуации. Оно только мешает.


Но, если он читает статью, в которой изображение органично дополняет текст, то всё будет хорошо. Он получает ожидаемую для него информацию. Она не вызывает у него вопрос: "А зачем мне это описание сейчас?".


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


▍ Атрибут alt может засорять уши пользователям скринридера дублированием информации


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


<div class="BaseInfo-avatar-DfkQZ">
  <img src="avatar.jpg" alt="Мельников Александр">
</div>
<div class="BaseInfo-name-QUscI">
  <h1 class="desktop-1r4tu1s">Мельников Александр</h1>
</div>

Скринридеры озвучат следующее: "Мельников Александр, изображение. Мельников Александр, заголовок первого уровня". Как видите, пользы от такого дублирования нет. Только засоряются уши пользователя. Поэтому так, точно, лучше не делать.


Как это сделать? Никогда не думал, что это скажу. Оставить атрибут alt пустым.


<div class="BaseInfo-avatar-DfkQZ">
  <img src="avatar.jpg" alt="">
</div>
<div class="BaseInfo-name-QUscI">
  <h1 class="desktop-1r4tu1s">Мельников Александр</h1>
</div>

Есть ещё одно частое решение, которое, по моему мнению, не несёт никакой пользы. Это написать текст "Аватар пользователя", а потом добавить имя.


<div class="root--JmqUV isExtraLarge--qqtKg withBadge--rtcDj">
  <img src="avatar.jpg" loading="lazy" class="imageAvatar--z7lC3" alt="Аватар пользователя Стас Мельников">
</div>
<div class="info--Ay9ih">
  <h1 class="name--a1797">Стас Мельников</h1>
</div>

Пользователь скринридера услышит: "Аватар пользователя Стас Мельников, изображение. Стас Мельников, заголовок первого уровня". Какая тут польза я не знаю. Мой знакомый также не знает. Может быть, вы знаете?


По мне в этой ситуации, также лучше оставить атрибут alt пустым.


<div class="root--JmqUV isExtraLarge--qqtKg withBadge--rtcDj">
  <img src="avatar.jpg" loading="lazy" class="imageAvatar--z7lC3" alt="">
</div>
<div class="info--Ay9ih">
  <h1 class="name--a1797">Стас Мельников</h1>
</div>

▍ Элемент <main> даёт быстрый доступ к основному содержимому страницы


В скринридерах есть функция, помогающая быстро перейти к основному содержимому страницы. Например, с помощью клавиши Q в скринридере JAWS. Но есть загвоздка. Без элемента <main> эта функция не работает. Пожалуйста, не забывайте добавлять элемент. Без него пользователям в сотни раз сложнее получить нужную им информацию.


▍ Заключение


Этой статьёй я хотел не только рассказать про неочевидные моменты и поделиться советами. Ещё хотелось показать, на примере атрибута alt, что бездумное следование лучшим практикам может привести к негативному результату. К моему сожалению, я зачастую вижу, что фронтендеры не продумывают, как их реализация сказывается на опыт людей. Я не осуждаю. Это моё наблюдение.


В качестве последних слов, я хочу попросить вас. По мере своих возможностей, пожалуйста, старайтесь думать, как ваш код сказывается на опыт людей. Подумайте, какие ограничения будут у пользователей. Я понимаю, что не всё зависит от разработчика. Но вы можете значительно повлиять на опыт людей. Заранее спасибо.


Ещё если вы заметили, что я что-то опустил, а я упустил, то напишите, пожалуйста, в комментариях аспекты HTML и CSS, которые повлияли на доступность вашего приложения. Я не хочу останавливаться на этой статье. В будущем напишу ещё несколько частей. Ваша помощь будет полезна.


Telegram-канал с розыгрышами призов, новостями IT и постами о ретроиграх ????️

Комментарии (9)


  1. gruzoveek
    05.09.2023 11:13
    +5

    В некоторых примерах было бы неплохо добавить картинки с результатами, а так статья хорошая


  1. ifap
    05.09.2023 11:13

    Пожалуйста, не забывайте добавлять элемент <main>

    Его не надо добавлять, им надо заменять теги блока с основным контентом, т.е.:

    <main>контент</main>

    вместо

    <div id=content>контент</div>

    Потому как конструкция

    <main><div id=content>контент</div></main>

    усложняет DOM, ничего не давая взамен.


    1. Spaceoddity
      05.09.2023 11:13
      +1

      усложняет DOM, ничего не давая взамен.

      Нет.

      https://developer.mozilla.org/ru/docs/Web/HTML/Element/main

      <main> не вносит вклад в структуру документа; то есть, в отличие от таких элементов, как <body>, заголовков, таких как <h2> (en-US) и т.п., <main> не влияет на концепцию DOM структуры страницы. Он носит исключительно информативный характер.


      1. ifap
        05.09.2023 11:13

        Спасибо, я мог бы и догадаться, что семантический тег не добавляет к DOM. Но все же настаиваю, что оборачивание в дополнительный тег, когда можно не оборачивать, а просто поменять div на main, не оправдано и усложняет разбор кода.


        1. YemSalat
          05.09.2023 11:13

          Там в примере выше на mdn они так и делают - используют main вместо div'a


      1. dom1n1k
        05.09.2023 11:13

        Даже если не обращать внимание на кривой надмозговый перевод (concept в данном случае это не концепция, а понимание), эта фраза всё равно очень спорная. В чем отличие "информативного характера" от "структуры документа"? Где и какими гвоздями эта самая структура забита?
        Понятно же, что в любом случае практическая интерпретация семантики остается на усмотрение юзер-агента. И есть случаи, когда тег <main> имеет конкретную функциональную нагрузку — пример приведен на том же самом MDN.


  1. tranklogic
    05.09.2023 11:13
    +2

    Настоящее бесилово - когда используют бездумно live region. Например, на сайте есть слайд-шоу, и скринридер при смене слайда произносит: "Слайд 1 из 10", "Слайд 2 из 10" и т.д. Читать такой сайт невозможно, а всё потому, что кто-то решил радовать незрячих бесполезной информацией о смене слайдов...

    Обновляйте элементы с aria-live атрибутом только в ответ на действие пользователя! Не используйте live region в периодически обновляемых элементах!


  1. sergey_privacy
    05.09.2023 11:13
    +1

    Отличная статья, лови плюсик в карму


  1. godsplane
    05.09.2023 11:13

    Первое правило доступности и aria-аттрибутов - по возможности не использовать aria-аттрибуты,а использовать для этого "нативные" методы