Перейти к основному содержимому
    Назад к блогу
    РазработкаОпубликовано 15 октября 2026 г.

    Клавиатура и фокус: как сделать сайт, которым можно пользоваться без мыши

    Порядок фокуса, видимый индикатор, ссылка «Перейти к содержимому», липкие шапки, модальные окна на dialog и составные виджеты

    Клавиатура и фокус: как сделать сайт, которым можно пользоваться без мыши

    Часть 4 из 8 серии «Доступный веб на практике». Предыдущая часть: Доступные формы: подписи, подсказки, ошибки и автозаполнение.

    Простая проверка: уберите мышь и попробуйте пройти главный сценарий своего сайта клавишей Tab. Большинство проблем найдётся за пять минут. В этой части — как сделать навигацию с клавиатуры удобной и предсказуемой.

    Клавиатура и фокус

    Порядок фокуса

    Фокус должен двигаться в логическом порядке — обычно таком же, как визуальный. Порядок определяется порядком элементов в DOM, поэтому:

    • не переставляйте элементы визуально через order, flex-direction: row-reverse или абсолютное позиционирование так, что порядок табуляции перестаёт совпадать с порядком чтения;
    • не используйте положительный tabindex (tabindex="1", "2"...). Он ломает естественный порядок для всей страницы.

    tabindex нужен только в двух значениях:

    Значение Когда
    tabindex="0" Сделать фокусируемым кастомный интерактивный элемент (если без него никак)
    tabindex="-1" Сделать элемент фокусируемым программно (element.focus()), но не через Tab: заголовок после перехода, контейнер ошибок, модальное окно

    Видимый фокус

    Никогда не делайте так без замены:

    /* ❌ Убивает навигацию с клавиатуры */
    *:focus { outline: none; }
    

    Если стандартная обводка не нравится дизайнеру — замените её, используя :focus-visible. Этот псевдокласс срабатывает при навигации с клавиатуры, но не при клике мышью:

    /* ✅ Заметный фокус только для клавиатуры */
    :focus-visible {
      outline: 3px solid #1a5fb4;
      outline-offset: 2px;
    }
    
    /* Убираем обводку при клике мышью там, где браузер её всё же рисует */
    :focus:not(:focus-visible) {
      outline: none;
    }
    

    Индикатор фокуса должен быть контрастен к фону (не менее 3:1) и заметен по площади. Если вы рисуете фокус через box-shadow, добавьте прозрачный outline — он станет видимым в режиме высокой контрастности Windows, где тени отключаются:

    .button:focus-visible {
      box-shadow: 0 0 0 3px #1a5fb4;
      outline: 3px solid transparent; /* проявится в forced-colors */
    }
    

    Ссылка «Перейти к содержимому»

    Без неё пользователь клавиатуры на каждой странице проходит Tab'ом всё меню.

    <a class="skip-link" href="#main">Перейти к содержимому</a>
    ...
    <main id="main" tabindex="-1">...</main>
    
    .skip-link {
      position: absolute;
      left: 1rem;
      top: -100px;
      padding: 0.5rem 1rem;
      background: #fff;
      color: #000;
      z-index: 1000;
    }
    .skip-link:focus {
      top: 1rem;
    }
    

    Ссылка невидима, пока не получит фокус — первым же нажатием Tab.

    Фокус не должен прятаться (WCAG 2.4.11)

    Классическая ситуация: липкая шапка. Пользователь жмёт Tab, страница прокручивается, и элемент в фокусе оказывается под шапкой. Решение в одну строку:

    html {
      scroll-padding-top: 5rem; /* высота липкой шапки + запас */
    }
    

    То же касается баннеров cookies и плавающих чатов: они не должны перекрывать элемент в фокусе.

    Ловушки фокуса

    Фокус не должен застревать в элементе без возможности выйти клавиатурой (критерий 2.1.2). Типичные виновники — встроенные плееры, виджеты карт, iframe сторонних сервисов, кастомные редакторы.

    Единственное место, где фокус должен удерживаться внутри, — модальное окно. Но и там Esc должен его закрывать.

    Модальное окно на <dialog>

    Раньше доступная модалка требовала десятков строк кода: ловушка фокуса, скрытие фона от скринридера, Esc, возврат фокуса. Сегодня всё это даёт нативный <dialog> с методом showModal():

    <button type="button" id="open-dialog">Записаться на консультацию</button>
    
    <dialog id="booking" aria-labelledby="booking-title">
      <h2 id="booking-title">Запись на консультацию</h2>
      <form method="dialog">
        <label for="b-name">Имя</label>
        <input id="b-name" name="name" autocomplete="name" required>
    
        <button value="submit">Записаться</button>
        <button value="cancel" formnovalidate>Отмена</button>
      </form>
    </dialog>
    
    const opener = document.getElementById('open-dialog');
    const dialog = document.getElementById('booking');
    
    opener.addEventListener('click', () => dialog.showModal());
    
    // Современные браузеры возвращают фокус сами, но явный возврат надёжнее
    dialog.addEventListener('close', () => opener.focus());
    

    showModal() делает остальную страницу инертной (недоступной для фокуса и скринридера), переносит фокус внутрь, закрывается по Esc, а ::backdrop даёт затемнение фона. Подробнее — MDN: <dialog>.

    Для боковых панелей и других случаев, когда часть страницы нужно временно «выключить», есть атрибут inert:

    <main inert>...</main> <!-- нельзя сфокусировать, скрыто от скринридера -->
    

    Составные виджеты: клавиши-стрелки

    Вкладки, меню, списки выбора, деревья работают по-другому: Tab заходит в виджет один раз, а внутри движение — стрелками (так называемый «roving tabindex»). Если вы пишете такой виджет сами, сверяйтесь с ARIA Authoring Practices Guide — там есть шаблон и ожидаемая клавиатурная модель для каждого паттерна. А лучше — возьмите проверенную библиотеку (см. Доступность в React: id, фокус при смене маршрута и объявления).

    Серия «Доступный веб на практике»

    1. Доступность сайта: для кого она на самом деле и с чего начать
    2. Семантический HTML и альтернативный текст: фундамент доступного сайта
    3. Доступные формы: подписи, подсказки, ошибки и автозаполнение
    4. Клавиатура и фокус: как сделать сайт, которым можно пользоваться без мыши — вы здесь
    5. Контраст, анимация и размер кнопок: визуальная доступность сайта
    6. ARIA и динамический контент: когда атрибуты помогают, а когда вредят
    7. Доступность в React: id, фокус при смене маршрута и объявления
    8. Как тестировать доступность: автоматика, ручная проверка и чеклист

    Следующая часть: Контраст, анимация и размер кнопок: визуальная доступность сайта

    Agama Labs — разработка сайтов и веб-приложений. Если хотите проверить доступность своего сайта, напишите нам: hello@agamalabs.com

    Контакты

    Давайте обсудим ваш проект

    Оставьте заявку, и мы свяжемся с вами в течение 2 часов для бесплатной консультации. Расскажите о вашей идее — поможем найти оптимальное решение.

    Время работы

    Пн–Пт: 08:00 – 17:00 (PST)
    Сб–Вс: Выходной

    Оставить заявку

    Заполните форму, и мы перезвоним вам

    Нажимая кнопку, вы соглашаетесь с политикой конфиденциальности

    Мы ценим вашу конфиденциальность

    Мы используем аналитические cookies, в том числе сторонних сервисов, чтобы понимать, как используется сайт, и улучшать его