Доступность
Почему доступность?
Доступность веб-сайтов (также известная как a11y) — это проектирование и создание веб-сайтов, которые могут использовать все. Поддержка доступности необходима, чтобы позволить assistive technology интерпретировать веб-страницы.
React полностью поддерживает создание доступных веб-сайтов, часто используя стандартные HTML-техники.
Стандарты и рекомендации
WCAG
Web Content Accessibility Guidelines предоставляют рекомендации по созданию доступных веб-сайтов.
Следующие контрольные списки WCAG предоставляют обзор:
WAI-ARIA
Web Accessibility Initiative - Accessible Rich Internet Applications содержит техники для создания полностью доступных JavaScript виджетов.
Обратите внимание, что все aria-* атрибуты HTML полностью поддерживаются в JSX. В то время как большинство свойств и атрибутов DOM в React используют верблюжью нотацию, эти атрибуты должны быть с дефисами (также известные как кебаб-кейс, лисп-кейс и т.д.), как и в обычном HTML:
<input
type="text"
aria-label={labelText} aria-required="true" onChange={onchangeHandler}
value={inputValue}
name="name"
/> Семантический HTML
Семантический HTML — основа доступности веб-приложений. Использование различных HTML-элементов для усиления смысла информации на наших веб-сайтах часто обеспечивает доступность без дополнительных усилий.
Иногда мы нарушаем семантику HTML, когда добавляем <div> элементы в наш JSX, чтобы наш код React работал, особенно при работе со списками (<ol>, <ul> и <dl>) и HTML <table>. В этих случаях следует использовать React Fragments для группировки нескольких элементов.
Например,
import React, { Fragment } from 'react';
function ListItem({ item }) {
return (
<Fragment>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</Fragment>
);
}
function Glossary(props) {
return (
<dl>
{props.items.map(item => (
<ListItem item={item} key={item.id} />
))}
</dl>
);
} Вы можете отобразить коллекцию элементов в массив фрагментов, как и любой другой тип элемента:
function Glossary(props) {
return (
<dl>
{props.items.map(item => (
// Fragments should also have a `key` prop when mapping collections
<Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</Fragment>
))}
</dl>
);
} Когда вам не нужны какие-либо свойства для тега Fragment, вы можете использовать сокращённую запись, если ваша среда разработки её поддерживает:
function ListItem({ item }) {
return (
<>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</>
);
} Дополнительную информацию можно найти в документации по Fragments.
Доступные формы
Мечение
Каждый HTML-элемент управления формой, например, <input> и <textarea>, должен быть доступно помечен. Нам нужно предоставить описательные метки, которые также будут доступны для экранных читателей.
Следующие ресурсы показывают, как это сделать:
- W3C показывает, как метить элементы
- WebAIM показывает, как метить элементы
- Paciello Group объясняет доступные имена
Хотя эти стандартные HTML-практики могут быть непосредственно использованы в React, обратите внимание, что атрибут for записывается как htmlFor в JSX:
<label htmlFor="namedInput">Name:</label> <input id="namedInput" type="text" name="name"/>
Уведомление пользователя об ошибках
Ситуации с ошибками должны быть понятны всем пользователям. Следующая ссылка показывает, как экспонировать тексты об ошибках для экранных читателей:
Управление фокусом
Обеспечьте, чтобы ваше веб-приложение могло быть полностью управляемо только с помощью клавиатуры:
Фокус клавиатуры и контур фокуса
Фокус клавиатуры относится к текущему элементу в DOM, который выбран для приема ввода с клавиатуры. Мы видим его повсюду в виде контура фокуса, подобного тому, что показан на следующем изображении:
Используйте только CSS для удаления этого контура, например, установив outline: 0, если вы его заменяете другим способом реализации фокуса.
Способы перехода к нужному содержанию
Предоставьте механизм, позволяющий пользователям пропустить навигационные разделы в вашем приложении, так как это помогает и ускоряет навигацию с помощью клавиатуры.
Ссылки для пропуска или ссылки для пропуска навигации — это скрытые ссылки навигации, которые становятся видимыми только когда пользователи, использующие клавиатуру, взаимодействуют со страницей. Они очень легко реализуются с помощью внутренних якорей страницы и некоторой стилизации:
Также используйте элементы и роли для ориентиров, такие как <main> и <aside>, чтобы обозначить области страницы, так как assistive technology позволяют пользователю быстро перемещаться к этим разделам.
Дополнительную информацию об использовании этих элементов для повышения доступности вы найдете здесь:
Программное управление фокусом
Наши приложения React постоянно изменяют HTML DOM во время выполнения, что иногда приводит к потере фокуса клавиатуры или установке его на неожиданный элемент. Для исправления этого нам нужно программно направить фокус клавиатуры в нужном направлении. Например, сбросив фокус клавиатуры на кнопку, открывающую модальное окно, после закрытия этого окна.
MDN Web Docs рассматривает этот вопрос и описывает, как можно создать доступные для навигации по клавиатуре JavaScript виджеты.
Для установки фокуса в React можно использовать Refs к элементам DOM.
Используя это, мы сначала создаем ref к элементу в JSX класса компонента:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// Create a ref to store the textInput DOM element
this.textInput = React.createRef();
}
render() {
// Use the `ref` callback to store a reference to the text input DOM
// element in an instance field (for example, this.textInput).
return (
<input
type="text"
ref={this.textInput} />
);
}
} Затем мы можем сфокусировать его в другом месте нашего компонента по мере необходимости:
focus() {
// Explicitly focus the text input using the raw DOM API
// Note: we're accessing "current" to get the DOM node
this.textInput.current.focus();
} Иногда родительский компонент должен установить фокус на элемент в дочернем компоненте. Это можно сделать, экспонируя DOM refs родительским компонентам через специальное свойство в дочернем компоненте, которое передает ref родителя к DOM-узлу дочернего компонента.
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.inputElement = React.createRef();
}
render() {
return (
<CustomTextInput inputRef={this.inputElement} />
);
}
}
// Now you can set focus when required.
this.inputElement.current.focus(); При использовании HOC для расширения компонентов рекомендуется передать ref в обернутый компонент с помощью функции forwardRef React. Если сторонний HOC не реализует передачу ref, указанный выше шаблон все еще может быть использован как резервный вариант.
Отличным примером управления фокусом является react-aria-modal. Это относительно редкий пример полностью доступного модального окна. Оно не только устанавливает начальный фокус на кнопку отмены (чтобы пользователь, использующий клавиатуру, не активировал случайно действие подтверждения), но и удерживает фокус клавиатуры внутри модального окна, а также возвращает фокус к элементу, который первоначально запустил модальное окно.
Примечание:
Хотя это очень важная функция доступности, это техника, которую следует использовать с осторожностью. Используйте её для восстановления потока фокуса клавиатуры при его нарушении, а не для предвосхищения того, как пользователи хотят использовать приложения.
События мыши и указателя
Обеспечьте, чтобы все функции, доступные через событие мыши или указателя, также были доступны с помощью только клавиатуры. Зависимость только от устройства указателя приведет к множеству случаев, когда пользователи, использующие клавиатуру, не смогут использовать ваше приложение.
Для иллюстрации рассмотрим распространенный пример нарушенной доступности, вызванный событиями кликов. Это паттерн внешнего клика, когда пользователь может отключить открытое всплывающее окно, щелкнув вне элемента.
Обычно это реализуется путем присоединения события click к объекту window, который закрывает всплывающее окно:
class OuterClickExample extends React.Component {
constructor(props) {
super(props);
this.state = { isOpen: false };
this.toggleContainer = React.createRef();
this.onClickHandler = this.onClickHandler.bind(this);
this.onClickOutsideHandler = this.onClickOutsideHandler.bind(this);
}
componentDidMount() {
window.addEventListener('click', this.onClickOutsideHandler);
}
componentWillUnmount() {
window.removeEventListener('click', this.onClickOutsideHandler);
}
onClickHandler() {
this.setState(currentState => ({
isOpen: !currentState.isOpen
}));
}
onClickOutsideHandler(event) {
if (this.state.isOpen && !this.toggleContainer.current.contains(event.target)) {
this.setState({ isOpen: false });
}
}
render() {
return (
<div ref={this.toggleContainer}>
<button onClick={this.onClickHandler}>Select an option</button>
{this.state.isOpen && (
<ul>
<li>Option 1</li>
<li>Option 2</li>
<li>Option 3</li>
</ul>
)}
</div>
);
}
} Это может работать хорошо для пользователей с устройствами указателя, такими как мышь, но работа с этим только с помощью клавиатуры приводит к нарушению функциональности при переходе на следующий элемент, так как объект window никогда не получает события click. Это может привести к скрытой функциональности, которая блокирует пользователей от использования вашего приложения.
Такую же функциональность можно достичь, используя соответствующие обработчики событий, такие как onBlur и onFocus:
class BlurExample extends React.Component {
constructor(props) {
super(props);
this.state = { isOpen: false };
this.timeOutId = null;
this.onClickHandler = this.onClickHandler.bind(this);
this.onBlurHandler = this.onBlurHandler.bind(this);
this.onFocusHandler = this.onFocusHandler.bind(this);
}
onClickHandler() {
this.setState(currentState => ({
isOpen: !currentState.isOpen
}));
}
// We close the popover on the next tick by using setTimeout.
// This is necessary because we need to first check if
// another child of the element has received focus as
// the blur event fires prior to the new focus event.
onBlurHandler() {
this.timeOutId = setTimeout(() => {
this.setState({
isOpen: false
});
});
}
// If a child receives focus, do not close the popover.
onFocusHandler() {
clearTimeout(this.timeOutId);
}
render() {
// React assists us by bubbling the blur and
// focus events to the parent.
return (
<div onBlur={this.onBlurHandler} onFocus={this.onFocusHandler}>
<button onClick={this.onClickHandler}
aria-haspopup="true"
aria-expanded={this.state.isOpen}>
Select an option
</button>
{this.state.isOpen && (
<ul>
<li>Option 1</li>
<li>Option 2</li>
<li>Option 3</li>
</ul>
)}
</div>
);
}
} Этот код предоставляет функциональность как для пользователей с устройствами указателя, так и для пользователей клавиатуры. Также обратите внимание на добавленные свойства aria-* для поддержки пользователей экранных читателей. Для простоты не реализованы события клавиатуры, необходимые для активации взаимодействия arrow key с опциями всплывающего окна.
Это один из многих случаев, когда зависимость только от событий указателя и мыши нарушает функциональность для пользователей клавиатуры. Тестирование с помощью клавиатуры сразу же выявит проблемные области, которые затем можно исправить, используя обработчики событий, осознающие клавиатуру.
Более сложные виджеты
Более сложный пользовательский опыт не должен означать менее доступный опыт. Хотя доступность легче всего достигается путем программирования, максимально приближенного к HTML, даже самые сложные виджеты можно написать с учетом доступности.
Здесь нам понадобятся знания о ролях ARIA и состояниях и свойствах ARIA. Это наборы HTML-атрибутов, которые полностью поддерживаются в JSX и позволяют создавать полностью доступные, высокофункциональные React-компоненты.
Каждый тип виджета имеет определенный шаблон проектирования и должен работать определенным образом для пользователей и пользовательских агентов:
- Практические рекомендации по ARIA — шаблоны проектирования и виджеты
- Примеры ARIA — Heydon Pickering
- Инклюзивные компоненты
Другие моменты для рассмотрения
Указание языка
Указывайте язык текстов на странице, так как программное обеспечение для экранного чтения использует его для выбора правильных настроек голоса:
Указание заголовка документа
Установите заголовок документа <title> для правильного описания содержимого текущей страницы, так как это гарантирует, что пользователь остается в курсе текущего контекста страницы:
Мы можем установить это в React, используя компонент React Document Title.
Цветовой контраст
Убедитесь, что весь читаемый текст на вашем веб-сайте имеет достаточный цветовой контраст, чтобы оставаться максимально читаемым для пользователей с низким зрением:
- WCAG — Понимание требования к цветовому контрасту
- Все о цветовом контрасте и почему следует пересмотреть его
- A11yProject — Что такое цветовой контраст
Ручное вычисление правильных цветовых комбинаций для всех случаев на вашем веб-сайте может быть утомительным, поэтому вместо этого вы можете вычислить целую доступную цветовую палитру с помощью Colorable.
Оба инструмента aXe и WAVE, упомянутые ниже, также включают тесты цветового контраста и будут сообщать об ошибках контраста.
Если вы хотите расширить возможности проверки контраста, вы можете использовать эти инструменты:
Инструменты разработки и тестирования
Существует ряд инструментов, которые мы можем использовать для создания доступных веб-приложений.
Клавиатура
Наиболее простой и один из самых важных проверок является проверка, может ли весь ваш веб-сайт быть достигнут и использован только с помощью клавиатуры. Сделайте это, выполнив следующие действия:
- Отключите вашу мышь.
- Используйте
TabиShift+Tabдля навигации. - Используйте
Enterдля активации элементов. - В случае необходимости используйте клавиши со стрелками вашей клавиатуры для взаимодействия с некоторыми элементами, такими как меню и раскрывающиеся списки.
Помощь в разработке
Мы можем проверить некоторые функции доступности непосредственно в нашем коде JSX. Часто проверки интеллисенса уже предоставляются в IDE, поддерживающих JSX, для ролей ARIA, состояний и свойств. У нас также есть доступ к следующему инструменту:
eslint-plugin-jsx-a11y
Плагин eslint-plugin-jsx-a11y для ESLint предоставляет обратную связь об ошибках доступности в вашем JSX с помощью анализа AST. Многие IDE позволяют интегрировать эти результаты непосредственно в анализ кода и окна исходного кода.
Create React App имеет этот плагин с подмножеством активных правил. Если вы хотите включить еще больше правил доступности, вы можете создать файл .eslintrc в корне вашего проекта со следующим содержанием:
{
"extends": ["react-app", "plugin:jsx-a11y/recommended"],
"plugins": ["jsx-a11y"]
} Тестирование доступности в браузере
Существует ряд инструментов, которые могут выполнять проверки доступности веб-страниц в вашем браузере. Пожалуйста, используйте их в сочетании с другими проверками доступности, упомянутыми здесь, так как они могут проверять только техническую доступность вашего HTML.
aXe, aXe-core и react-axe
Deque Systems предлагает aXe-core для автоматизированных и комплексных проверок доступности ваших приложений. Этот модуль включает интеграции для Selenium.
Accessibility Engine или aXe — это расширение браузера для проверки доступности, созданное на основе aXe-core.
Вы также можете использовать модуль @axe-core/react для отчета об этих результатах доступности непосредственно в консоль во время разработки и отладки.
WebAIM WAVE
Еще одно расширение браузера для проверки доступности — Инструмент оценки доступности веб-сайта.
Инспекторы доступности и дерево доступности
Дерево доступности — это подмножество дерева DOM, содержащее доступные объекты для каждого элемента DOM, который должен быть представлен средствам вспомогательной технологии, например, средствам чтения с экрана.
В некоторых браузерах мы можем легко просмотреть информацию о доступности каждого элемента в дереве доступности:
- Использование Инспектора доступности в Firefox
- Использование Инспектора доступности в Chrome
- Использование Инспектора доступности в OS X Safari
Средства чтения с экрана
Тестирование с помощью средства чтения с экрана должно быть частью ваших тестов доступности.
Обратите внимание, что комбинации браузера/средства чтения с экрана имеют значение. Рекомендуется тестировать ваше приложение в браузере, наиболее подходящем для вашего средства чтения с экрана.
Распространённые средства чтения с экрана
NVDA в Firefox
NonVisual Desktop Access или NVDA — это открытый исходный код Windows средство чтения с экрана, которое широко используется.
Обратитесь к следующим руководствам, чтобы узнать, как лучше использовать NVDA:
VoiceOver в Safari
VoiceOver — это встроенное средство чтения с экрана на устройствах Apple.
Обратитесь к следующим руководствам, чтобы узнать, как активировать и использовать VoiceOver:
- WebAIM — Использование VoiceOver для оценки доступности веб-сайтов
- Deque — Клавишные сочетания VoiceOver для OS X
- Deque — Клавишные сочетания VoiceOver для iOS
JAWS в Internet Explorer
Job Access With Speech или JAWS — широко используемое средство чтения с экрана для Windows.
Обратитесь к следующим руководствам, чтобы узнать, как лучше использовать JAWS:
Другие средства чтения с экрана
ChromeVox в Google Chrome
ChromeVox — встроенное средство чтения с экрана для Chromebook и доступно в качестве расширения для Google Chrome.
Обратитесь к следующим руководствам, чтобы узнать, как лучше использовать ChromeVox:
- Помощь Google Chromebook — Использование встроенного средства чтения с экрана
- Справочник по клавишным сочетаниям ChromeVox
© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://17.reactjs.org/docs/accessibility.html