Универсальный фокус для интерактивных элементов
Описание паттерна
Этот паттерн помогает создать функциональное состояние фокуса для всех интерактивных элементов. Он делает интерфейс доступнее и понятнее для пользователей, которые работают с сайтом с клавиатуры или с помощью вспомогательных технологий.
Код паттерна
*:focus-visible {
outline: 3px solid light-dark(#000000, #ffffff);
outline-offset: 0.25rem;
border-radius: 0.13rem;
}
:root {
color-scheme: light dark;
}

Постановка задачи
Нужно обеспечить:
- доступный фокус для всех интерактивных элементов;
- универсальную обводку, которая одинаково хорошо видна в светлой и тёмной теме;
- простое решение «на все случаи», которое можно применить глобально.
Антипаттерн
Обычно разработчики делали так:
*:focus {
outline: none; /* убираем фокус */
}
или использовали только чёрный/синий цвет по умолчанию. Это ухудшает доступность особенно, когда в проекте есть переключение между тёмной и светлой темами.
Никогда не отключайте
outline
для интерактивных элементов. Это нарушает доступность. Либо подготовьте достойную кастомную обводку.
Современный подход
Мы используем селектор :focus-visible
и современный цветовой синтаксис light-dark()
:
*:focus-visible {
outline: 3px solid light-dark(#000000, #ffffff);
outline-offset: 0.25rem;
border-radius: 0.13rem;
}
:root {
color-scheme: light dark;
}
Почему так лучше?
:focus-visible
Срабатывает только при управлении с клавиатуры и вспомогательными технологиями. При клике мышью лишних обводок нет.light-dark()
Позволяет автоматически менять цвет в зависимости от темы: чёрный(#000000
) в светлой, белый(#ffffff
) в тёмной.outline-offset
иborder-radius
Делаем обводку чуть смещённой и закруглённой для лучшей визуальной читаемости.color-scheme: light dark
Сообщаем браузеру, что сайт поддерживает обе темы.
Примеры использования
Пример №1: Базовый
<button>Нажми меня</button>
<a href="#">Ссылка</a>
<input type="text" placeholder="Фокус сюда" />
Ссылка
На клавиатуре все элементы получат одинаковый аккуратный фокус. Нажимай Tab, чтобы посмотреть на фокус.
Пример №2: С кастомным цветом
Можно использовать currentcolor
, если хочется, чтобы обводка наследовала цвет текста элемента:
*:focus-visible {
outline: 3px solid currentcolor;
outline-offset: 0.25rem;
border-radius: 0.13rem;
}
Обратите внимание
На данный момент самый универсальным и рабочим способом является подход, когда outline
вообще не изменяется, то есть оставляется по умолчанию, который принят в конкретном браузере.
Браузер учитывает контрастность цвета обводки и фона. Он прекрасно работает в любой теме, а также если в теме используется противоположный цвет фона у блока, например в тёмной теме может быть блок с светлым фоном. Такой подход работает с любой версией браузера.
Поддержка браузерами
- Chrome 123+ из-за
light-dark()
- Firefox 120+ из-за
light-dark()
- Safari 17.4+ из-за
light-dark()
Обсудить паттерн
Этот паттерн можно обсудить на GitHub — задавайте вопросы, делитесь идеями и опытом.
Если у вас есть мысль для нового паттерна, пример из практики или предложение по улучшению — расскажите об этом в обсуждении: любой вклад помогает развивать проект.