Доступность для mui компонент
Введение
На заре становления веба сайты были огромным полем для творчества и экспериментов. Каждый хотел выделиться на фоне остальных, придумать уникальный дизайн, разработать не похожие на другие поля формы, меню, попапы, чтобы сайт запомнился пользователям. Неожиданные музыкальные вставки, анимации, видео, всё, чтобы впечатлить. Разработчики изобретали свои элементы управления, как удачные, так и нет. Неудивтельно, что чтобы как-то систематизировать и упорядочить всё это многообразие, чтобы облегчить жизнь рядовому пользователю, появилась W3C — организация, которая разрабатывает стандарты для web'а, включая стандарты доступности.
Доступность (accessibility) — это не только для людей с ограниченными возможностями (слабовидящих, слабослышащих и проч.) как мы привикли воспринимать, а потому и забивать на неё.
Для меня доступность — это правила вежливости при разработке сайтов и приложений, когда сайт помогает пользователю решить его проблему (залогиниться, заполнить форму, найти какую-то информацию и проч.), а не заставляет его бороться с навигацией, или выдает некорректные ошибки мелким бледным шрифтом.
Доступность — это про уважение к пользователю. Хочешь с клавиатуры работать — пожалуйста. Хочешь понять почему форма не отправляет данные — вот большая видная причина, понятным цветом, вот нотификация, выплывающая и показанная достаточное количество времени, чтобы можно было прочитать. Непривычно читать на белом фоне — вот тебе переключатель на тёмную тему. Поэтому доступность — это набор, состоящий из маленьких деталей, который делает интерфейс понятным и однозначным.
Именно поэтому это про симбиоз дизайнера и фронтэндера. Чтобы сделать сайт доступным, недостаточно одного дизайна (выбора цветовой палитры и расположения элементов), важно и имплементировать это с учётом возможного поведения пользователя, прописать все area-атрибуты, продумать перемещение фокуса и много-много прочей мелкой работы.
Поэтому мне импонирует идея использовать готовые компоненты, такие как mui, которые уже разработаны с учётом доступности и автоматически содержат нужные атрибуты. Хотя это не отменяет того, что их нужно уметь правильно готовить. Поэтому я хотела бы рассказать о тех нюансах, на которые стоит обратить внимание при использовании mui. Это далеко не полный список. Есть в нём и недостаки mui, о чём я тоже расскажу далее.
1. Использование цветовой схемы для нативных элементов
Если на сайте использвается переключатель светлая/тёмная тема, не забудьте добавить enableColorScheme в теге CssBaseline, чтобы такие элементы, как scroll view выглядел тоже тёмным в тёмной теме.
<CssBaseline enableColorScheme />
2. Подсветка элементов в фокусе (при работе с клавиатуры)
При использовании мыши мы используем всевдоэлемент :focus для отображения фокуса, но при работе с клавиатуры он не отрабатывает, а срабатывает :focus-visible, о чём разработчики или забывают, или просто не знают.
Конечно, mui за нас уже позаботились о красивой подсветке, но если вы используете кастомную тему со своими цветами, то не забудьте переопределить в ней цвета и для фокуса.
Глобально для всех:
<GlobalStyles
styles={{
'.focus-visible:focus-visible': {
outline: `2px solid ${theme.palette.primary.main}`
}
}}
/>
И/или для каждого элемента, где это необходимо. Например:
createTheme({
components: {
MuiMenuItem: {
styleOverrides: {
root: {
':focus-visible': {
background: theme.palette.action.hover
}
}
}
}
}
})
2.1 Пробемы material design
Есть определенная неконсистентность в ссылках и элементах material design, который используется в mui. Дело в том, что, например, у кнопок в выделенном состоянии с клавиатуры нет синей обводки, как у ссылок, у них расползается на фоне круглое пятно, обозначающее фокус. И это не проблема mui, это проблема material design, который mui реализуют. Об этом есть даже issue в репозитории, но пока это никак не решено. Надеюсь, в будущих версиях эту проблему учтут и всё-таки найдут решение.
3. Семантика
По ней написано много статей, но главная идея в том, что каждый тег предназначен для определенных целей. Поэтому лучше не использовать везде div, а писать более подходящее под страницу. Большой ошибкой будет ставить ссылку там, где нужна кнопка, и наоборот. Ссылки (link) будут использовать для перехода на сторонние ресурсы или же переход между страницами, кнопки — для сабмита формы, открытия попапов и проч.
3.1 «Кнопки в кнопке»
Иногда дизайнеры рисуют кликабельный блок, в котором находится кнопка. Конечно, помещать кнопку в кнопку было бы неправильно. Если элемент выглядит как карточка, то идеально подойдёт mui-компонента Card, так как у нее есть компонента CardActionArea, которая обозначает главную область для клика, но даже она не перекрывается с другой кнопкой, поэтому не совсем наш случай. Что делать, если нужно всё-таки делать клик и по области, и по кнопке внутри области?
Один из вариантов мог бы быть с помощью CardActionArea, но с использованием component="div", т.е. с рендерингом div вместо button. Но мне не нравятся 2 момента:
- использование CardActionArea вне контекста Card как-то само себе не семантично,
- при фокусе под курсором мышки распозается тень как на кнопке, т. е. дизайн этого блока соответствует кнопке, что не всегда нужно, а не выделается бордюркой по периметру (см. п.2.1).
Использование <button component="div"> тоже попадает под только что описанный второй пункт.
Если описать елемент как div, то чтобы он был кликабельным по всем свойствам аксессибилити, нужно очень много дополнительно описать. Поэтому я использую для родительского элемента ButtonBase, который является базовым классом для кнопки со всеми нужными аттрибутами, но без стилей.
<ButtonBase component='div' onClick={...} className='focus-visible'>
Конечно, его нужно ещё застилизовать, но в него уже без проблем можно помещать кнопки.
3.2 Один клик — одно событие
Это очень простое правило «один клик — одно событие», которое дизайнер может нарушить наведением, он же «:hover». Бывает такое, что при наведении на элемент показывается огромный тултип с текстом и пояснениями, а при клике на этот элемент — основное событие (например, переход на страницу подробностей). Это приводит к проблемам работы как с клавиатуры, так и на тач-устройствах, где нет такого понятия как hover. Есть клик, а, управляя с клавиатуры, есть ещё focus. Как быть в таком случае?
Если говорить о клавиатурном вводе, то не стоит городить javascript'ом, что при первом клике мы показываем тултип, а при втором — отрабатываем основное событие перехода. Получается, что для того, чтобы событие сработало, надо нажать два раза. Тогда как мышкой надо нажать всего один раз. Поэтому мне и нравится правило «один клик — одно событие», просто и понятно, как и должно работать. Что же делать тогда с ховером? Можно навесить показ тултипа на выделение, используя наш любимый :focus-visible. Как показывать на тач-устройках? Никак. И дизайнер должен это понимать, чтобы проектировать дизайн с учетом того, чтобы не прятать важную информацию в ховер, а показывать её более явно.
4. Загрузка контента и идентификаторы ожидания
Важно своевременно давать пользователю обратную связь о том, что действие произошло или о том, что контент загружается и какого рода будет контент.
Так, правилом хорошего тона будет использование Skeleton, который можно настроить так, чтобы он эмулировал загружаемую таблицу или данные в том виде, в котором он после отобразится.
Другой вариант — использование LoadingButton вместо обычной Button кнопки. Она решает сразу несколько проблем:
- сообщает пользователю, что что-то произошло (данные отправились, форма засабмитилась) в момент, когда loading={true} и визуально появился спиннер на кнопке, и
- предотращается двойной клик по кнопке.
5. Навигация с клавиатуры
У пользователя есть две основные комбинации на клавиатуре для навигации:
- «tab» – для перехода на следующий элемент,
- «shift+tab» — для перехода на предыдущий элемент.
Стрелки, пробел и enter —это уже для взаимодействия внутри элементов. Но переход между ними происходит с использованием tab. Поэтому нет никакой возможности открыть контекстное меню, которые открывается по нажатию правой кнопки мыши, с клавиатуры. Что делать в этом случае? Важно, чтобы сайт был полностью доступен с клавиатуры, поэтому хорошим решением будет дублирование функционала из контекстного меню недалеко от этого элемента (или в самом элементе). Один из вариантов — спрятать в меню, которое открывается по кнопке «...». Это очень популярный паттерн, который можно смело использовать.
5.1 Кастомные дизайнерские компоненты
Иногда дизайнеры придумывают кастомные компоненты, которые сложно реализовать стандартными средствами, но которые решают определенную задачу и уместны в конкретной ситуации. Тут, пожалуй, следует обсудить с дизайнером о целесообразности использования таких элементов и возможные варианты работы с ними с клавиатуры. Написание таких кастомных элементов как правило трудоёмко и может занять много времени и на обсуждение поведения, и на саму реализацию. Как одно из простых решений, когда сложно продумать как работать с таким компонентом с клавиатуры, будет дублирование функционала стандартными средствами, позволяя пользователю использовать или мышь, или клавиатуру в зависимости от предпочтений.
5.2 Enter для сабмита формы
Нет ничего проще, чем проверить работу вашего приложения с клавиатуры, просто пройдись по приложению, не используя мышку. Насколько лично вам удобно пользоваться? Что бы вы поменяли? Правильна ли последовательность перехода по элементам?
Может оказаться, что форму удобнее сабмитить по нажатию на Enter в поле ввода, а не через tab (фокус на кнопке сабмита) и затем Enter. Так мы сокращаем путь на одно нажатие:
<TextField onKeyDown={(event: KeyboardEvent<HTMLDivElement>) => {
if (event.key === 'Enter') { ... }
}}>
5.3 Focus trap (ловушка для фокусировки)
Mui прекрасно само справляется с доступностью своих элементов. Если посмотреть на диалоговые окна и модалки, как фокус «зацикливается» в открытом окне, не переходя за его пределы, восхищаешься, что они даже это продумали! Но ещё больше меня поразило то, что они даже разработчикам предоставляеют механизм для замыкания фокуса в области с помощью focus-trap.
6. Плагины для Chrome
Когда разрабатываешь приложение, сложно уследить за всеми и здорово, когда эту работу можно автоматизировать с помощью плагинов. Для хрома есть два незаменимых:
- Lighthouse — плагин, позволяющий оценить производительность и корректность, в том числе и доступность.
- Accessibility Insights for Web — плагин, непосредственно разработанный находить проблемы в аксессибилити и помогающий их устранить, предлагая возможные варианты.
7. Другие инструменты
Есть и другие хорошие инструменты, на которые стоит обратить внимание:
- axe DevTools® for Web — платный, но очень мощный инструмент для проверки на доступность.
- Auto-VO — бесплатная озвучка для тестирования web-приложения (туториал)
Комментарии