Расширенные шаблоны взаимодействия при наведении и фокусе
Создание доступных интерактивных состояний без JavaScript
Почему важны состояния взаимодействия
Состояния при наведении и фокусе — это основные подсказки, что говорит пользователю: «эта область интерактивна». Когда эти признаки отсутствуют или реализованы плохо, пользователи, навигационные с помощью клавиатуры, голоса или переключателей, остаются dогадываться. WCAG 2.2 требует, чтобы интерактивные элементы показывали видимый фокус и чтобы информация, передаваемая при наведении, была также доступна при фокусе. Выполнить это можно с помощью чистого CSS — без необходимости JavaScript.
Основные принципы
- Паритет фокуса: Всё, что появляется при
:hover, должно появляться при:focusи, желательно,:focus-visible. - Безопасность при движении: Предпочтительнее использовать
@media (prefers-reduced-motion: reduce)для отключения сложных переходов для чувствительных к движению пользователей. - Большие целевые зоны: Пользователи с сенсорным управлением не имеют состояния hover; убедитесь, что фокусированные/активированные элементы имеют щедрые отступы.
CSS-селекторы, на которые можно положиться
Современная тройка селекторов для дизайна с состояниями:
a:hover,
a:focus,
a:focus-visible {}
.parent:focus-within {}
input[type="checkbox"]:checked + .panel {}
:focus-visible нацелено на клавиатурный фокус без перекрытия авторских рамок для мышиных пользователей. :focus-within позволяет контейнеру реагировать, когда любой его ребёнок получает фокус — важно для виджетов раскрытия и меню.
Шаблон 1: Подсказка при наведении и фокусе
Подсказки ни при каких условиях не должны быть только при наведении. В приведённом ниже коде используется aria-describedby для ссылки на подсказку, которая визуально скрыта по умолчанию и отображается при наведении или фокусе.
<button aria-describedby="tip1" class="icon-btn">?</button>
<span role="tooltip" id="tip1" class="tooltip">Дополнительная информация</span>
.tooltip {
position: absolute;
opacity: 0;
transform: translateY(.5rem);
transition: all .2s;
}
.icon-btn:hover + .tooltip,
.icon-btn:focus + .tooltip,
.icon-btn:focus-visible + .tooltip {
opacity: 1;
transform: none;
}
Шаблон 2: Раскатывающаяся карта
Карта может показывать больше текста, когда пользователь выбирает взаимодействие, оставляя предварительный просмотр аккуратным.
<a href="#" class="card">
<h4>Проект Альфа</h4>
<p class="meta" aria-hidden="true">Январь 2026</p>
<div class="details">Полное описание…</div>
</a>
.card {
display: block;
border: 1px solid #ccc;
overflow: hidden;
}
.card .details {
max-height: 0;
transition: max-height .3s ease;
}
.card:hover .details,
.card:focus .details,
.card:focus-visible .details {
max-height: 8rem; /* достаточно для содержимого */
}
Шаблон 3: Drop-down навигация на CSS
Переключатели checkbox, связанные с :focus-within, позволяют сделать выпадающее меню открытым для клавиатурных пользователей и закрывать его при потере фокуса.
<nav class="menu">
<input type="checkbox" id="toggle" class="menu__toggle" />
<label for="toggle" class="menu__button">Продукты</label>
<ul class="submenu">
<li><a href="#">API</a></li>
<li><a href="#">CLI</a></li>
<li><a href="#">SDK</a></li>
</ul>
</nav>
.menu__toggle {
position: absolute;
clip: rect(0 0 0 0);
}
.submenu {
max-height: 0;
overflow: hidden;
transition: max-height .25s ease;
}
.menu__toggle:checked ~ .submenu,
.menu:focus-within .submenu {
max-height: 20rem;
}
Хак с checkbox удерживает меню открытым для пользователей с мышью, в то время как :focus-within позволяет переключаться между ссылками при навигации с помощью таба.
Шаблон 4: Переключатель без JavaScript
Такой же подход через чекбокс может создать ARIA-совместимую кнопку-переключатель.
<input type="checkbox" id="darkmode" class="visually-hidden" />
<label for="darkmode" role="button" aria-pressed="false" id="dmLabel">
Тёмный режим
</label>
#darkmode:checked + label {
background: #000;
color: #fff;
}
#darkmode:checked + label[aria-pressed] {
aria-pressed: true;
}
Поскольку атрибуты ARIA не могут динамически изменяться через CSS, значение aria-pressed статично. Пользователи скринридеров всё равно поймут состояние через нативный чекбокс. Если требуютсь динамические ARIA-атрибуты, потребуется JavaScript.
Шаблон 5: Вкладки только на CSS
Радио-кнопки создают полностью клавиатурно-доступный набор вкладок.
<div class="tabs">
<input type="radio" name="tabs" id="t1" checked>
<label for="t1">HTML</label>
<input type="radio" name="tabs" id="t2">
<label for="t2">CSS</label>
<input type="radio" name="tabs" id="t3">
<label for="t3">SVG</label>
<div class="panel html">…</div>
<div class="panel css">…</div>
<div class="panel svg">…</div>
</div>
.panel { display: none; }
#t1:checked ~ .html,
#t2:checked ~ .css,
#t3:checked ~ .svg { display: block; }
label:focus,
label:hover {
outline: 2px solid #0b7;
}
Радио-кнопки находятся вне видимой области или скрыты с помощью position: absolute;. Так как они остаются в порядке табуляции, screen reader озвучивают их как положено, соответствуя рекомендациям по созданию вкладок ARIA.
Тестирование без мыши
- Отключите мышь или используйте вариант Keyboard Access в вашей ОС.
- Используйте клавишу Tab для перехода по элементам; убедитесь, что все состояния при наведении доступны и видимы.
- Активируйте Shift+Tab, чтобы вернуться назад, и убедитесь, что фокус не застревает.
- Запустите скринридер или вкладку Accessibility в инструментах браузера, чтобы проверить логический порядок и роли.
Прогрессивное улучшение и когда добавлять JavaScript
Паттерны только на CSS отлично подходят для простых раскрытий, дисплеев и переключателей темы. Подключайте JavaScript, когда нужно:
- Динамическое изменение ARIA-атрибутов (
aria-expanded,aria-pressed). - Сложное управление фокусом (например, защита фокуса внутри модальных окон).
- Асинхронная загрузка данных, анимации или сохранение пользовательских настроек.
Основные выводы
- Объединяйте
:hover,:focus,:focus-visibleи:focus-within, чтобы обеспечить паритет для всех режимов ввода. - Хаки с checkbox и радиокнопками позволяют реализовать переключатели, вкладки и выпадающие меню без скриптов.
- Проверяйте взаимодействие только с клавиатурой, чтобы убедиться, что все визуальные сигналы доступны для пользователей без указателя.
- Используйте подход прогрессивного улучшения: начинайте с семантического HTML, обогащайте его CSS и добавляйте JavaScript только там, где это необходимо для доступа и динамичности.


