Модуль 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 — та, яку не довелось писати. <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 повертається: колір — підсилення, а не носій змісту. Наступний урок про контраст покаже другу половину цієї історії.
Критерій у таблиці — «ARIA attributes, color contrast compliance, full keyboard navigation». Тобто не потрібно вивчати всю специфікацію: достатньо, щоб кожен інтерактивний елемент мав імʼя, стан передавався атрибутами, а таблиця й модалка проходились із клавіатури. Наступні два уроки саме про це.