
Хабр, я снова пришёл к вам с практическими советами про доступность вместе с Ильёй. Мы показываем, как HTML и CSS могут улучшить или ухудшить её. Напоминаю, что Илья — мой незрячий знакомый, который помогает мне найти наши косяки в вёрстке.
Сегодня мы рассмотрим следующие аспекты:
- какие скрытые проблемы с паттерном «visually-hidden» нас ждут;
- в каких ситуациях кнопка «Закрыть» указывает на выход;
- чем вредно значение contentsу свойстваdisplay;
- почему подсказка с помощью атрибута aria-labelвызывает недоумение.
Давайте начнём!
▍ Нежданчик с паттерном «visually-hidden»
Я занимаюсь цифровой доступностью 6 лет, и всё это время существует паттерн «visually-hidden». Скорее всего, вы его видели. Данная техника представляет собой набор свойств, позволяющих только скрыть визуально элемент. Вот они:
.visually-hidden {
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}
Для демонстрации принципа работы я добавлю его для элемента <h1>.
<body>
  <h1 class="visually-hidden">Дизайнер пользовательского интерфейса Стас Мельников</h1>
</body>
Так я скрыл визуально элемент, но для скринридера он останется доступен. Он скажет: «Заголовок уровень один. Дизайнер пользовательского интерфейса Стас Мельников». Вроде всё отлично. Но есть момент. Вы знаете, что будет, если использовать паттерн для части текста?
Например, представим, что имя и фамилия должно визуально отобразиться, а текст «дизайнер пользовательского интерфейса» должен быть доступен только скринридерам. Для этого нужно его обвернуть элементом с классом .visually-hidden. 
<body>
  <h1 class="logo">
    <span class="visually-hidden">Дизайнер пользовательского интерфейса</span>
    <span class="logo__firstname">Стас</span>
    <span class="logo__lastname">Мельников</span>
  </h1>
</body>
Скринридер NVDA во всех браузерах озвучит текст за раз, кроме браузера Firefox. Для полного прочтения пользователю нужно нажать клавишу со стрелкой вниз (↓) два раза. После первого нажатия он скажет: «Заголовок уровень один. Дизайнер пользовательского интерфейса». После второго: «Заголовок уровень один. Стас Мельников».
Почему так? Дело в том, что в паттерне используется position: absolute. Значение absolute добавляет для элемента display: block. А скринридер считывает такие элементы отдельно. 
Для решения проблемы я предлагаю использовать атрибут role со значением presentation. Если вам интересно узнать больше деталей, у меня есть отдельная статья про атрибут. А сейчас просто добавим его в наш код. 
<body>
  <h1 class="logo">
    <span class="visually-hidden" role="presentation">Дизайнер пользовательского интерфейса</span>
    <span class="logo__firstname">Стас</span>
    <span class="logo__lastname">Мельников</span>
  </h1>
</body>
Значение presentation скрывает элемент, но оставляет доступным его контент. Таким образом, скринридер увидит только текст в элементе <h1>. 
▍ Кнопка «Закрыть» случайно прогоняет пользователя скринридера
В своих статьях я стараюсь показать, что разработчикам нужно быть внимательными при вёрстке. Случайно сделал неверный порядок элементов, сразу страдает доступность. Очередным примером будет модальное окно.
Рассмотрим следующую разметку:
<body>
  <dialog class="modal">
    <button type="button" aria-label="Закрыть">
      <!-- здесь иконка --->
    </button>
    <!-- здесь контент модального окна -->
  </dialog>
</body>
Как думаете, где здесь проблема? После открытия модального окна кнопка «Закрыть» будет первым элементом, на который попадёт пользователь при нажатии клавиши со стрелкой вниз (↓) или Tab. Илья говорит, что такое поведение негативно настраивает пользователя.
«Таким образом ты настраиваешь пользователя, что это главное действие на этом экране. Что маловероятно. У меня есть аналогия из бытовой жизни. Заходишь в магазин и сразу видишь стрелку к выходу. Ощущение, что тебя там не ждут и хотят, чтобы быстрее ушёл».
Конечно, не все пользователи нажмут на кнопку в такой ситуации. Как говорит Илья, они попробуют поискать то, что им нужно.
«Сам факт фокуса на «Закрыть» вызывает подозрение, что с окном что-то не так. Я попробую табать вниз, чтобы посмотреть, есть ли там функционал, или экран пустой».
В итоге ошибка в порядке элементов приводит к тому, что люди тратят дополнительные усилия, чтобы выполнить свою задачу. По этой причине я предлагаю располагать кнопку «Закрыть» последним элементом в модальном окне.
<body>
  <dialog class="modal">
    <!-- здесь контент модального окна -->
    <button type="button" aria-label="Закрыть">
      <!-- здесь иконка --->
    </button>
  </dialog>
</body>
Тем более такая позиция позволяет использовать жест перехода к последнему элементу в скринридере TalkBack на платформе Android. Это мне подсказал тоже Илья.
▍ display: contents для интерактивных элементов
На сегодняшний день CSS позволяет нам скрыть элемент с сохранением его контента с помощью значения contents для свойства display. Допустим, мы применили его для элемента <div>.
<body>
  <div class="container">
    <button type="button">Кнопка</button>
  </div>
</body>
body {
  display: grid;
}
.container {
  display: contents;
}

Стили элемента <body> стали действовать на кнопку, которая встала на место скрывшегося элемента <div>. По этой причине мы видим, что она растянулась на всю ширину элемента <body>. В этом заключается вся мощь значения contents. И в этом же скрыта большая проблема.
А что будет, если применить значение для элемента <button> или <a>? Давайте посмотрим.
<body>
  <button class="control" type="button">Кнопка</button>
  <a href="#0" class="control">Ссылка</a>
</body>
body {
  display: grid;
}
.control {
  display: contents;
}

Мы видим текст. Кнопка потеряла все стили. У ссылки остался только цвет. Вроде всё нормально. Где подвох?
Признаюсь. Когда я первый раз увидел эту демонстрацию, у меня была эйфория, и я подумал: «Вроде классный способ сбросить стили». Хорошо, что потом задумался о том, а можно ли взаимодействовать с такими элементами?
Сначала я нажал клавишу Tab. Тут ждало меня разочарование. У меня не получилось сфокусироваться на элементы. Это означает, что скринридеры тоже не найдут их, если пользователи используют Tab. А также для них останется доступен только текст, а сами элементы — нет. В итоге пришлось отказать от идеи. И это хорошо! 
А потом я узнал, что кнопки и ссылки — не единственные элементы, для которых не стоит применять display: contents. Adrian Roselli подробнее рассказал в своём исследовании. Я не вижу смысла делать пересказ в этой статье. Лучше сами прочтите. Там много интересного.
Завершу раздел своим личным выводом. Не стоит отказываться от display: contents. Значение часто выручает. Лучше использовать его для элементов <div> и <span>. Скринридеры для этих элементов не делают подсказок, следовательно, отменить их не получится. Поэтому очень маленькая вероятность навредить пользователям. 
▍ Как не перестараться с атрибутом aria-label
Я много общаюсь на тему цифровой доступности с разработчиками. Заметил одну проблему, которая появилась от большого желания людей сделать интерфейс доступным. Она есть в следующем коде:
<body>
  <a href="/logout" aria-label="Кнопка выйти">
    <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
      <path d="M24 20v-4h-10v-4h10v-4l6 6zM22 18v8h-10v6l-12-6v-26h22v10h-2v-8h-16l8 4v18h8v-6z"/>
    </svg>
  </a>
</body>
Чтобы понять ошибку, нужно услышать, как скринридер озвучит ссылку. А сделает он вот так: «Кнопка выйти, ссылка».
Илья говорит, что такие элементы ставят в ступор:
«В программе экранного доступа можно выставить порядок озвучивания атрибутов элемента. Если у меня в начале будет стоять тип элемента, то я подумаю, что это кнопка, а ссылка — это подпись к кнопке.
Конечно, я нажму на такой элемент. Но у меня не будет уверенности в том, что будет дальше. Этот элемент неоднозначный для меня. Он как чёрный ящик. Поэтому мне приходится включать опыт, знания и фантазию».
Как же исправить ошибку? Нужно помнить, что для большинства HTML-элементов уже определены подсказки, сообщающие скринридерам назначение элементов. По этой причине они знают, что элемент <a> является ссылкой.
Таким образом не надо использовать слова, которые уже используются в подсказках. Например, слова «Кнопка», «Ссылка», «Меню»», «Навигация». А уж тем более не надо добавлять слова противоречащие значению HTML-элемента. Скринридеры сами правильно расскажут про элемент, если вы используйте его правильно.
Осталось убрать из нашего примера слово «Кнопка».
<body>
  <a href="/logout" aria-label="Выйти">
    <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
      <path d="M24 20v-4h-10v-4h10v-4l6 6zM22 18v8h-10v6l-12-6v-26h22v10h-2v-8h-16l8 4v18h8v-6z"/>
    </svg>
  </a>
</body>
▍ Заключение
С помощью этой статьи мы с Ильёй хотели призвать вас:
- если часть текста скрыта через паттерн, добавить к элементу role="presentation";
- следить за тем, чтобы кнопка «Закрыть» была последним элементом в модальном окне;
- использовать display: contentsс осторожностью;
- не использовать в атрибуте aria-labelслова, описывающие тип элемента.
Оставлю ссылки на все выпуски:
- Первый выпуск;
- Второй выпуск;
- Третий выпуск;
- Четвёртый выпуск;
- Пятый выпуск;
- Шестой выпуск;
- Седьмой выпуск.
Также нам будет интересен и ваш опыт. Делитесь своими кейсами в комментариях. Спасибо за чтение.
P.S. Если вы хотите больше узнать о цифровой доступности, добавляйтесь в мой ТГ канал. Ссылка в профиле.
Telegram-канал со скидками, розыгрышами призов и новостями IT ?
 
Комментарии (5)
 - ifap22.05.2024 10:48- Почему так? Дело в том, что в паттерне используется position: absolute. - Нет, это потому, что тэги заголовков h1-h6 предусматривают внутри себя лишь фразовый контент, т.е. текст и его оформление уровня параграфа. Т.е. воткнуть внутрь заголовка какой-нибудь - <b>(что само по себе не слишком феншуйно) стандарт позволяет, а- <span>- нет. В итоге сперва браузер разбирает этот говнокод в соответствие с собственным представлением о прекрасном, а не о стандарте, затем кринридер переваривает это мнение и выдает свою версию.- Илья говорит, что такие элементы ставят в ступор - Ну еще бы: тут и зрячий без бутылки не разберется, зачем эти извращенцы набыдлокодили кнопку через - aкогда именно для таких целей есть- button...
 
           
 
kurozetsu
Пока читал воодушевленно думал как можно применить все это на рабочем проекте, а потом внезапно понял что занимаюсь поддержкой сайта автомобильного холдинга..