Angular IDPStrong Junior
🔥 0

Модуль 7 · урок 5 з 10

Основи доступності

Доступність звучить як окрема велика тема, але на практиці зводиться до трьох питань про кожен елемент: що це, як він називається і в якому він стані. Якщо відповіді дає сама розмітка — ARIA взагалі не потрібна.

Роль, імʼя, стан

<!-- ❌ роль невідома, імені немає, стан ніяк не передається -->
<div class="btn" (click)="save()">💾</div>

<!-- ✅ роль з тега, імʼя з aria-label, стан з disabled -->
<button type="button" (click)="save()" [disabled]="saving()" aria-label="Зберегти">
  <span aria-hidden="true">💾</span>
</button>
Перше правило ARIA

Найкраща ARIA — та, яку не довелось писати. <button> уже має роль кнопки, працює з клавіатури, реагує на Enter і Space, підтримує disabled. <div role="button" tabindex="0"> вимагає відтворити все це руками — і хтось обовʼязково забуде обробник Space.

Що дає семантика з модуля 1

ТегРоль і поведінка задарма
<button>Фокус, Enter, Space, disabled
<a href>Фокус, Enter, контекстне меню, відкриття в новій вкладці
<input> + <label>Імʼя поля, клік по підпису, зв'язок для скрінрідера
<nav>, <main>, <aside>Орієнтири: можна стрибати по розділах
<table> з <th>Читання «колонка Статус, значення Готово»
<h1>…<h6>Зміст сторінки, по якому можна навігувати

Тобто якщо модуль 1 зроблено чесно, половина доступності вже є. Це не збіг: обидві вимоги йдуть від однієї ідеї — розмітка має описувати зміст, а не вигляд.

Коли ARIA таки потрібна

<!-- імʼя для іконки без тексту -->
<button aria-label="Видалити задачу"><svg …></svg></button>

<!-- імʼя з наявного тексту на сторінці -->
<section aria-labelledby="filters-title">
  <h2 id="filters-title">Фільтри</h2>
</section>

<!-- стан, який не виражається тегом -->
<button [attr.aria-expanded]="open()" aria-controls="panel">Деталі</button>
<div id="panel" [hidden]="!open()">…</div>

<!-- стан сортування колонки -->
<th [attr.aria-sort]="sortDir() ?? 'none'">Термін</th>

<!-- звʼязок поля з помилкою (модуль 6) -->
<input [attr.aria-describedby]="hasError() ? 'code-err' : null"
       [attr.aria-invalid]="hasError() ? 'true' : null">

Приховати правильно

<span aria-hidden="true">🔍</span>        <!-- декор: не читати -->

<span class="visually-hidden">Результатів: 42</span>   <!-- читати, але не показувати -->

<div hidden>…</div>                       <!-- немає ні для кого -->
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

Три різні наміри — три різні інструменти. Найчастіша помилка тут — display: none для тексту, який мав озвучитись: він зникає й для скрінрідера теж.

Іконки й кольори

<!-- ❌ статус лише кольором -->
<span class="badge badge--done"></span>

<!-- ✅ колір, іконка й текст -->
<span class="badge badge--done">
  <span aria-hidden="true">✓</span> Готово
</span>

Це правило з модуля 1 повертається: колір — підсилення, а не носій змісту. Наступний урок про контраст покаже другу половину цієї історії.

Що з цього потрібно для IDP

Критерій у таблиці — «ARIA attributes, color contrast compliance, full keyboard navigation». Тобто не потрібно вивчати всю специфікацію: достатньо, щоб кожен інтерактивний елемент мав імʼя, стан передавався атрибутами, а таблиця й модалка проходились із клавіатури. Наступні два уроки саме про це.