Пройди свой сайт без мышки. Скорее всего ты застрянешь на третьем экране
Основной чат
Чат для вайбкодеров: новости, гайды, поиск исполнителей, маркетплейс и разбор реальных кейсов.
Закройте глаза, отодвиньте мышку, уберите руку с трекпада. Откройте свой продукт и пройдите главный сценарий — регистрацию, оформление заказа, создание проекта — только с клавиатуры. Не как упражнение на доступность, а как обычный пользователь, который сегодня сломал мышь, или едет в поезде с тачпадом, который глючит, или просто привык работать с клавиатуры.
Скорее всего, вы застрянете. Иногда на втором экране, чаще на третьем. На модалке, которую не закрыть по Escape. На дропдауне, который не открывается с Enter. На кастомном чекбоксе, который вообще не получает фокус. На «крестике», который визуально есть, а в tab-порядке его нет.
И вот что важно: это не история про слепых пользователей и не про соответствие WCAG ради юриста. Это история про то, что половина интерфейса держится на одной модальности ввода — и если её убрать, продукт ломается. А это уже не про доступность. Это про качество.
Почему это вообще проблема продукта, а не «инклюзивности»
Когда дизайнер слышит «доступность», в голове щёлкает: скринридеры, контраст, alt-тексты, отдельная задача в бэклоге с приоритетом «потом». Это удобная отговорка, потому что позволяет не думать о клавиатуре каждый день.
Но клавиатурная навигация — это не подмножество доступности. Это базовая механика интерфейса, которой пользуются:
- любой опытный пользователь, который быстрее печатает, чем целится курсором
- все, кто работает на ноутбуке без мыши в дороге
- пользователи скринридеров и людей с моторными ограничениями
- автотесты, которые ходят по интерфейсу через tab/enter
- браузерные расширения и автозаполнение, которые опираются на фокус
- LLM-агенты и autofill из паролей, которым нужен предсказуемый focus order
Если клавиатурный сценарий рваный — страдает не только пользователь скринридера. Страдает скорость работы вашего же саппорта, которому каждый день приходится заполнять одну и ту же форму.
Где это бьёт по метрикам
В команде это редко всплывает как «проблема доступности». Всплывает иначе:
- падает конверсия в формах с нестандартными полями (даты, мультиселекты, загрузка файлов)
- растёт количество тикетов «не могу закрыть окно», «ничего не нажимается»
- автотесты на E2E постоянно падают, и QA пишет хрупкие селекторы вместо опоры на роли
- новый онбординг с модалками показывает странный drop-off у опытных пользователей — а это те, кто жмёт Esc по привычке и улетает не туда
Связь не всегда видна напрямую. Но если начать чинить клавиатуру системно, эти симптомы уходят пачкой.
Тест на 10 минут: пройдите свой продукт
Прежде чем читать дальше, сделайте упражнение. Оно занимает меньше времени, чем дочитать эту статью, и даёт больше, чем любой аудит.
Правила
- Откройте продукт в обычном браузере. Не в режиме разработчика, не с включённым скринридером — просто браузер.
- Уберите руку с мыши. Совсем. Положите на колено.
- Пройдите ключевой сценарий целиком: от входа до получения результата.
- Разрешены только: Tab, Shift+Tab, Enter, Space, Escape, стрелки.
Что фиксировать
Заведите простой документ и пишите по ходу:
- Где пропал фокус. Нажал Tab — и не видно, на чём ты. Это самая частая поломка.
- Где фокус ушёл не туда. Открыл модалку — а фокус остался на кнопке под ней.
- Где порядок странный. Tab прыгает с шапки в футер, потом в середину.
- Что не нажимается. Кастомный селект, свитч, карточка-кнопка.
- Что не закрывается. Модалка, попап, дропдаун без Escape.
- Куда нельзя добраться вообще. Иконка-меню, тултип, действие в таблице.
После такого прохода обычно набирается 15–30 пунктов на средний продукт. Это и есть ваш бэклог на ближайший спринт по клавиатуре — и он чинится быстрее, чем кажется.
Анти-паттерны, которые встречаются почти везде
Прежде чем разбирать, как строить хорошо, полезно увидеть типовые поломки. Если узнали что-то своё — это нормально, они есть у всех.
Невидимый фокус
Самый частый случай: кто-то в CSS написал outline: none на кнопках, потому что «синяя рамка некрасивая», и не добавил замену. Tab работает, но пользователь не видит, где он. Это эквивалент курсора мыши, который стал прозрачным.
Focus trap, которого нет
Открылась модалка — а Tab продолжает гулять по странице под ней. Пользователь жмёт Enter и случайно нажимает кнопку, которую даже не видит. Обратная история — focus trap есть, но из модалки не выйти, потому что Escape не повешен.
Кастомные компоненты без ролей
Div с обработчиком onClick — это не кнопка. Для глаза — да, для клавиатуры и скринридера — просто прямоугольник. Если в дизайн-системе есть <Card clickable>, который под капотом div — это бомба замедленного действия.
Tab-порядок по DOM, а не по логике
Визуально кнопка «Продолжить» внизу формы. В DOM она оказалась раньше полей, потому что так удобнее верстальщику. Пользователь жмёт Tab и улетает на «Продолжить» до того, как заполнит форму.
Skip link, который никто не видел
Формально есть «Перейти к содержимому» в начале страницы. По факту он скрыт навсегда, появляется только при фокусе, и половина команды даже не знает, что он там.
Если из этого списка узнали хотя бы три пункта — добро пожаловать в клуб. Дальше разберём, как это чинить системно, а не точечно.
Как дизайнеру встроить клавиатурный сценарий в работу
Чинить клавиатуру в проде — это всегда дороже, чем заложить её в макет. Не потому что код сложный, а потому что половина решений принимается на уровне «как устроен экран», а не «как покрасить кнопку». Если дизайнер не подумал про порядок и состояния — разработчик будет угадывать. И угадает не туда.
Что закладывать в макет, а не оставлять на потом
В Figma редко рисуют focus-состояния. Это первый признак того, что про клавиатуру забыли. Минимум, который должен быть в любом интерактивном компоненте:
- default, hover, focus-visible, active, disabled
- состояние для focus внутри группы (например, выбранный чип в фокусе)
- как выглядит focus на тёмном фоне и на цветной кнопке — контраст не должен ломаться
Focus — это не hover. Hover показывает «мышь сверху», focus показывает «сейчас я тут, нажму Enter — что-то произойдёт». Если они выглядят одинаково, пользователь с клавиатуры теряется в момент, когда курсор мыши тоже над страницей.
Порядок чтения как часть макета
На любом сложном экране есть несколько логических зон: шапка, фильтры, контент, действия. В макете полезно прямо стрелочками показать, в каком порядке по ним должен идти Tab. Это не лишняя бюрократия — это спасает от ситуации, когда разработчик собирает страницу из компонентов в том порядке, в каком ему удобно, и Tab прыгает зигзагом.
Особенно это важно для:
- модалок с формой и кнопками «Отмена / Сохранить»
- таблиц с действиями в строках
- сайдбаров с вложенной навигацией
- многошаговых форм, где «Назад» и «Далее» визуально разнесены
Шорткаты: договоритесь о словаре
Если в продукте появляются хоткеи (/ для поиска, ? для подсказок, g + i для перехода), они должны быть едиными во всех разделах. Самая частая ошибка — каждая команда придумывает свои, и в одном разделе Esc закрывает модалку, а в другом сбрасывает фильтры.
Полезно завести страницу в дизайн-системе: список глобальных шорткатов, список зарезервированных клавиш (Esc, Enter, Space, Tab — их нельзя переопределять под свои хотелки) и правило, как показывать подсказку с шорткатом в тултипе.
Диагностика на ревью макета
Когда вы смотрите чужой макет (или свой через день), есть короткий список вопросов, который вылавливает большинство клавиатурных проблем ещё до разработки.
Вопросы для дизайн-ревью
- Где здесь фокус по умолчанию, когда экран открылся?
- Что произойдёт, если нажать Tab прямо сейчас? Куда уйдёт фокус?
- Как пользователь закроет это окно без мыши?
- Есть ли состояние focus-visible у всех кликабельных элементов, включая иконки и карточки?
- Если это модалка — куда вернётся фокус после её закрытия?
- Если это список — стрелки внутри работают? Tab выходит наружу?
- Disabled-кнопка получает фокус или пропускается? Что лучше для этого сценария?
Последний пункт стоит пояснить. Полностью убирать disabled-элемент из tab-порядка — значит, пользователь не поймёт, почему «Сохранить» недоступно. Часто правильнее оставить фокус, но дать понятную причину рядом.
Сценарии, на которых обычно ломается
Соберите внутренний список «адских флоу» и прогоняйте их с клавиатуры на каждом релизе:
- логин с переключением на код из СМС
- оплата с выбором карты и вводом CVC
- загрузка файла перетаскиванием (и альтернатива через кнопку)
- поиск с автодополнением: стрелки выбирают подсказку, Enter подтверждает, Esc закрывает
- мультиселект с поиском внутри
- таблица с inline-редактированием ячеек
Это места, где обычно стоят кастомные компоненты, и где клавиатурный сценарий ломается первым.
Типичные ошибки на стороне дизайнера
Не все клавиатурные баги — вина разработки. Часть из них растёт прямо из макета.
Прозрачный focus ради эстетики
«Давайте сделаем focus как hover, только чуть-чуть». В итоге на светлой кнопке focus — серая рамка на белом фоне, которую не видно. Правило простое: focus должен быть заметен с расстояния полуметра от экрана. Если приходится приглядываться — он не работает.
Иконка без текста и без роли
Иконка-кнопка «три точки» в углу карточки. На макете красиво, в проде — для скринридера это «button», без названия. Дизайнер должен в макете подписывать, какой aria-label у такой кнопки. Это не работа разработчика — он не знает, что эта иконка означает «Открыть меню действий с задачей».
Модалка, в которой нет первого фокуса
Открылась — а фокус непонятно где. Хорошая модалка ловит фокус на первом значимом элементе: на поле ввода, если есть форма; на заголовке, если это подтверждение; на главной кнопке, если это короткий вопрос «Удалить?». В макете это решение принимается, а не оставляется «на разработку».
Тултип, который виден только на hover
Подсказка по иконке появляется на наведение мыши. С клавиатуры — никак. Если тултип несёт важную информацию (например, объясняет, что значит иконка), он должен показываться и на focus.
Короткий итог
Клавиатурная доступность — это не «специальная фича для слепых». Это базовая характеристика интерфейса, как контраст или читаемость. Она ловится за один проход без мыши, чинится в основном на уровне макета и дизайн-системы, и окупается ростом скорости работы внутренних пользователей раньше, чем формальной галочкой по WCAG. Дальше разберём, как превратить это в постоянную практику команды, а не разовый героизм одного дизайнера.
Когда в команде сидит AI и пишет код
Сейчас половина продуктовых команд так или иначе использует AI-ассистента: Copilot, Cursor, что-то на основе MCP, генерацию компонентов из Figma. Это сильно меняет, кто и где роняет клавиатурную доступность. Раньше виноват был дизайнер, который не подумал, или разработчик, который не успел. Теперь добавился третий: модель, которая бодро сгенерировала <div onClick={...}> вместо кнопки, потому что в обучающей выборке такого было много.
Что AI делает плохо по умолчанию
Без явных инструкций модель почти всегда:
- ставит интерактивность на
divиspan, потому что это короче - забывает
aria-labelна иконочных кнопках - придумывает кастомный dropdown без управления стрелками
- делает модалку без trap фокуса и без возврата фокуса на триггер
- использует
tabindex="-1"иtabindex="3"так, как будто это нормально
Это не злой умысел, это статистика. Если в промпте не сказано «семантические теги, focus management, клавиатурная навигация», модель оптимизирует на «выглядит как на скриншоте».
Что должно быть в системном промпте команды
Если у вас есть общий промпт для AI-агента (в Cursor, в кастомном тулинге, в MCP-сервере для Figma), туда стоит зашить три вещи:
- использовать нативные элементы:
button,a,input,dialog, и только при необходимости — кастом сrole - любой интерактивный элемент должен быть достижим Tab и активироваться Enter/Space
- при генерации модалок, меню и комбобоксов опираться на ARIA Authoring Practices, а не выдумывать
Это короткие правила, но они меняют выдачу заметно. Проверяется просто: попросите сгенерировать «дропдаун с поиском» до и после — разница видна сразу.
Figma → код через MCP: где теряется доступность
Когда дизайн уезжает в генерацию через MCP или плагин, теряется именно семантика. В макете «карточка с тремя точками» — это фрейм, прямоугольник и иконка. Модель не знает, что три точки — это кнопка «Открыть меню действий». Поэтому в макете нужно явно подписывать роли и aria-label прямо в слое или в описании компонента. Это не лишняя работа — это единственный способ донести смысл до автогенерации.
Как проверять клавиатурное качество
Чтобы это не было разовым подвигом, нужны три уровня проверки: быстрый ручной, автоматический и регулярный.
Минута на компонент
Перед мерджом компонента в дизайн-систему:
- Tab по нему — фокус виден, порядок логичный
- Enter и Space делают то, что ожидается от его роли
- Esc закрывает, если есть что закрывать
- стрелки работают, если это список, меню или таблица
- focus-visible не пропадает на тёмной теме и на цветном фоне
Автоматика как нижний порог
Axe, Lighthouse, Storybook a11y addon ловят грубые вещи: отсутствие label, кнопку без имени, контраст фокуса ниже порога. Это не заменяет ручную проверку, но не пускает откровенный мусор в main. Полезно прикрутить такие проверки в CI на уровне сторибука компонентов.
Регулярный прогон сценариев
Раз в спринт или раз в релиз — кто-то один проходит ключевые флоу полностью без мыши. Не для галочки, а с записью экрана и списком мест, где застрял. Это та же практика, что и кросс-браузерное тестирование: скучно, но единственное, что реально работает.
Как объяснить это команде и не быть занудой
«Нам нужна accessibility» — фраза, которая отскакивает от любого бэклога. Работает другой заход.
Говорите про скорость, а не про инвалидность
Внутренние пользователи — операторы, саппорт, аналитики — работают с интерфейсом по восемь часов в день. Для них клавиатурный флоу — это буквально деньги: меньше движений мышью, выше throughput. Это аргумент, который понимает любой продакт и любой руководитель операционки.
Покажите видео, а не отчёт
Запишите тридцать секунд, как вы пытаетесь оформить заказ без мыши и застреваете на третьем экране. Это сильнее любого WCAG-аудита. После такого видео разговор про приоритет задачи занимает пять минут вместо двух недель.
Встройте в definition of done
Не отдельной задачей «починить доступность через квартал», а строкой в DoD компонента: «проходится с клавиатуры, focus-visible на всех состояниях, корректные роли». Тогда это не героизм, а норма, и не нужно каждый раз воевать за приоритет.
Вопросы, которые стоит задавать на синке
- какой клавиатурный сценарий у этой фичи?
- что делает Esc на этом экране?
- куда возвращается фокус после закрытия модалки?
- если это новый кастомный компонент — на какой паттерн из ARIA он опирается?
Эти четыре вопроса на планировании экономят недели переделок.
Короткий итог
AI ускоряет генерацию интерфейсов, но по умолчанию ухудшает их клавиатурное качество — значит, правила нужно зашивать в промпты и в дизайн-систему, а не надеяться на здравый смысл модели. Проверка делается тремя слоями: ручная минута на компонент, автоматика в CI, регулярный прогон сценариев целиком. А разговор с командой ведётся не через accessibility-моралите, а через скорость работы и видео, где интерфейс не проходится без мыши.
Чеклист перед релизом фичи
Этот список — то, что должно умещаться на одну страницу и проходиться за десять минут на ревью. Не как формальность, а как фильтр, который ловит большую часть боли до того, как она доедет до прода.
Клавиатура
- по всему флоу можно пройти от первого экрана до подтверждения, не трогая мышь
- порядок Tab идёт сверху вниз, слева направо, без скачков через всю страницу
- focus-visible виден на светлой и тёмной теме, на цветных кнопках и поверх картинок
- ловушки фокуса есть только в модалках и дровэрах, и они отпускают по Esc
- после закрытия модалки фокус возвращается на элемент, который её открыл
- skip link «к основному контенту» работает и не спрятан намертво
Семантика и роли
- кастомные компоненты опираются на конкретный паттерн ARIA, а не на «ну, похоже на дропдаун»
- иконки-кнопки имеют aria-label или скрытый текст
- состояния (выбрано, развёрнуто, загружается) переданы через aria-*, а не только цветом
- ошибки формы связаны с полями через aria-describedby, а не висят рядом в воздухе
AI-генерация
- в системный промпт зашиты правила про семантику, фокус и ARIA
- сгенерированный компонент прогнан через axe или storybook a11y до ревью
- ревьюер прошёл компонент клавиатурой руками, а не поверил скриншоту
Анти-паттерны, которые встречаются чаще всего
Это не экзотика, а вещи, которые попадают в прод почти в каждом проекте, где доступность не зашита в процесс.
Div, который притворяется кнопкой
Самый частый случай — особенно после AI-генерации. Визуально кнопка, кликается мышью, с клавиатуры не активируется, скринридер видит «группа». Чинится переписыванием на button или добавлением role, tabindex и обработчика Enter/Space. Лучше первым способом.
Модалка без возврата фокуса
Открыли диалог, закрыли — фокус улетел в начало страницы или в body. Клавиатурный пользователь теряет место и начинает заново. Возврат фокуса на триггер должен быть дефолтом компонента модалки, а не задачей каждого, кто её использует.
Outline: none без замены
Классика, которая до сих пор приезжает из «красивых» темплейтов. Дизайнеру не нравится системный контур — он его убирает и забывает нарисовать свой. В результате клавиатурный пользователь не понимает, где он находится. Если убираете outline, обязательно рисуете focus-visible поверх.
Тосты и автодисмисс важных сообщений
Сообщение об ошибке всплывает на три секунды и пропадает. Зрячий с мышью успевает, клавиатурный пользователь и пользователь скринридера — нет. Критичные сообщения не должны исчезать сами, а должны быть в живом регионе.
Кастомный селект ради дизайна
Команда переписывает нативный select, потому что «он некрасивый», и теряет на этом клавиатуру, мобильную клавиатуру, поиск по буквам, интеграцию со скринридером. В девяти случаях из десяти стоит остаться на нативном и стилизовать его, а не делать свой.
Вопросы для ревью дизайна и кода
Эти вопросы стоит задавать одинаково — и на ревью макета в Figma, и на пул-реквесте.
По макету
- где здесь фокус, и как он выглядит на каждом состоянии?
- какие элементы — кнопки, какие — ссылки, какие — переключатели? подписано ли это в слоях?
- что происходит по Esc на этом экране?
- куда уезжает фокус после успешной отправки формы?
По коду
- если это не нативный элемент — на какой ARIA-паттерн опирается реализация?
- что в DOM-порядке: совпадает ли он с визуальным?
- какие aria-* атрибуты меняются в рантайме, и кто их обновляет?
- что говорит скринридер при ошибке валидации?
По AI-сгенерированному коду
- какие правила доступности были в промпте?
- прошёл ли результат автоматическую проверку до ревью человеком?
- кто-то реально нажимал Tab по этому компоненту, или мы смотрим только на скриншот?
Что забрать с собой
Клавиатурная доступность — не отдельная дисциплина, а проверка на то, что интерфейс вообще понятен как система: где кнопки, где состояния, где выходы. Если без мыши вы застреваете на третьем экране — это сигнал, что интерфейс держится на визуальных подпорках, а не на структуре. Чините структуру: семантику, порядок, фокус, возвраты. Тогда и мышь, и клавиатура, и скринридер, и автогенерация будут работать с одним и тем же продуктом, а не с тремя разными.