ДокументацияHTMLДоступность (a11y)
Средний 10 мин чтения

Доступность (a11y)

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

accessibilitya11yARIAscreen reader

Почему доступность важна?

  • ~15% людей имеют то или иное ограничение
  • Улучшает UX для всех (клавиатурная навигация, контраст)
  • Требуется по закону во многих странах
  • Хорошо влияет на SEO

Семантический HTML — основа

Правильная семантика уже решает 80% задач доступности:

<!-- Плохо -->
<div onclick="navigate()">Главная</div>
<div class="btn">Сохранить</div>
<div class="heading">Заголовок</div>

<!-- Хорошо -->
<a href="/">Главная</a>
<button>Сохранить</button>
<h2>Заголовок</h2>

Скринридеры объявляют роли элементов. <button> автоматически объявляется как «кнопка», <a> — как «ссылка».

Альтернативный текст для изображений

<!-- Информативное изображение -->
<img src="chart.png" alt="График роста продаж: +42% в 2024 году">

<!-- Декоративное изображение (скринридер пропустит) -->
<img src="divider.png" alt="">

<!-- Иконка с функцией -->
<button>
  <img src="search.png" alt="Поиск">
</button>

<!-- SVG-иконка -->
<svg aria-hidden="true" focusable="false">
  <use href="#icon-close"/>
</svg>
<span class="sr-only">Закрыть</span>

ARIA (Accessible Rich Internet Applications)

ARIA добавляет семантику там, где HTML её не предоставляет:

aria-label — текстовая метка

<!-- Когда видимый текст отсутствует или недостаточен -->
<button aria-label="Закрыть диалог">×</button>

<nav aria-label="Главная навигация">
  <ul>...</ul>
</nav>

<nav aria-label="Хлебные крошки">
  <ol>...</ol>
</nav>

aria-describedby — дополнительное описание

<input type="password" aria-describedby="pwd-rules">
<p id="pwd-rules">Минимум 8 символов, включая цифру</p>

aria-labelledby — связь с заголовком

<section aria-labelledby="features-title">
  <h2 id="features-title">Возможности</h2>
  <ul>...</ul>
</section>

Роли (role)

<div role="alert">Файл успешно загружен!</div>
<div role="status">Загрузка: 75%</div>
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
  <h2 id="dialog-title">Подтверждение</h2>
</div>

Состояния

<button aria-expanded="false" aria-controls="menu">Меню</button>
<ul id="menu" hidden>...</ul>

<input type="checkbox" aria-checked="mixed"><!-- Indeterminate state -->

<li role="option" aria-selected="true">Выбранный пункт</li>

<input aria-invalid="true" aria-describedby="error">
<span id="error" role="alert">Обязательное поле</span>

Клавиатурная навигация

Все интерактивные элементы должны быть доступны с клавиатуры:

<!-- focusable по умолчанию: a, button, input, select, textarea -->

<!-- Добавить в Tab-порядок -->
<div tabindex="0" role="button" onkeydown="handleKey(event)">
  Кастомный элемент
</div>

<!-- Убрать из Tab-порядка (но оставить фокусируемым через JS) -->
<div tabindex="-1">Элемент</div>

<!-- Никогда не убирайте outline у фокусируемых элементов! -->
/* Кастомный стиль фокуса */
:focus-visible {
  outline: 2px solid #7c3aed;
  outline-offset: 2px;
  border-radius: 4px;
}

Позволяет пропустить навигацию и перейти к основному контенту:

<a class="skip-link" href="#main-content">Перейти к содержимому</a>

<header>...</header>
<main id="main-content">...</main>
.skip-link {
  position: absolute;
  top: -100%;
  left: 0;
  background: #7c3aed;
  color: white;
  padding: 8px 16px;
  z-index: 9999;
}

.skip-link:focus {
  top: 0;
}

Скрытый для глаз, но видимый скринридеру

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

Контраст

WCAG 2.1 требует:

  • Уровень AA: соотношение 4.5:1 для обычного текста, 3:1 для крупного
  • Уровень AAA: 7:1 для обычного текста

Инструменты проверки: Chrome DevTools, WebAIM Contrast Checker.