Разработка доступных интерфейсов

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

    Хорошие примеры доступных сайтов у Пенсионного Фонда и Госуслуг. Чтобы ваш ресурс стал доступным, такой масштабной работы, как для ПФР, не требуется. Достаточно применить три принципа, добавить в закладки два сайта с подробной документацией по разработке доступных сайтов и немного адаптировать рабочий процесс под новую парадигму. В результате ваши ресурсы перейдут на новую ступень: будут доступными и удобными для людей с ограниченными возможностями.

    О том, как быстро и эффективно разрабатывать доступные ресурсы сегодняшняя расшифровка доклада Сергея Кригера на Frontend Conf.


    Доступность в вебе


    Наш докладчик — Сергей Кригер — фронтенд-разработчик веб-студии SinnerSchrader, создающей веб-приложения для Альянс, Ауди и БМВ и других компаний. Основные интересы Сергея: JavaScript, разработка интерфейсов и доступность. Про доступность поговорим подробнее.

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

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

    Чтобы наши сайты были удобны всем пользователям, мы научимся разрабатывать доступные веб-интерфейсы на реальных примерах.

    Разработка


    Как строится обычный процесс разработки?

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

    Часто в ТЗ нет ни слова про доступность и я считаю, что это проблема.

    По данным Росстата людей с проблемами зрения около 1 000 000. Верстая сайты без акцента на доступность мы создаем ресурсы, которыми не смогут пользоваться 1 млн. пользователей.

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

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

    Для удобства разобьем процесс создания виджета на 4 части:

    • верстка кнопки;
    • модального окна;
    • меню;
    • сборка виджета.

    Начнем работу с кнопки.

    Кнопка


    Самый простой из интерактивных элементов: прекрасно стилизуется и хорошо работает с JavaScript.

    В макете дизайнера кнопка выглядит так:



    Обычный вид кнопки и второстепенный.

    Ниже первый вариант разметки:


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

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

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

    Шаг 1. Фокус


    У элементов есть много состояний. Для работы с ними существует целый набор псевдоклассов, но мы рассмотрим 3 основных:

    • :hover — наведение;
    • :focus — фокус;
    • :active — нажатие.

    Задача кажется простой — добавить псевдоклассы к нашим элементам и готово, но есть одно «но».

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

    Иногда бывает, что в макете нет всех состояний. Часто у нас есть всего 2 вида: обычное и состояние наведения.



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

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

    Шаг 2. Клавиатура


    Кнопки на странице находятся не просто так. При клике на кнопку должно происходить какое-то событие.

    На доступном ресурсе все, что можно сделать на при помощи мышки, должно быть продублировано клавиатурой.

    Мы не будем придумывать как наша кнопка должна работать с клавиатурой. На сайте w3.org собрано множество элементов с прописанным сценарием поведения при взаимодействии с клавиатурой, скринридером и мышкой. Документацией на сайте мы будем руководствоваться на всех шагах разработки доступных ресурсов.



    В документации сказано, что кнопка становится активной при нажатии пробела или Enter на клавиатуре.

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

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


    • Когда кликаем кнопку «Count», счетчик увеличивает значение. Кликаем соседнюю кнопку, и счетчик сбрасывается.
    • Если же мы клавиатурой через Tab сфокусируемся на кнопке и нажмем пробел, то увидим, что все работает так же.
    • Для того, чтобы скинуть результат, нам нужно перескочить на вторую кнопку и нажать пробел — результат скинется.

    Мы научили наши кнопки правильно держать фокус и работать с клавиатурой. Пойдем дальше.

    Основной совет, который я хотел бы дать: если есть возможность использовать нативные браузерные интерактивные элементы — используйте. Это несложно и они уже поддерживают стили, клавиатуру и даже скринридер.

    Шаг 3. Скринридер


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


    • Выключим цвет на счетчике, чтобы протестировать скринридер.
    • Если перескочить Tab’ом на первую кнопку, то скринридер укажет, что объект — кнопка и озвучит название поясняющее, что кнопка обновляет счетчик.
    • Результат счетчика сринридер не озвучивает.
    • Попробуем сбросить результат и перескакиваем на вторую кнопку.
    • Скринридер говорит, что это кнопка, но мы не имеем представления, что она делает. Пользователь не станет кликать на кнопку, в которой не уверен.

    Наши кнопки пока неправильно работают со скринридером, но мы можем это исправить.

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

    Попробуем разобраться, почему так происходит сейчас.

    По умолчанию скринридер читает текст, который расположен внутри кнопки. В коде для кнопки «Сбросить» нет никакого текста, а просто сама кнопка и элемент внутри — SVG иконка.

    <button>
        <svg>
            <use xlink:href=”#reset”></use>
        </svg>
    </button>
    

    В документации сказано, что для случая когда у кнопки нет текста, можно добавить её описание вручную при помощи атрибута arial-label.

    Кнопке с текстом добавляем атрибут arial-label и любой подходящий текст, например «Reset counter». Элементу counter, который выводит большие цифры результата на экран мы добавим атрибут arial-live. Этого нет в документации, но мы так поступим чтобы пользователь мог получить реальный feedback и узнать результат счетчика.

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

    Таким простым образом при помощи 3 шагов мы сделали наши обычные кнопки доступными.

    Модальное окно


    Вид окна в макете:



    Окно верстается просто: фиксированный элемент, центрированный горизонтально и вертикально с затемненным фоном.

    В нашем случае модальное окно состоит из двух элементов:

    • кнопки, которая вызывает модальное окно;
    • непосредственно окна.

    Начнем с кнопки. Вот первая версия нашего модального окна:

    После клика на элемент с иконкой шестеренки откроется модальное окно. В нем есть текст и две кнопки: «Согласиться» или «Отменить». Механизм работает с мышки, но мы знаем, что этого недостаточно. Чтобы сделать элемент доступным, снова пройдем 3 шага: работа с фокусом, клавиатурой и скринридером.

    Шаг 1. Фокус


    Включаем размытый экран и тестируем.


    До и после иконки добавлен текст, чтобы проследить движение фокуса.

    • Кликаем Tab’ом первый раз — фокус уходит на первый элемент.
    • Кликаем второй раз — и фокус, минуя иконку, переходит на вторую ссылку.

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

    Наша иконка — обычный svg-элемент:

    <svg viewBox=”0 0 32 32”>
        <use xlink:href=”#gear”/>
    </svg>
    

    Для JavaScript нет никакой разницы, какой элемент вы кликнете, чтобы открыть модальное окно. Можно поставить обработчик на любой элемент и он сработает. Несмотря на это, хорошей практикой считается использовать элементы по назначению, поэтому мы будем использовать svg-элемент для отрисовки иконки, а кнопку — для обработки кликов.

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

    • Дописываем стили и изменяем код.
    • Проверяем, как работает иконка — на ней уже можно сфокусироваться. Мы знаем, что если сейчас кликнуть пробел, то сработает клик на иконку и откроется модальное окно.
    • Кликаем пробел — работает. Отлично!

    С кнопкой разобрались. Протестируем как с фокусом работает модальное окно.

    • Продолжим кликать Tab’ом и видим, что при первом клике фокус уходит на ссылку после иконки, что уже выглядит странно.
    • Продолжаем кликать. Фокус заходит в модальное окно и переходит на кнопки. Если продолжить кликать, то фокус уходит со страницы. Непонятно, как пользователи будут работать с этим модальным окном.

    Чтобы решить проблему читаем документацию.



    Зеленым выделено описание работы модального окна с фокусом. Текст подробный, но вот важные моменты:

    • При открытии модального окна фокус должен перемещаться внутрь окна, и оставаться там всегда. Мы можем передвигаться вне модального окна, но фокус всегда остается внутри.
    • При закрытии модального окна фокус должен отправляться к тому элементу, что его вызвал.

    Это поведение называется focus-trap и настраивается при помощи JS самостоятельно или с готовыми библиотеками:


    Проверяем отредактированное окно:

    • Дописываем код и проверяем как кнопка и модальное окно работают.
    • Перескакиваем фокусом на кнопку и кликаем пробел — модальное окно открывается.
    • Продолжаем кликать Tab’ом — фокус остается внутри.
    • Кликаем на кнопку ОК: фокус уходит на иконку и все работает.

    Мы научили наше модальное окно правильно работать с фокусом.

    Шаг 2. Клавиатура


    Если мы верстаем доступное модальное окно, то должны иметь возможность перемещаться внутри него используя Tab или Shift+Tab. Если использовать дефолтные интерактивные элементы, то ничего дописывать не нужно, кроме закрытия модального окна по escape, что мы сейчас и сделаем.

    • Переходим фокусом на иконку.
    • Кликаем пробел — все работает.
    • Внутри модального окна имитируем работу.
    • Кликаем Escape и модальное окно закрывается.
    • Все работает.

    С клавиатурой разобрались.

    Шаг 3. Скринридер


    Проверяем наше модальное окно на скринридер:



    • Выключаем свет, включаем скринридер и слушаем, что он скажет про наше модальное окно.
    • Tab’ом перемещаемся к нашей кнопке и видим знакомую проблему: вместо текста иконка, нет описания и непонятно, что кнопка делает. Как решать эту проблему мы уже знаем,.
    • Кликаем и смотрим, что произойдет. Скринридер считает весь контент с модального окна, но для пользователя будет непонятно: текст принадлежит модальному окну, это текст после кнопки или пользователь уже ушел на другую страницу?

    Как можно улучшить наше модальное окно? С помощью документации по работе со скринридером, в которой есть детальная инструкция:



    Что нам нужно сделать:

    • Добавить модальному окну role="dialog" и атрибут aria-modal="true".
    • Кнопке, открывающей модальное окно, добавить знакомый нам aria-label и атрибут aria-expanded.


    Дописываем правки, включаем скринридер и тестируем модальное окно:

    • Нажимаем на кнопку. Скринридер произносит, что модальное окно открыто.
    • Если будем нажимать Tab внутри модального окна, то услышим, что мы переместились на кнопку, и контент, который эта кнопка открывала, сейчас закрыт.

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

    Обычное модальное окно мы превратили в доступное, просто выполнив инструкции из документации.

    Меню


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



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

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

    С точки зрения дизайна это не совсем правильно. Верстка дефолтного элемента select займет в разы меньше времени, чем верстка кастомной. Если дизайнер готов на компромисс, то нам стоит выбрать дефолтную, потому что не придется писать никакого JS, а только стилизовать через CSS. Для среднего разработчика это 15-30 минут работы вместе с тестированием.

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

    Особой сложности работа не представляет:

    • выбираем элемент;
    • кликаем;
    • выпадает меню;
    • выбираем что-то и видим, что стрелочка вверх/вниз меняется.

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

    Шаг 1. Фокус


    Начнем, как обычно с фокуса. Включаем размытость экрана и пробуем:

    • Фокусируемся на элементе.
    • Кликаем Tab и фокус уходит на нативный элемент Select — это нормально. Элемент просто держит фокус по умолчанию. Дизайн элемента можно стилизовать, если он кажется невзрачным, но сейчас это не важно.
    • Кликаем Tab’ом, и видим, что фокус уходит непонятно куда. Наш элемент не умеет держать фокус с самого начала. Давайте разбираться почему.

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

    В блоке с классом select есть:

    • Select-btn — элемент, на который мы будем кликать, чтобы открывать меню.
    • Сам список, который будет либо открыт, либо закрыт.

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

    Почему бы элемент, на который мы жмем, не сверстать обычной кнопкой?

    Из предыдущих примеров мы знаем, что поставив кнопку в этом месте, мы получим вместе и фокус, и клавиатуру без дополнительных действий.

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

    Шаг 2. Клавиатура


    Давайте пойдем дальше и научим наш элемент работать с клавиатурой.



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

    • Добавляем обработчиков;
    • переключаемся на кнопку и нажимаем пробел — все работает.
    • Нажимаем стрелки, выбираем любой другой город — тоже все работает.

    Задача поддержки клавиатуры решена: на стрелки повесили JS-обработчики.

    Шаг 3. Скринридер


    Включаем скринридер и смотрим, как наше меню будет читаться с его помощью:


    • Перемещаемся Tab’ом на элемент меню и попробуем его открыть пробелом.
    • Нажимаем пробел. Меню должно открыться.
    • Выбираем последний элемент из списка и рандомно кликаем стрелкой вниз и пробел. При нажатии на пробел меню должно закрыться и значение смениться. Если это так, то мы выбрали последний элемент из списка.

    Чтобы меню работало со скринридером, необходимо добавить некоторые атрибуты к нашему модальному окну:

    • role="listbox" — он делает то же самое, что и select — дает возможность выбрать что-то из списка;
    • role="option" — описание для каждого элемента меню;
    • aria-live="polite" — уже знакомый нам атрибут. Скроем от всех пользователей, но оставим доступным для скринридера.

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

    Проверка работы:

    • Проверяем мышкой меню, выбирая элементы.
    • Проверяем клавиатурой.
    • Включаем скринридер и повторяем проверку. Скринридер говорит, что сейчас откроется контент, но пока он скрыт.
    • Нажимаем пробел, выбираем город, например, Лондон. Если получается, то все работает.

    Сборка виджета погоды


    Мы сверстали все интерактивные элементы, которые есть в нашем виджете погоды и соберем все воедино. Так виджет выглядит в макете:



    Верстается на флекс-боксах или абсолютным позиционированием для некоторых элементов.

    Мы уже поработали почти над всеми элементами в виджете, и большинство кода уже написано, осталось собрать


    С точки зрения JS нам нужно сделать две вещи:

    1. Научить виджет забирать погоду с сервера;
    2. По успешному ответу сервера обновлять DOM-элементы.

    Работа с сервером — отдельная тема, но это не проблема.

    Чтобы виджет был полностью доступен, мы должны учесть несколько моментов.

    Иконки


    Нам нужно научить скринридер читать наши иконки, потому что информация в виджете состоит из 3 частей:

    • Локация, которую мы выбрали — в данном случае город Берлин;
    • температура;
    • описание погоды, которое есть на экране в виде иконок. Мы видим солнце, и понимаем, что дождя сегодня нет, но пользователь этого не знает, так как скринридер не читает подобную информации.

    Аббревиатуры


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

    Загрузка города


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

    Заголовки


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

    Общий список улучшений


    • H3 — заголовок для навигации;
    • Aria-label — необходимые описания к элементам;
    • Aria-live — атрибут, который мы уже использовали в элементе меню для того, чтобы научить скринридер произносить статус запроса: идет загрузка, либо она получилась, либо не получилась.

    Проверим, как текущая версия нашего виджета работает со скринридером:

    • Выключаем свет, включаем скринридер, и пробуем найти на странице наш виджет погоды при помощи передвижения по заголовкам.
    • Мы перемещаемся на заголовок и пробуем прочитать текст с виджета. Все работает нормально.
    • Теперь попробуем сменить город и узнать погоду.Открываем модальное окно, выбираем нужный город, и смотрим, что произойдет. Загрузка идет довольно быстро.
    • Тут есть еще промежуточный статус. Когда загрузка происходит, скринридер говорит: «Сейчас погода загружается». Сейчас загружен Лондон, мы находимся на кнопке и пробуем прочитать все, что там есть. Это действительно Лондон, и все работает.

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

    Выводы


    3 шага к доступности. Чтобы перевести виджет из обычного в доступный, к стандартному процессу верстки по макету мы добавили 3 шага:

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

    Дизайн, контент и сложные слова. Тема доступности не ограничивается работой с фокусом, клавиатурой и скринридером — она гораздо шире. Тема доступности — это:

    • Дизайн. Контрастные цвета, анимация на сайте, а еще нужно помнить про дальтоников.
    • Контент. Длинный текст вероятнее всего прочитают меньше пользователей, чем короткий. Обращайте внимание на простыни текста, скорее всего они недоступны.
    • Сложные слова в тексте. Если школьник услышит неизвестное научное слово, то не поймет его. Термины, аббревиатуры, жаргонизмы и профессиональный сленг создают недоступный контент.

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

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



    В русскоязычном сегменте есть аналогичный сайт weblind.ru.



    Сайт в открытом доступе и если вам есть, что добавить по теме доступности, то можете зайти на GitHub и добавить эту информацию на сайт.

    Контакты Сергея Кригера и ссылка на интерактивную презентацию к докладу.
    Важное!

    На нашем youtube-канале мы открыли видео всех докладов Frontend Conf в составе фестиваля РИТ++ — вот плейлист с лучшими из них.

    Следующая наша конференция для фронтенд-разработчиков пройдет в мае в составе фестиваля РИТ++. В его ожидании подпишитесь на рассылку и мы будем отправлять вам новые материалы, лучшие доклады за 2018 год, а также анонсы выступлений и другие новости будущей конференции.

    Подписывайтесь и оставайтесь с нами, будет интересно :)
    • +30
    • 4,2k
    • 3

    Конференции Олега Бунина (Онтико)

    450,00

    Конференции Олега Бунина

    Поделиться публикацией
    Комментарии 3
      0
      • role=«listbox» — он делает то же самое, что и select — дает возможность выбрать что-то из списка;
      • role=«option» — описание для каждого элемента меню;



      Для меню хорошо бы использовать не role=«listbox», а role=«menu», а для пунктов меню не option, а role=«menuitem».
        –2
        вот бы приняли за правило не постить страшненькие лица. Нет понимания почему все в монитор залезть хотят? Негоже ведь. Отторжение случается.
          0
          Ещё слышал, что желательно ставить атрибут aria-busy=«true» контейнеру сайта под модальным окном (если модальное окно это отдельный слой над основным контейнером сайта):

          <body aria-busy="true">
            <div class="popup" aria-modal="true"></div>
           </body>
          

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

          Самое читаемое