У доступности есть дешёвый рычаг, о котором часто забывают: правильный выбор HTML-элемента. Семантическая разметка - когда тег выбран по смыслу, а не по виду - сразу улучшает поддержку клавиатуры, наполняет дерево доступности, облегчает разбор поисковиком и повышает устойчивость приложения. Один ход закрывает четыре фронта, и стоит он ровно ничего.
Наивная привычка тянет в другую сторону. Раз всё стилизуется через CSS, проще взять div и навесить на него вид кнопки: класс, отступы, курсор, обработчик клика. На экране неотличимо от настоящей кнопки, и задача будто решена. Но решён только визуальный слой, а смысловой остался пустым.
Вот что теряется, если собрать кнопку добавления в корзину на div. Нет роли - платформа не знает, что это кнопка. Нет клавиатурной активации - на него не встать по Tab, не нажать Enter или Space. Нет состояния disabled - не сообщить, что действие временно недоступно. Нет ожидаемого поведения, которое пользователь привык получать от кнопок. Всё это придётся воспроизводить руками на JavaScript и ARIA, и почти наверняка не полностью.
Правильный путь - взять родной элемент. У настоящей button имя, роль, попадание в фокус, обработка Enter и Space и состояние disabled уже реализованы браузером - одинаково во всех вспомогательных технологиях и бесплатно. Отсюда правило, экономящее недели работы: native HTML first, сначала родной элемент. ARIA и обработчики достраивают недостающее, а не воспроизводят с нуля уже существующее.
Чтобы почувствовать, сколько браузер отдаёт даром, полезно свести несколько родных элементов и то, что каждый приносит в дерево доступности и в поведение.
| Элемент | Роль в дереве | Что браузер даёт бесплатно |
|---|---|---|
| button | button | Фокус, Enter/Space, состояние disabled |
| a[href] | link | Фокус, Enter, переход по адресу, открытие в новой вкладке |
| h1..h6 | heading (с уровнем) | Иерархия и быстрая навигация по заголовкам |
| nav, main |
| landmark |
| Ориентиры для перехода по областям страницы |
Отдельная тема - заголовки. У страницы должен быть один понятный главный заголовок h1, а уровни h2, h3 и далее обязаны отражать структуру содержимого, а не размер шрифта. Для карточки кроссовок: h1 - название модели, h2 - разделы вроде описания, характеристик и отзывов. Программа чтения строит по этим уровням карту страницы и даёт прыгать по ней, а поисковик читает ту же иерархию как оглавление темы. Выбрать h3 только потому, что он мельче, - сломать обе карты сразу.
Ссылки и кнопки - не взаимозаменяемые украшения, а разные по смыслу элементы. Ссылка перемещает пользователя к ресурсу: к другой странице, к разделу, к файлу; у неё есть адрес, её можно открыть в новой вкладке и скопировать. Кнопка выполняет действие здесь и сейчас: добавляет в корзину, отправляет форму, раскрывает панель. Перепутанные местами, они сбивают и клавиатурного пользователя, и робота: ссылка, которая на деле кнопка, обещает переход, которого не будет.
Крупные смысловые области страницы размечаются ориентирами - landmarks: header, nav, main, footer. Они дают вспомогательной технологии быструю навигацию и чёткую структуру для разбора. Каркас остаётся строгим: у навигации осмысленное имя, основной контент в одном main, таблицы - только для табличных отношений, а не для раскладки, а интерактивные элементы не вкладываются друг в друга (ссылка внутри кнопки - сломанное дерево доступности).
Провал здесь незаметен на макете и виден только в реальном использовании. Страница из вложенных div-ов рендерится идеально, но программа чтения не находит ни одного ориентира, ни одной кнопки, ни одной внятной иерархии заголовков - для неё это ровное полотно текста без карты. Правильный выбор элементов возвращает всё это бесплатно, поэтому семантика идёт первой, а стили и ARIA - следом.
<div class="button" onclick="save()">
Сохранить
</div><button type="button" disabled>
Сохранить
</button><header>…</header>
<nav aria-label="Основная навигация">…</nav>
<main id="main">
<article>
<h1>Заголовок страницы</h1>
…
</article>
</main>
<footer>…</footer>