company_banner

Доступность интерфейсов. Лекция Яндекса

  • Tutorial
Меня зовут Дима, я работаю в офисе Яндекса в Санкт-Петербурге и занимаюсь внутренними сервисами в команде разработки интерфейсов Толоки. В этом году я подготовил лекцию для Школы разработки интерфейсов. Ниже — её расшифровка.

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


— Что скрывается под модным нынче термином accessibility? Какие у вас есть варианты? Для слепых, чтение с экрана, с ограниченными возможностями, координация движений… Все верно. Доступность — возможность использования интерфейса всеми, независимо от физических или технических ограничений.

Доступный интерфейс — это интерфейс, который максимально удобен в использовании для широкого круга пользователей. Возможно, у кого-то возник вопрос, зачем делать интерфейсы доступными? Какие у этого есть мотивации? Первая и, по мне, самая важная мотивации — моральная. Люди с ограниченными возможностями здоровья ничем не хуже остальных, они тоже хотят и имеют возможность пользоваться полным функционалом ресурсов, которые вы разрабатываете.

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

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

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

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

Сейчас в России примерно 10% населения — люди с той или иной инвалидностью. И далеко не последнее место тут занимают люди с проблемами со зрением или опорно-двигательным аппаратом. И все эти люди — потенциальные посетители вашего сайта. Им особенно важна доступность.

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



В качестве примера хочу привести эту штуку. Этот тип девайсов называется Switch. Доступность — это не только про компьютеры и ноутбуки, это еще и про мобильные устройства. Человек с ограниченными возможностями может зайти на ваш сайт с телефона или планшета. Это устройство подключается к iOS- или Android-девайсу, и на его кнопки можно назначить какие-то пользовательские действия. Например, этот Switch — с двумя большими кнопками, удобно назначить на него управление фокусом на странице.



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

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

Программных технологий тоже достаточно. Это инструменты для увеличения изображения на странице, например, экранные лупы, которые встроены в Windows, macOS, это программные модификаторы цветовой гаммы и прочее. Например, есть софт, который позволяет управлять интерфейсом с помощью движения глаз и головы. В macOS такая программа встроена, она называется Dwell Control. Но среди программных технологий особое место занимают скринридеры. Это специальное приложение, которое зачитывает пользователю содержимое сайта и ОС, предоставляет ему удобный функционал по навигации.

Скринридеры — это самый доступный и распространенный способ воспринимать информацию для людей с нарушением зрения. Более подробно мы о них поговорим в ходе лекции.



Пару слов про рекомендации в области доступности интерфейсов. У нас это ГОСТ, а на Западе — WCAG и Section 508. Способы, описанные в данных рекомендациях, не влияют на внешний вид сайта, но предоставляют пользователям с ограниченными возможностями какими-то дополнительные штуки для навигации и пользования.

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



То же самое касается графики и иконок на странице. Помните: они также нуждаются в дополнительном сопровождении. Если какой-то дополнительный элемент на вашей странице представлен в виде изображения, то его необходимо скрыть от скринридера с помощью атрибута aria-hidden=“true”.



Про атрибуты с приставкой aria поговорим дальше.

Необходимо описывать поля ввода с помощью тега label, у которого мы указываем атрибут for со значением id поля ввода. Это не только позволит быстро перейти к редактированию контрола по нажатию на лейбл, но также семантически свяжет контрол с его описанием, то есть скринридер зачитает нам описание этого элемента. Указывает типы данных у полей ввода.



Помимо валидационных правил, которые автоматически добавятся к этим инпутам, во многих случаях будет представлен удобный кроссбраузерный способ ввода информации. Например, как здесь, календарь в случае с типом Date или Сolor, если это цвет. Если браузер не поддерживает какой-то из типов, то будет подставлен input с типом text, ничего не сломается.



Помечайте обязательные поля с помощью атрибутов required или aria-required=”true”. Группируйте связанные поля с помощью тега fieldset, с помощью элемента legend указывается заголовок группы, где можно прописать не только назначение группы, но и общие характеристики полей (например, что все поля в этой группе обязательные).



Три радиокнопки оборачиваем в тег fieldset, задаем заголовок в теге legend. Теперь вне зависимости от того, какая из кнопок выбрана, скринридер прочитает содержимое тега legend. И человек, который посещает сайт, поймет, что происходит в этой форме.

Выводите сообщения об ошибках и успехе.



Про title и так понятно, текст в этом теге должен описывать назначение или название страницы. Если у вас single page application, то не забывайте менять содержимое тега title при навигации по странице.

Указывайте язык страницы. Для этого надо установить атрибут lang в теге HTML. Это хорошо для SEO, это помогает плагинам-переводчиком, а также скринридеры точно определяют язык такой страницы. Если у вас в документе встречается несколько языков, то вы можете указать атрибут lang для отдельных тегов. Соблюдайте валидность верстки. Это важно, потому что скринридеры могут неправильно воспринимать невалидные страницы. То же самое касается семантики. Все элементы на странице должны быть семантически верные, вы должны использовать теги HTML по их смысловому назначению. А когда вы создаете свои кастомные элементы, то позаботьтесь о правильной семантике.



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



Еще один пример важности семантики — верстка меню. Если сделаете меню как слева, с точки зрения семантики это будет просто группа. div — это группирующий тег, и внутри него будет какое-то количество несвязанных элементов, в этом случае просто три ссылки.

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



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



Про адаптивную верстку и мобильную версию. У вас уже была лекция по адаптивной верстке, надеюсь, вы понимаете важность этого.

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



Они отличаются только надписью, но в действительности только левая кнопка является кнопкой. В основе ее лежит тег button. Правая — просто стилизоварованный div. Поэтому в левую кнопку мы можем сфокусироваться — button по умолчанию фокусируемый элемент. У кнопки есть состояние disabled, в котором она не может быть нажата. И клик по кнопке происходит по нажатию Enter и Space, если мы в фокусе кнопки. И только у этой кнопки правильная семантическая роль, с точки зрения браузера, скринридера это кнопка, и когда туда попадет скринридер, он так и скажет, что это кнопка. Всего этого нет у кнопки на основе div. Следующие пункты вам придется реализовывать самостоятельно. Но зачем, если есть встроенное хорошее браузерное API, которое можно использовать?

О доступности важно задумываться до этапа разработки и верстки. Кто когда-либо на работе или своих сайд-проектах занимался проектированием или отрисовкой дизайна? Нас точно больше половины зала. Пару советов в этом направлении. Не допускайте в появлении своих интерфейсов слишком мелких или сливающихся с фоном элементов.



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

О формах мы говорили, но больше с точки зрения семантики. Здесь же обратите внимание на грамотное проектирование пользовательского взаимодействия с формами, причем с учетом особенностей некоторых пользователей. Например, есть люди с проблемами с моторикой, им бывает трудно попасть по маленьким элементам. Поэтому сделайте у таких элементов область кликов больше, чем контрол или кнопка. То же самое касается элементов, расположенных близко друг к другу. Человек, у которого проблема с моторикой, может промахнуться и нажать не на то. Разносите важные элементы дальше друг от друга, и обязательно запрашивайте подтверждение каких-то необратимых операций, например, удаления данных.

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

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

Прежде чем разбираться в тонкостях, давайте посмотрим, как это работает под капотом с точки зрения браузера. После загрузки вашей страницы, браузер начинает парсить HTML-разметку, и на основе нее строит DOM-дерево. Надеюсь, вам эта структура известна, на ней основывается отображение данных в браузере, ее можно менять с помощью JS и т. д. Когда DOM-дерево построено, браузер строит на основе него другую структуру данных — accessibility tree. Это дерево содержит в себе информацию, полезную с точки зрения доступности. Эту информацию берут себе ассистивные технологии, например, скринридеры. И предоставляют пользователю какой-то удобный способ взаимодействия с сайтом. В процессе этого взаимодействия DOM может меняться, поэтому браузер следит за изменениями в DOM и актуалиазирует accessibility tree по необходимости. Ассистивные технологии забирают эти изменения и как-то модифицируют функционал, который они предоставляют своему пользователю.



Это изолированная структура данных. К ней нельзя получить доступ из DOM, ее нельзя просмотреть или отредактировать с помощью JS. Реализацией и управлением данной структуры полностью занимаются браузеры. Хранится в этом дереве информация о семантике элементов, с помощью которой ассистивные технологии понимают, как интерпретировать элементы со страницы.

Доступ к этой информации сейчас можно получить лишь с помощью специальных инструментов. Один из таких элементов — DevTools Accessibility Inspector. Сейчас инспектор доступности Chrome находится в разделе экспериментальных технологий, включить которые можно, зайдя на страницу Chrome Flags. Вы включаете эти технологии, потом идете в DevTools и включаете саму панель accessibility. Так она выглядит в деле.



Здесь показана различная полезная для доступности информация. И сразу возникает вопрос, откуда эта информация берется и как ее можно изменить?

Небольшие примеры.



Просто нативный чекбокс, обернутый в label. Так он будет представлен в accessibility tree. Мы видим, что это объект, у него есть поля, тип checkbox, имя, забранное из label, и состояние, которое может быть checked и unchecked.



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

Спецификация ARIA добавляет особые атрибуты, которые определяют то, как именно будет элемент представлен в accessibility tree, какими свойствами он будет обладать. Основное, что дает нам эта спецификация, — роли для описания типа элементов, свойства для описания его состояния.

Вкратце посмотрим, какими бывают роли и свойства, потом это закрепим на примерах. Начнем с понятия роли. Роль позволяет нам классифицировать элементы на странице. Устанавливается она добавляем к HTML элементу атрибута role c нужным значением.



Различных ролей в стандарте много, и они делятся на разные группы. Например, роли виджетов. Они назначаются элементам, которые являются независимыми частями пользовательского интерфейса. Это привычные нам кнопки, радиокнопки, табы, тултипы. Дальше идут составные роли, которые агрегируют элементы других ролей. Очевидно, radiogroup состоит из элементов radio или tablist состоит из tab. Далее следуют структурные роли и landmarks, ориентиры. Эти роли даются крупным семантическим блокам на странице.

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



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

Ролей и состояний много. Все их можно почитать на сайте спецификации.

Посмотрим на примеры наиболее часто используемых ролей.



Доделаем наш кастомный чекбокс. Берем тот же div, но теперь добавляем для него роль checkbox, а также атрибут, который показывает состояние, aria-checked, true или false. Теперь надо добавить соответствующий JS, и с точки зрения браузера, семантики это будет честный чекбокс. Таким же образом можно сделать другие элементы. Например, кнопку, которая будет выполнять роль свича, переключаться между состояниями. У нее есть своя роль switch и атрибут, обозначающий ее текущее состояние.

Пример составной роли. Есть список с элементом tablist, внутри него элементы с ролями tab. Таким образом мы говорим браузеру, подразумевая, что эти элементы являются tab.



Более сложный пример — многоуровневое меню. Помимо различных ролей, среди которых есть структурная роль — navigation, — здесь есть составные роли, вложенные друг в друга. Есть пара интересных состояний, например, aria-haspopup со значением true говорит о том, что этот элемент раскрывающийся. Или, например, aria-hidden со значением true, говорящая ассистивным технологиям, браузеру, что этот элемент в данный момент скрыт.

На этом слайде я хотел обратить внимание, что само добавление состояния, ролей никак не изменяет поведение элемента, его функционал. Есть элемент, вы сверстали его, написали JS, а потом с помощью этих атрибутов описываете его поведение, которое будет интерпретировано ассистивными технологиями, которые предоставят пользователю удобный способ взаимодействия. Добавление aria-haspopup не делает элемент раскрывающимся сам по себе. Или добавление aria-hidden не скрывает элемент со страницы, помните об этом.



Про атрибуты, устанавливающие связь между элементами. Этот атрибут позволяет указать, что один элемент управляет другим элементом. В данном случае это кнопка «показать настройки», которая скрывает или показывает блок под ними. Мы используем атрибут aria-controls со значением id блока, которым мы управляем. Похожий пример, но здесь мы ссылаемся на блок, который описывает наш текущий блок, используем атрибут aria-describedby. В этом случае скринридеры прочтут нам содержимое и предоставят пользователю больше информации о том, что произойдет по нажатию на эту кнопку.



Ни для кого не секрет, что сейчас почти все сайты в интернете динамические, то есть содержимое на них меняется без перезагрузки страницы. И тут с точки зрения доступности появляются проблемы. Как оповестить ассистивные технологии, те же самые скринридеры, что на странице произошло изменение? Для этого в aria ввели концепцию live регионов. Их можно создать либо с помощью роли alert, либо с помощью атрибута aria-live. Теперь скринридер будет отслеживать изменения в этих блоках, и, если изменение произошло, он будет зачитывать пользователю новое содержимое. Причем если вы укажете role=”alert” или aria-live со значением assertive, то произношение нового контента произойдет немедленно. Если сейчас скринридер зачитывал содержимое на странице, зачитывание прервется, и начнется зачитывание содержимого в live-region.

Если вы хотите, чтобы оповещение о новом контенте было более вежливым, то необходимо создавать live region с помощью атрибута aria-live со значением polite. Тогда он дождется, пока текущий контент будет произнесен, и произнесет новый контент после.



Важно помнить интересную особенность. Чтобы live регионы работали в разных браузерах на разных ОС, лучше создавать их статическими. Вы создаете элемент, где будет изменяться контент, делаете его пустым, прячете, например, визуально, а потом, в случае необходимости, просто добавляете новое содержимое туда и показываете. Тогда можно быть уверенным на 100%, что все ассистивные технологии, все браузеры на всех ОС точно и корректно его отобразят и зачитают.

Подробнее поговорим про управление с клавиатуры и о фокусе. Для людей, которые при просмотре сайтов пользуются преимущественно или только клавиатурой, существует два основных сочетания клавиш: Shift + Tab и Tab. Переход на следующий и предыдущий фокусируемый элемент. Когда мы говорим о фокусе и его переходе по странице, важно обеспечить правильную последовательность перехода фокуса.



Фокус идет по порядку HTML-разметки. Как элементы расположены в разметке, так фокус и будет идти. Если вы изменяете положение элементов визуально с помощью каких-то CSS-свойств, например, используя абсолютное позиционирование, float или свойство order у флексбоксов и гридов, то вы меняете положение элемента только визуально, в разметке он остается на прежнем месте.



Небольшой пример. Правая колонка находится справа, мы читаем слева направо, нам кажется, что она идет после. И ожидаемо должны попасть в нее после основного содержимого. Но в разметке правая колонка идет перед основным содержимым. И если человек зайдет на сайт с помощью скринридера или просто начнет навигироваться по нему с помощью клавиши tab, то сначала попадет в правую колонку, а потом в основную. Человек, который ваш интерфейс как-нибудь разберется, а незрячий человек может сильно запутаться.

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



Этим мы запрещаем людям, которые видят наш сайт, но пользуются им с клавиатуры, увидеть текущее местоположение фокуса. Непонятно, где мы сейчас находимся. Именно поэтому отключать ее полностью нельзя. Если вам не нравится внешний вид, то переопределите ее, причем необязательно использовать свойство outline, вы можете использовать box-shadow, border, что угодно, лишь бы она четко была визуально заметна на сайте.

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

Почему так работает, какая здесь разница? У браузеров есть свои внутренние эвристики, которые определяют, должен отобразиться focus ring вокруг элемента или нет. Доступ к этим эвристикам можно получить через такой интересный псевдоселектор.



Он применяется к элементам, для которых в текущий момент браузер считает нужным отобразить focus ring. Если мы хотим сделать наоборот, отображать обводку вокруг сфокусированного элемента только если пользователь навигируется по сайту с помощью клавиатуры, то мы пишем такой не совсем тривиальный селектор, который дословно можно прочитать так: если элемент фокусируемый, но в текущий момент браузер считает, что вокруг него фокус показывать не нужно, тогда мы обводку убираем. Эту технологию можно использовать, когда создаешь свои кастомные компоненты. Но, как это обычно бывает во фронтенде, все крутые штуки сейчас не работают. Именно поэтому эта технология экспериментальная, сейчас в браузерах недоступна, ее можно использовать только с помощью полифилла, ссылку на него я оставлю в конце презентации.



Интерактивные элементы, кнопки, ссылки, поля ввода по умолчанию попадают в порядок перехода фокуса по табу. Но что делать, если мы хотим добавить в этот порядок собственные элементы или элементы, которые по умолчанию являются не фокусируемыми? Тут на помощь приходит атрибут tabindex. Если установить его в значение 0, элемент станет фокусируемым. Плюс он добавится в поток фокуса. Поток, в который мы попадаем по нажатию tab.



У нас есть фокусируемый и нефокусируемый элементы и div между ними. Если мы будем навигироваться по сайту с помощью tab, мы попадаем в первую кнопку и вторую кнопку. Если добавим для второго блока tabindex=0, то он станет фокусируемым и попадет в порядок фокуса, станет вторым.



Если элементу задать tabindex=-1, он станет фокусируемым, но в порядок фокуса не попадет. Однако ему можно фокус поставить с помощью JS.



tabindex > 0 задает фактически порядок фокуса. Имея три нефокусируемых элемента, мы можем сделать их фокусируемыми, причем порядок фокуса будет указан явно этими индексами.

tabindex > 0 — это мощный инструмент, но пользоваться им нежелательно. Любое изменение в верстке, в дизайне может сломать эту последовательность, и тогда ваш интерфейс станет непредсказуемым. Так делать нельзя. Грамотное управление фокусом в особых ситуациях. Приведу пример двух компонентов, где используется особое управление фокусом. Этот попап, по-моему, из четвертого Bootstrap.



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

Другой пример, material design, есть основное содержимое и меню слева. Здесь похожая ситуация, пока меню скрыто, мы не должны попасть в него фокусом. Мы ходим табом по странице, никуда не проваливаемся. Как только меню открывается, мы фокус ставим в первый пункт, и потом можем ходить фокусом по остальным. Всё, меню закрылось, оно инертно для фокуса, мы в него попасть никак не можем. Чтобы не получилось так, что пользователь после нескольких нажатий на tab проваливается куда-то и не видит текущего положения фокуса.

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



Он задается просто элементу, и работает очевидно. Мы ставим inert для элемента, он и все его дочерние элементы становятся инертными для фокуса. Эта штука крутая, и, как и любая крутая штука, сейчас не работает. Эта технология экспериментальная, для ее работы нужен полифилл.



Другие решения, которые можно использовать с помощью JS. Существует большое количество библиотек для решения этой проблемы. Одна из них — Focus Manager. Она реализует два метода. Метод сapture захватывают фокус на каком-то элементе. Функция release принимает аргумент, куда вернется фокус после того, как мы его отпустим. Опять вспоминаем модальное окно, по закрытию модального окна возвращается фокус в тот элемент, кнопку или что угодно, откуда он пришел. Это круто и удобно, это надо делать.

А другое решение для реакта — просто обертка, которая не дает фокусу выйти за его пределы. Импортируешь и используешь — даже думать не надо.

Многие библиотеки модальных окон для реакта (например, React Modal) уже из коробки имеют такую логику управления фокусом.

Скринридеры являются самой удобной и доступной ассистивной технологией. Какие скринридеры бывают?

Первый на очереди VoiceOver, который идет с операционными системами Apple, это не только macOS, но и iOS, WatchOS, и он предоставляет нативный функционал по взаимодействию с интерфейсом для людей с ограниченными возможностями. Это классный инструмент, простой в использовании, функциональный, там большое количество голосов, настроек, можно очень удобно под себя настроить. Но проблема очевидна, он только под macOS.

Если смотреть на скринридеры для Windows, первым на очередь приходит NVDA. Это бесплатный скринридер. По сути, он реализует то же самое, что VoiceOver, любые скринридеры делают примерно то же самое, но он менее функционален, менее удобен, более сложен в настройке.

Третье решение для Windows — платный скринридер JAWS. Классная, функциональная, мощная штука, такая же настраиваемая как VoiceOver, но за него надо платить.

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

Настройки скринридера в macOS находятся во вкладке Accessability или «Универсальный доступ», там есть раздел VoiceOver. Нажимаете на галочку “включить VoiceOver” или комбинацию Cmd + F5. Уверен, кто пользуется маками, случайно много раз так делал, потом долго искал, где это выключить. Нажимаем, и поехали.

Добро пожаловать в macOS. Скринридер включается, сразу начинает говорить. Все, что он проговаривает, видно в черном прямоугольнике снизу. Свернем это меню, зайдем в браузер и нажнем tab, чтобы попасть в первый элемент на странице.



Это главная Яндекса. Мы попадаем в первый элемент на странице. Сразу обратите внимание, что focus ring по умолчанию переопределен, он в стиле Яндекса. Что мы видим, когда попали на этот элемент?

Во-первых, скринридер сказал, что это ссылка. Он произнес заголовок этой ссылки. Во второй строке он показал то, какому элементу принадлежит эта ссылка. Она принадлежит блоку с ролью complimentary, это дополнительный блок на странице. Цифра 2 говорит, что помимо этой ссылки там еще какой-то элемент, то есть их два. Нажимаем tab и попадаем во второй элемент этого блока.



Попали в меню настройки. Скринридер зачитал правильную роль — это всплывающая кнопка. Вспоминаем, что это aria-haspopup. Эта кнопка также имеет дополнительное состояние, она свернута, и так как это кнопка, мы можем удобно нажать на нее с помощью Enter или пробела. Нажимаем пробел:



Статус поменялся, переходим дальше.



Мы прошлись по всем ссылкам в этом popup. Обратите внимание, что про просмотренные ссылки он так и говорит, что она просмотрена. Вспоминаем про фокус менеджмент. Мы говорили, что в подобных элементах круто, если бы фокус не вываливался вовне, а циклично в них ходил. Сказано — сделано. В этом элементе мы не можем фокусом попасть вне, это круто и удобно.

Возвращаемся в настройки, идем по странице дальше.

Мы попали в блок новостей. Судя по типу, это tablist, он выбран, у него стоит нужный статус. Ожидаемо мы ждем от него перемещения по остальным табам с помощью стрелок. И действительно, нажимая на стрелки, мы будем ходить по тегам.



Переходим внутрь.



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



Попадаем в кнопку, у которой есть альтернативный текст. Если бы его не было, мы бы просто не поняли, что это такое, там иконка. Но человек со скринридером этой иконки не видит, поэтому альтернативный текст. Очевидны роли и состояние, что это всплывающая кнопка и прочее.

Открылся попап, закрываем его с клавиатуры, идем дальше, пропуская блок новостей.





У нас есть погода, но, если бы не было альтернативного текста у иконки, мы бы не поняли, какая погода сейчас в Питере. Хотя, может, это и так понятно.

Идем дальше, поскольку порядок элементов не перепутан, нажимая tab, мы переходим по разметке.

Но если страница большая, ходить так с помощью tab или shift+tab не очень удобно. Поэтому скринридеры часто предоставляют удобный функционал по навигации по вашему сайту. В VoiceOver такой инструмент называется ротор, он открывается по нажатию Ctrl+Cmd+U.



Вот меню, которое содержит в себе различные типы сущностей на странице. Здесь первыми оказались ориентиры. Это те самые landmarks, специальные роли.



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



Это заголовки. Мы говорили о важности заголовков. Здесь все заголовки первого уровня, но они позволят удобно перейти, например, к пробкам или телепрограмме. Ссылки — то же самое.



Ротор, как и VoiceOver, очень кастомизируемый, сюда можно добавить много других сущностей.

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



Давайте обозначим, на что следует обращать особое внимание при обеспечении доступности сайта, при проверке сайта на доступность. Начнем с элементов на странице, верстки и семантики. Верстка должна быть валидной и правильной с точки зрения семантики и микросемантики ваших элементов, макросемантики всей вашей страницы. Изображения и медиаконтент должны быть доступными, как и формы и ваши кастомные компоненты. Вы видели, что скринридер часто использует роли и состояния, и во многом благодаря ролям можно понять, что сейчас происходит на странице. Главное, чтобы ваш сайт был читаемым для всех, у кого плохой монитор или плохое зрение. Необходимо заниматься грамотным проектированием элементов. Ну и консистентность. По сайту должно быть удобно перемещаться с помощью Tab или Shift +Tab. Все интерактивные элементы должны быть достижимы, а все скрытые элементы — недостижимы для фокуса.

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

Accessibility Inspector. С его помощью удобно смотреть, что под капотом у сайта, какая семантика у вашей верстки.

Вкладка Audits в DevTools. По-моему, она появилась с 60-м Chrome. Удобно смотреть, и он подсказывает, что у вас не так на сайте, что можно поправить и в каком месте.

Штука, похожая на Audits, — вкладка Axe. Это набор инструментов, позволяющий провести автоматизированный тест доступности вашего сайта. В простом виде он доступен как расширение для браузера. Вы просто скачиваете его, открываете нужный сайт и видите следующую картину. У вас показывается список проблем, описание проблемы, где она находится и, возможно, способ ее решения.



Также Axe представлен в виде веб-драйвера для Selenium. Если вы хотите сделать проверку на доступность частью вашей тестовой инфраструктуры, — пожалуйста, используйте. Настраивать быстро и удобно. Axe CLI — по сути, то же, что и расширение для браузера, только в вашем терминале. Под капотом там Phantom JS.

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

Туториал от Mozilla;
Сайт Веблайнд с советами по доступности на русском языке, очень круто оформленный;
A11ycast — видео на YouTube от ребят из Google, подкаст, там много классной информации;
Inclusive components интересен с точки зрения того, как конкретно реализуются доступные компоненты. На этом сайте различные статьи, которые пошагово показывают, как реализовывать разные привычные нам компоненты дизайна.

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

Ссылки на материалы из презентации

ГОСТ Р 52872-2012
WCAG 2.0
Полифилл для inert
Полифилл для :focus-visible
Focus manager
React Focus Lock
Axe Core
Яндекс
Как мы делаем Яндекс

Comments 10

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

      Довольно спорное утверждение, на сколько я могу судить, не существует какого-то нормативного акта, который бы однозначно утверждал, что все государственные сайты должны соответствовать ГОСТу доступности с одной стороны, но с другой стороны в тексте ФЗ №181 утверждается, что инвалиду гарантируется право на получение необходимой информации, и это правило не подразумевает никаких исключений, таким образом предполагается, что абсолютно все Российские сайты должны соответствовать этому ГОСТу.
      Попали в меню настройки. Скринридер зачитал правильную роль — это всплывающая кнопка. Вспоминаем, что это aria-haspopup. Эта кнопка также имеет дополнительное
      состояние, она свернута, и так как это кнопка, мы можем удобно нажать на нее с помощью Enter или пробела. Нажимаем пробел:

      Статус поменялся, переходим дальше.

      Паттерн Menu Button Предполагает, что при нажатии этой кнопки фокус переместится на первый пункт открывшегося меню и дальше пользователь сможет перемещаться по этому меню с помощью клавиш up/down arrowkeys, но в реализации яндекса это работает совсем иначе. После того как пользователь активирует кнопку «Настройки», фокус никуда не перемещается, а остается на этой кнопке и дальше пользователю нужно догадаться, что ему следует перемещаться по пунктам menu с помощью клавиши tab. Кому-то покажется, что небольшая разница, но дело в том, что этот способ взаимодействия совершенно не очевиден для пользователя скринридера. Пользователь множество раз раньше встречал меню, он знает как нужно им пользоваться, но в данном случае яндекс изобрел свой путь и как это работает понятно далеко не сразу. Более того, в некоторых случаях это вовсе не работает. К примеру, в jaws+chrome Tab не перемещает фокус по пунктам меню, а после активации кнопки просто изменяется состояние самой кнопки, а меню открывается внизу страницы и пользователю, который этого не знает, абсолютно непонятно, где его искать. Очевидно, что сложно протестировать все возможные сочетания браузеров и скринридеров, хотя и можно попытаться, но вот именно поэтому и стоит реализовывать элементы управления следуя устоявшимся паттернам.
      В данном меню есть еще одна ошибка, элемент с ролью menuitem оборачивает тег «a»:
      <div role="menuitem"><a href="https://yandex.ru/tune/geo/?retpath=https%3A%2F%2Fwww.yandex.ru%2F%3Fdomredir%3D1&nosync=1" class="link i-counter-toggler i-bem link_js_inited" aria-label="Изменить город" target="_self" data-statlog="head.settings.region" data-statlog-showed="1">Изменить город</a></div>
      

      , что приводит к эффекту, когда в одном случае скринридер называет элемент пунктом меню, в другом ссылкой, а в третьем и пунктом меню, и ссылкой одновременно. Роль menuitem нельзя использовать так, элемент управления должен быть либо ссылкой, либо пунктом меню, аналогично тому как нельзя вставлять ссылку в кнопку или checkbox в ссылку.
        +1
        Довольно спорное утверждение, на сколько я могу судить, не существует какого-то нормативного акта, который бы однозначно утверждал, что все государственные сайты должны соответствовать ГОСТу доступности с одной стороны, но с другой стороны в тексте ФЗ №181 утверждается, что инвалиду гарантируется право на получение необходимой информации, и это правило не подразумевает никаких исключений, таким образом предполагается, что абсолютно все Российские сайты должны соответствовать этому ГОСТу.
        Согласно требованиям закона 419-ФЗ, все сайты государственных учреждений должны быть приспособлены для слепых и слабовидящих. На государственных сайтах помимо внедрения техник доступности в основную версию сайта, можно часто увидеть и специальную версию для слабовидящих (с упрощённой разметкой, настройками цвета и шрифтов).

        По поводу ФЗ №181 вы абсолютно правы, но реальность такова, что процент сайтов, которые этому ГОСТу соответствуют, крайне мал, и с этим ничего не делается.

        Паттерн Menu Button Предполагает, что при нажатии этой кнопки фокус переместится на первый пункт открывшегося меню и дальше пользователь сможет перемещаться по этому меню с помощью клавиш up/down arrowkeys, но в реализации яндекса это работает совсем иначе...
        Спасибо за фидбек. Передам коллегам из команды главной.
          +1
          Согласно требованиям закона 419-ФЗ, все сайты государственных учреждений должны быть приспособлены для слепых и слабовидящих.

          К сожалению, все, что мне удалось найти в 401 это:
          обеспечения условий доступности для инвалидов по
          зрению официальных сайтов федеральных органов государственной
          власти, органов государственной власти субъектов Российской
          Федерации и органов местного самоуправления в сети «Интернет»
          устанавливается уполномоченным Правительством Российской Федерации
          федеральным органом исполнительной власти.

          А таким федеральным органом является, видимо, Министерство связи и массовых коммуникаций, которое издало два приказа на эту тему, один из которых вообще не упоминает Гост, а другой просто "рекомендует" ему соответствовать.
          По поводу ФЗ №181 вы абсолютно правы, но реальность такова, что процент сайтов, которые этому ГОСТу соответствуют, крайне мал, и с этим ничего не делается.

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

            0
            К сожалению, все, что мне удалось найти в 401

            Прошу прощение, имелось ввиду конечно же 419.
        +1
        Здравствуйте! Меня зовут Вадим. Я незрячий пользователь инструментов от яндекса. Пару недель назад я установил на свой андроид смартфон приложение яндекс.почта, и через 5 минут его удалил, потому что оно совершенно не оптимизировано под доступность для скринридеров. В связи с чем очень я бы вас просил прочитать вашу лекцию подразделению мобильной разработки вашей компании. Спасибо!

        P.S. Очень жаль, что вы вряд ли имеете доступ к разработчикам mail.ru group. А то официальное приложение вконтакте для андроида и клиент mail.ru также недоступны/не удобны/совершенно не оптимизированы. Если честно, стыдно даже. Такие большие компании и совершенно не парятся по поводу таких элементарных вещей, как, например, информативный тег alt на веб-страницах, и подписание графических кнопок в приложениях…

        P.P.S. Веб версия яндекс.почты вполне доступна, спасибо! Но пользоваться ей с помощью скринридера все равно не слишком удобно, т.к. она совершенно не размечена заголовками, графическими элементами или еще хоть чем-нибудь и представляет собой, по сути, сплошной поток ссылок, что совершенно неудобно.
          +1
          Здравствуй. Спасибо за комментарий. Передам коллегам из мобильной почты.
          0
          Часто заказчик уже обладает нарушением цветовосприятия, так что уже заложено на стадии макета
            0
            Было бы неплохо, если бы Яндекс следовал этим рекомендациям.
            Некоторые продукты Яндекса просто ужасны с точки зрения доступности.
            Web-версия Диска — яркий пример того, как не нужно делать. Множество кастомных элементов управления, которые воспринимаются скринридерами просто в качестве текста. И хоть интуитивно понятно, что они кликабельные, но добавление role=«link», или role=«button» значительно упростило бы пользование. И это только один пример.
            Или, скажем, yandex.passport, выпадающий список с месяцами во время регистрации. Из под Jaws он не работает совершенно. Из под NVDA с ним можно справиться, но только в режиме плоского просмотра, что не всегда (не всем) очевидно.
            Вы пишете, что картинки должны сопровождаться альтернативным текстом, грубо говоря иметь осмысленный атрибут alt, но даже в этой статье не одна картинка не имеет описания.

            Хотя, справедливости ради, хочу отметить, что вопреки распостранённому мнению, не каждая картинка должна иметь описание.
            Если то, что изображено на картинке можно понять исходя из контекста и это нельзя (нецелесообразно) конкретизировать при помощи описания, то и вставлять простыню пространного текста в alt не нужно. Ибо только время отнимет у читающего. Но при этом, картинка всё равно должна иметь непустой атрибут alt. Т.к. скринридеры игнорируют такие изображения. И пользователь может даже не узнать что там есть картинка. В противном же случае, он может попросить описать изображение зрячего человека.
            Если какой-то дополнительный элемент на вашей странице представлен в виде изображения, то его необходимо скрыть от скринридера с помощью атрибута aria-hidden=“true”.

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

            Что касается атрибута aria-hidden. Его упоминают почти во всех рекомендациях по доступности. Но как разработчик и постоянный пользователь скринридеров, хочу отметить, что очень часто его применяют неправильно. Более того, в подавляющем большинстве случаев, его применение избыточно.
            Классический пример неправильного применения:
            Есть изначально скрытый блок, который необходимо показать в ответ на какое-либо действие пользователя.
            [div style="display: none;" aria-hidden="true"]...[/div]

            Свойство display изменяют, скажем, на block, но об aria-hidden совершенно забывают. После чего мы получаем, что блок визуально отображается, но остаётся недоступным для скринридеров.
            В данном случае aria-hidden является избыточным. Все скринридеры вполне успешно умеют не читать элементы с display: none. одновременное использование display: none и aria-hidden=«true» — просто ещё одна возможность накосячить на ровном месте.
            aria-hidden стоит применять только в случае, если вам необходимо сохранить визуальную видимость элемента, но сделать его нечитабельным для скринридеров. В прочем, такая необходимость встречается не так уж и часто.

            Использование дополнительной текстовой информации для элементов при помощи aria-label или aria-description тоже должно рассматриваться в каждом конкретном случае отдельно. Иногда это может лишь усложнить взаимодействие пользователя с интерфейсом.
            Мы все не любим раздутые визуальные интерфейсы, но многие считают что напихать кучу дополнительной информации через те же aria-атрибуты — не будет лишним.
            Отсутствие зрения !== отсутствию интеллекта. Во всяком случае, не всегда)))
            Если предназначение кнопки понятно из самой подписи на кнопке, то любая дополнительная информация будет лишней.
            У нас есть некая кнопка по нажатию на которую должен выпадать некий список:
            [button type="button" aria-haspopup="true" aria-expanded="false"]Actions[/button]

            Скринридер на этой кнопке нам прочитает что-то вроде: actions кнопочное меню свёрнуто (произносимая информация может несколько отличаться у разных скринридеров).
            Добавим aria-label:
            [button type="button" aria-haspopup="true" aria-expanded="false" aria-label="Какой-то текст"]Actions[/button]

            Теперь получаем: какой-то текст кнопочное меню свёрнуто.
            Проверялось в NVDA, Jaws и TalkBack.
            Прошу заметить, что при использовании aria-label в данном случае текст, который расположен между открывающим и закрывающим тегами button вообще игнорируется.
            Но когда это может быть полезным?
            Например, если в кнопке используется какая-то векторная иконка и отсутствует текстовая подпись, но мы хотим указать на предназначение кнопки.

            Тут уже писали об интерфейсе почты. Кстати, облегчённая версия web-версии почты — сделана просто идеально с точки зрения доступности. И этот тот редкий случай, когда aria-label применялся правильно и в тему.

            Ну и несколько рекомендаций.
            1. Необходимо продумывать реализацию доступности точно так же, как вы продумываете визуальный интерфейс.
            2. Избыток иногда хуже недостачи
            3. Невозможно сделать интерфейс максимально привлекательным с точки зрения доступности без реального опыта в этом деле и/или хорошего тестирования только на основе каких-то рекомендаций.

            4. П.С. парсер режет теги, не Так и не смог написать правильно.
              0

              Отличная лекция и плохая расшифровка :(


              Самое плохое — слайды с текстом и кодом картинками. Это противоречит почти всему, чему учит лекция. Как так? Ну и убрать из текста контекст ШРИ, сделать его полноценным и независимым тоже несложно, но это скорее про редактирование.

              Only users with full accounts can post comments. Log in, please.