Доступність
Навіщо нам доступність?
Веб-доступність (також відома як a11y) ґрунтується на дизайні та розробці сайтів, які можуть використовуватися будь-ким. Підтримка доступності необхідна, щоб дозволити допоміжним технологіям інтерпретувати веб-сторінки.
React повністю підтримує створення доступних веб-сайтів, часто за допомогою стандартних методів HTML.
Стандарти та рекомендації
WCAG
Правила доступності веб-контенту (Web Content Accessibility Guidelines) надають рекомендації щодо створення доступних веб-сайтів.
Наступні контрольні списки WCAG надають огляд загальних правил:
WAI-ARIA
Документ Web Accessibility Initiative - Accessible Rich Internet Applications містить набір технік для розробки повністю доступних JavaScript-віджетів.
Зверніть увагу, що всі aria-*
HTML-атрибути повністю підтримуються у JSX. У той час як більшість DOM-властивостей і атрибутів у React записуються у верблюжому регістрі (camelСase, ще називають горба́тий регістр, верблюже письмо), ці атрибути мають бути записані у дефіс-регістрі (hyphen-case, також відомий як кебаб-регістр, LISP-регістр, і т.д.), оскільки вони знаходяться у простому 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>
) та <table>
(HTML-таблиця).
У такому випадку краще використовувати React-фрагменти, щоб згрупувати декілька елементів разом.
Наприклад,
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 => (
// Фрагменти також повинні мати пропс `key` при відображенні колекцій
<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>
</> );
}
Для більш детальної інформації, перегляньте документацію фрагментів.
Доступні форми
Підписи елементів форм
Кожен елемент HTML-форми, наприклад <input>
або <textarea>
, повинен мати підпис, який забезпечує доступність контенту. Підписи потрібно виконувати так, щоб їх могли використовувати екранні зчитувальні пристрої.
Наступні ресурси показують нам як це робити:
- W3C показує, як підписувати елементи
- WebAIM показує, як підписувати елементи
- Paciello Group пояснює доступні найменування
Ці стандартні практики HTML можна використовувати безпосередньо в React, але зауважте, що атрибут for
у JSX записується як htmlFor
:
<label htmlFor="namedInput">Ім'я:</label><input id="namedInput" type="text" name="name"/>
Повідомляємо користувача про помилки
Випадки з помилками мають бути зрозумілими для всіх користувачів. Наступні посилання показують як зробити текст помилок доступним для пристроїв зчитування з екрану:
Контроль фокусу
Переконайтеся, що ваш веб-додаток можна повноцінно використовувати, в тому числі, лише за допомогою клавіатури:
Фокус клавіатури та контур елемента
Фокус клавіатури посилається на поточний елемент у DOM, який вибрано для отримання вводу з клавіатури. Зазвичай такий елемент виділяється контуром, як це показано на малюнку:
Використовуйте CSS, який видаляє цей контур (наприклад, встановлюючи outline: 0
), тільки в тому випадку, якщо ви реалізуєте фокус-контур якимось іншим чином.
Механізм швидкого переходу до бажаного змісту
Забезпечте механізм, що дозволяє користувачам пропускати розділи під час навігації у додатку, оскільки це допомагає і пришвидшує навігацію за допомогою клавіатури.
Так звані «пропускні посилання» чи «пропускні навігаційні посилання» - це приховані навігаційні посилання, які стають видимими лише тоді, коли користувачі клавіатури взаємодіють зі сторінкою. Їх дуже просто реалізувати за допомогою внутрішніх якорів сторінки та певного стилю:
Також використовуйте структурні елементи та ролі, такі як <main>
та <aside>
, для розмежування регіонів сторінок, оскільки допоміжна технологія дозволяє користувачеві швидко переходити до цих розділів.
Детальніше про використання цих елементів для підвищення доступності читайте тут:
Керуємо фокусом програмним шляхом
Наші React-додатки постійно змінюють HTML DOM під час виконання, іноді це призводить до того, що фокус клавіатури втрачається або встановлюється на несподіваний елемент. Для того, щоб виправити це, нам потрібно програмно просунути фокус клавіатури в потрібному напрямку. Наприклад, після закриття модального вікна перевести фокус клавіатури на кнопку, яка його відкрила.
MDN Web Docs розглядає це і описує, як ми можемо побудувати віджети JavaScript, орієнтовані на клавіатуру.
Щоб встановити фокус у React, ми можемо використовувати рефи на елементи DOM.
Використовуючи цей спосіб, спочатку ми створюємо реф у класі компонента на елемент у JSX:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// Створюємо реф на текстове поле вводу як елемент DOM this.textInput = React.createRef(); }
render() {
// Використовуємо зворотній виклик `ref` щоб зберегти реф на текстове поле вводу // як елемент DOM в полі екземпляру (наприклад, this.textInput). return (
<input
type="text"
ref={this.textInput} />
);
}
}
Потім ми можемо примусово встановити фокус на елемент за потреби будь-де у нашому компоненті:
focus() {
// Безпосередньо фокусуємося на текстовому полі за допомогою DOM API
// Примітка: ми використовуємо властивість "current", щоб дістатися вузла DOM
this.textInput.current.focus();
}
Іноді батьківській компонент потребує встановити фокус на елементі у дочірньому компоненті. Ми можемо зробити це за допомогою передачі DOM-рефів батьківським компонентам через спеціальний проп дочірнього компонента який передає батьківському компоненту реф на вузол 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} /> );
}
}
// Тепер ви можете встановити фокус, коли потрібно.
this.inputElement.current.focus();
Якщо ви використовуєте КВП щоб розширити компоненти, рекомендується перенаправляти рефи до обгорнутого компоненту за допомогою React-функції forwardRef
. Якщо сторонній КВП не реалізує перенаправлення, підхід описаний вище все ще може бути використаний для зворотньої сумісності.
В якості чудового прикладу керування фокусом можна використовувати компонент 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
}));
}
// Ми закриваємо відкритий список за допомогою setTimeout. // Це необхідно, щоб перевірити, // що інший дочірній елемент отримав фокус, оскільки // подія 'blur' відбувається завжди перед подією 'focus'. onBlurHandler() { this.timeOutId = setTimeout(() => { this.setState({ isOpen: false }); }); }
// Якщо дочірній елемент отримав фокус, то список не закриваємо. onFocusHandler() { clearTimeout(this.timeOutId); }
render() {
// React допомагає нам, підіймаючи події `blur` та // `focus` до батьківського елемента. 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-*
властивості для підтримки користувачів пристроїв екранного зчитування. Задля простоти прикладу тут не було реалізовано перехід по списку за допомогою клавіш зі стрілками через події клавіатури.
Це один із прикладів багатьох випадків, коли залежно від подій лише вказівника та миші буде порушено функціональність для користувачів клавіатури. Постійне тестування за допомогою клавіатури негайно виділить проблемні області, які потім можна виправити, використовуючи обробники подій, які доступні для клавіатури.
Більш складні рішення
Ускладнення користувальницького інтерфейсу не повинно погіршувати доступність контенту. Незважаючи на те, що найкращий результат досягається при максимальному використанні синтаксису HTML, навіть дуже складний компонент можна зробити доступним для всіх.
Тут ми потребуємо знання ARIA-ролей, а також станів та властивостей ARIA . Наведені вище посилання є наборами інструкцій по HTML-атрибутам, які повністю підтримуються в JSX. Використовуючи їх, можна створювати високофункціональні і при цьому повністю доступні React-компоненти.
Кожен з таких компонентів наслідує спеціальний шаблон дизайну, та має функціонувати певним чином незалежно від користувача та агента користувача (браузера):
- Практичні рекомендації WAI-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-плагін eslint-plugin-jsx-a11y надає змогу аналізувати АСД (Абстрактне синтаксичне дерево) стосовно проблем доступності у вашому JSX. Багато 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.
The Accessibility Engine або aXe - це інспектор доступності в браузері на базі aXe-core
.
Ви також можете використовувати модуль @axe-core/react, щоб бачити повідомлення про проблеми доступності у консолі безпосередньо під час розробки та перевірки помилок.
WebAIM WAVE
Web Accessibility Evaluation Tool - ще один інструмент для перевірки доступності в браузері.
Інспектори доступності да дерево доступності
Дерево доступності - це підмножина дерева 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: