Доступність

Навіщо нам доступність?

Веб-доступність (також відома як 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>, мати підпис, який забезпечує доступність контенту. Підписи потрібно виконувати так, щоб їх могли використовувати екранні зчитувальні пристрої.

Наступні ресурси показують нам як це робити:

Ці стандартні практики 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-компоненти.

Кожен з таких компонентів наслідує спеціальний шаблон дизайну, та має функціонувати певним чином незалежно від користувача та агента користувача (браузера):

На що ще потрібно звернути увагу

Встановлення мови сторінки

Обов’язково вказуйте мову текста на сторінці. Це необхідно для правильних головних налаштувань екранних зчитувальних пристроїв:

Встановлення заголовка документа

Встановіть <title> документа, щоб коректно визначити зміст сторінки, оскільки це дає змогу користувачеві орієнтуватися в контексті поточної сторінки:

у React ми можемо зробити це, використовуючи компонент «React Document Title».

Контрастність кольорів

Переконайтесь, що весь текст для читання на вашому веб-сайті має достатній кольоровий контраст, щоб він залишався максимально доступним для зчитування користувачами зі слабким зором:

Ручний розрахунок правильних поєднань кольорів для усіх випадків на вашому веб-сайті може бути досить важким. Замість цього ви можете визначити всю доступну палітру кольорів за допомогою Colorable.

Згадані нижче інструменти aXe та WAVE також включають тести на контрастність кольорів та повідомлять про помилки.

Якщо ви хочете розширити свої можливості тестування контрастності, ви можете скористатися цими інструментами:

Інструменти розробки та тестування

Існує багато інструментів, якими ми можемо скористатися для створення доступних веб-додатків.

Клавіатура

На сьогоднішній день найпростіша, а також одна з найважливіших перевірок - це тестування клавіатурою. Така перевірка дозволяє визначити доступність контенту на вашому сайті при роботі тільки з клавіатурою. Це можна зробити так:

  1. Від’єднайте вашу мишу.
  2. Використовуйте Tab та Shift+Tab для переміщення сторінкою.
  3. Використовуйте Enter для активації елементів.
  4. Якщо потрібно, використовуйте клавіші зі стрілками клавіатури для взаємодії з деякими елементами, такими як меню та списки, що випадають.

Підтримка у розробці

Ми можемо перевірити певні особливості притаманні доступності безпосередньо у 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.

Ви також можете використовувати модуль react-axe, щоб бачити повідомлення про проблеми доступності у консолі безпосередньо під час розробки та перевірки помилок.

WebAIM WAVE

Web Accessibility Evaluation Tool ще один інструмент для перевірки доступності в браузері.

Інспектори доступності да дерево доступності

Дерево доступності це підмножина дерева DOM яка містить доступні об’єкти для кожного елемента DOM, що мають буду представлені допоміжним технологіям, таким як пристрої для зчитування екрану.

У деяких браузерах ми можемо легко переглядати інформацію про доступність кожного елемента в дереві доступності:

Пристрой дл зчитування екрану

Тестування за допомогою пристроїв зчитування екрану має буду невід’ємною частиною вашого тестування доступності.

Зверніть увагу, що комбінації веб-переглядача та пристрою зчитування мають значення. Рекомендується протестувати свою програму у веб-переглядачі, який найкраще підходить для певного пристрою зчитування екрану.

Найпопулярніші пристрої зчитування екрану

NVDA в Firefox

NonVisual Desktop Access або NVDA це зчитувач екрану з відкритим кодом, який широко використовується в Windows.

Зверніть увагу на настуні рекомендації по використанню NVDA:

VoiceOver в Safari

VoiceOver це інтегрований зчитувач екрану для пристроїв Apple.

Зверніться до наступних посібників з активації та використання VoiceOver:

JAWS в Internet Explorer

Job Access With Speech або JAWS є широко використовуваним зчитувачем екранів у Windows.

Зверніться до наступних посібників, щоб якнайкраще скористатися JAWS:

Інші пристрої зчитування екрану

ChromeVox в Google Chrome

ChromeVox є інтегрованим зчитувачем екрана на Chromebook і доступний як плагін для Google Chrome.

Зверніться до наступних посібників, щоб якнайкраще скористатися ChromeVox: