Хабр, я снова пришёл к вам с практическими советами про доступность вместе с Ильёй. Мы показываем, как HTML и CSS могут улучшить или ухудшить её. Напоминаю, что Илья — мой незрячий знакомый, который помогает мне найти наши косяки в вёрстке.


Сегодня мы рассмотрим следующие аспекты:

  • какие сюрпризы есть при использовании свойства display;
  • о чём нужно помнить при вёрстке кнопки «Показать пароль»;
  • зачем нужны подсказки с помощью атрибута aria-label при разметке областей навигации;
  • как можно переборщить с заголовками и какие проблемы будут.

Давайте начнём!


▍ Подводные камни свойства display для скринридера NVDA


В предыдущей части я рассказывал, что паттерн «visually-hidden» приводит к тому, что скринридер NVDA в браузере Firefox зачитывает текст отдельно. Так получается, потому что у элемента устанавливается значение block для свойства display.


Дальше я подумал: «А как будет озвучен текст, находящийся внутри элемента с установленными значениями flex и grid для свойства display?» Ведь браузеры для их дочерних элементов устанавливают значение block. Давайте посмотрим, к чему в итоге я пришёл.


В своём исследовании я использовал разметку блока с ценой.


<body>
  <span class="price">
    <span>94 682</span>₽ за ночь
  </span>
</body>

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


Тестирование проводил в скринридерах NVDA и JAWS вместе с браузерами Google Chrome, Firefox, Edge и Opera. В каждом тесте я использовал значения flex, inline-flex, grid и inline-grid для элемента с классом .price.


.price {
  display: flex; /* также по очереди изменял значение на `inline-flex`, `grid` и `inline-grid` */
}

В результате скринридер JAWS зачитывает весь текст сразу во всех браузерах. NVDA делает так в Google Chrome, Edge и Opera. В браузер Firefox, как ожидалось, текст озвучивается за два нажатия клавиши со стрелкой вниз (). При первом пользователи услышат число из отдельного элемента <span>, а при втором — оставшийся текст.


Исправить такое поведение можно, скрыв отдельный элемент <span> с сохранением доступа к его тексту. Сделает это значение presentation для атрибута role.


<body>
  <span class="price">
    <span role="presentation">94 682</span>₽ за ночь
  </span>
</body>

Теперь для скринридера NVDA в элементе с классом .price существует только текст «94 682₽ за ночь». Поэтому он будет озвучен сразу за одно взаимодействие пользователей.


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


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


Честно, я думал, данный паттерн бесполезен для пользователей скринридера. Но Илья переубедил меня.


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


А я думал, что функция не нужна. Хорошо, что Илья поправил меня. Кроме того, он рассказал, какие есть нюансы. Первый заключается в расположении кнопки.


«Перемещаюсь по форме с помощью клавиши Tab через логин и пароль. Если есть необходимость отредактировать или проверить пароль, нажимаю кнопку, которая показывает пароль. Обычно она расположена после поля».


По этой причине я всегда рекомендую располагать кнопку за элементом input.


<body>
  <!-- здесь элементы формы -->
  <label for="password">Пароль</label>
  <input id="password" type="password">
  <button type="button" aria-label="Показать пароль">
    <!-- здесь иконка в виде глаза -->
  </button>
  <!-- здесь элементы формы -->
</body>

Обращаю ваше внимание, что кнопка должна быть кнопкой. Так пользователи скринридера и клавиатуры смогут на неё попасть.


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


«В худшем случае приходится возвращаться от кнопки на шаг назад в поле и нажимать enter для входа в режим редактирования. В лучшем — фокус сразу попадает в поле с текстом».


Для демонстрации первого варианта подходит сайт Госуслуг.




Я уже сфокусировался на кнопку. Нажму на клавишу Enter.




Я остался на ней. Мне нужно вернуться в поле редактирования. В моём случае я нажму сочетание клавиш Shift + Tab. После этого я уже могу редактировать пароль. Илье потребуется ещё войти в режим редактирования поля.


Другой пример можно найти на сайте Сбера.




Я также нахожусь на кнопке «Показать пароль». Жму клавишу Enter.




Сайт перенёс меня в поле. Я могу отредактировать пароль и авторизоваться.


▍ Помогаем скринридерам ориентироваться в областях навигации


Представьте, что вы работаете доставщиком еды и вам требуется доставить заказ. Вы на месте. Перед вами три одинаковых дома. Таблички с номером дома нет. Как вы найдёте нужный?


Такой квест проходят пользователи скринридера, когда на странице несколько областей навигации.


<body>
  <nav>
    <!-- область основной навигации -->
  </nav>

  <nav>
    <!-- область хлебных крошек -->
  </nav>

  <nav>
    <!-- область пользовательской навигации -->
  </nav>
</body>

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


С такой же проблемой сталкиваются скринридеры. Они тоже не знают, какая область за что отвечает. Вот так JAWS видит страницу в режиме «Список областей»:




Можно решить проблему, добавив подсказку о типе навигации. Она будет озвучена пользователю скринридера и отображена в специальных режимах. Сделать это можно с помощью атрибута aria-label.


<body>
  <nav aria-label="Основная">
    <!-- элементы основной навигации -->
  </nav>

  <nav aria-label="Хлебные крошки">
    <!-- область хлебных крошек -->
  </nav>

  <nav aria-label="Пользовательская">
    <!-- элементы пользовательской навигации -->
  </nav>
</body>



Вот теперь сразу понятно, где какая область навигации. А сейчас расскажу, как озвучиваются подсказки при переключении с помощью клавиши со стрелкой вниз ().


В случае основной области навигации я слышу при входе: «Основная область навигации». А при выходе из неё: «Основная конец область навигации». В области хлебных крошек — «Хлебные крошки область навигации» и «Хлебные крошки конец область навигации».


В области пользовательской навигации он скажет: «Пользовательская область навигации» и «Пользовательская конец область навигации».


▍ Не весь текст это заголовок


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




Каким элементом вы разметили бы текст «Фокус внимания Правительства Нижегородской области»? Если хотите использовать элемент <h3>, то вы удачно зашли. Не делайте так.


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


«Заголовки размечают контент на части, служат ориентирами для быстрого перехода. Если два заголовка созданы для одного блока с контентом, это бьёт по логике его восприятия и задерживает навигацию горячими клавишами. Приходится дополнительно разбираться».


Решить эту проблему можно несколькими способами. Первый, это подойти к дизайнеру и сказать: «Друг, давай мы тут заголовок изменим, чтобы подзаголовка не было». Это будет наиболее лучший вариант. В моём примере блок можно изменить так:




Второе решение — сделать один заголовок.


<body>
  <section>
    <h2>
      <span>Приоритеты</span>
      <span>Фокус внимания Правительства Нижегородской области</span>
    </h2>
    <!-- здесь оставшиеся контент -->
  </section>
</body>

Это моё решение. Я рекомендую использовать его, потому что для прочтения текста «Приоритеты Фокус внимания Правительства Нижегородской области» пользователю достаточно сделать одно взаимодействие. Правда Илья, сказал, что длинный текст это не очень здорово.


«В любом случае от длинных заголовков устаёшь больше, чем от коротких. Даже если прерываешь чтение клавишей H и идёшь дальше».


Третье решение от Ильи — оставить текст текстом. Это можно сделать, использовав элемент <p>.


<body>
  <section>
    <h2>Приоритеты</h2>
    <p>Фокус внимания Правительства Нижегородской области</p>
    <!-- здесь оставшиеся контент -->
  </section>
</body>

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


▍ Заключение


С помощью этой статьи мы с Ильёй хотели призвать вас:

  • если часть текста скрыта через паттерн, добавить к элементу role="presentation";
  • расположить кнопку «Показать пароль» после поля для ввода пароля;
  • переносить пользователя в поле для ввода пароля после использования кнопки «Показать пароль»;
  • не использовать в атрибуте aria-label слова, описывающие тип элемента;
  • Внимательнее относиться к разметке заголовков, чтобы не запутывать пользователей скринридера.

Оставлю ссылки на все выпуски:


Также нам будет интересен и ваш опыт. Делитесь своими кейсами в комментариях. Спасибо за чтение.


P.S. Если вы хотите больше узнать о цифровой доступности, добавляйтесь в мой ТГ канал. Ссылка в профиле.


P.S.S. Ещё я начал делиться своими практическими лайфхаками на Хабре. Серия статей называется «Магия CSS на практике: советы по вёрстке от гика»


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

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


  1. ifap
    09.07.2024 11:05

    Исправить такое поведение можно, скрыв отдельный элемент <span> с сохранением доступа к его тексту.

    Может просто не оборачивать контент в два span подряд?


    1. melnik909 Автор
      09.07.2024 11:05

      Всегда будут разработчики, которые обвернут.


      1. ifap
        09.07.2024 11:05

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


    1. ilekarev
      09.07.2024 11:05

      Может просто не оборачивать контент в два span подряд?

      Так то представлен вариант, если появилась необходимость в этом оборачивании. Да и в места span, полагаю вторым тегом какойнибудь strong может так же быть?

      Какая проблема тут использовать два span?

      <p class="price">
        <span class="price__first-line">Цена за товар:</span>
        <span class="price__second-line">
          <span class="price__value">94 682</span>₽ за штуку
        </span>
      </p>

      price__value для стилизации значения. Либо вставить в текст для простого поиска значений для использования в JS


      1. wadowad
        09.07.2024 11:05

        Такое семантичней или наоборот?

        <dl class="price">
          <dt class="price__first-line">Цена за товар:</dt>
          <dd class="price__second-line">
            <strong class="price__value">94 682</strong>&thinsp;₽ за штуку
          </dd>
        </dl>


        1. ilekarev
          09.07.2024 11:05

          Я не думаю, что это семантично :) Здесь нет списка, и нет элементов "термин - описание".


          1. wadowad
            09.07.2024 11:05
            +1

            У DL смысл несколько шире, чем просто "термин - описание", скорее это "ключ - значение". А список есть (тут правда только из одного пункта). Всё встаёт на свои места, когда появляются варианты:

            <dl class="price">
              <dt class="price__first-line">Цена за товар:</dt>
              <dd class="price__second-line">
                <strong class="price__value">94 682</strong>&thinsp;₽ за штуку
              </dd>
              <dt class="price__first-line">Цена со скидкой:</dt>
              <dd class="price__second-line">
                <strong class="price__value">253 199</strong>&thinsp;₽ за три штуки
              </dd>
              <dt class="price__first-line">Оптовая цена:</dt>
              <dd class="price__second-line">
                <strong class="price__value">650 000</strong>&thinsp;₽ за 10 штук
              </dd>
            </dl>


  1. kranid
    09.07.2024 11:05

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


  1. bubn0ff
    09.07.2024 11:05

    По всем этим нюансам во всех выпусках хорошо бы составить некий "чек-лист доступности".