Условные стили для клавиатуры и мыши

Современные веб-интерфейсы должны одинаково корректно работать при использовании различных способов ввода: клавиатуры, мыши, сенсорного экрана, стилуса и других указательных устройств. Однако визуальная обратная связь, которая уместна при работе с клавиатурой, может выглядеть избыточной или даже мешающей при использовании мыши.

Наиболее распространённый пример — фокусные контуры (focus outlines). При навигации клавиатурой фокус необходим для понимания текущего положения в интерфейсе. При клике мышью эти контуры часто воспринимаются как визуальный шум.

Разработчики нередко решают эту проблему отключением стиля outline, что приводит к ухудшению доступности. Пользователи клавиатуры теряют ориентиры навигации, а интерфейс становится трудным для использования.

Библиотека ally.js предоставляет инструменты, позволяющие условно применять стили в зависимости от способа взаимодействия, не нарушая доступность.


Проблема фокусных контуров

По умолчанию браузеры применяют фокусный стиль к интерактивным элементам:

button:focus {
  outline: auto;
}

При клике мышью этот стиль также появляется. Чтобы избежать этого, многие проекты используют правило:

:focus {
  outline: none;
}

Подобный подход считается анти-паттерном доступности, поскольку:

  • исчезает индикация текущего элемента для пользователей клавиатуры;
  • ухудшается навигация в формах и меню;
  • интерфейс становится практически непригодным для использования без мыши.

Необходимо различать программный фокус, фокус клавиатуры и фокус после клика мышью.


Подход ally.js

Библиотека ally.js предоставляет модуль focus-source, который отслеживает источник возникновения фокуса.

Основная задача модуля — определить, каким способом был установлен фокус:

  • клавиатура;
  • мышь;
  • сенсорный экран;
  • программный вызов element.focus().

В зависимости от этого источника можно применять разные CSS-классы или стили.


Определение источника фокуса

Модуль focus-source анализирует события пользовательского ввода:

  • keydown
  • mousedown
  • touchstart
  • focus

Последовательность событий позволяет определить, каким действием был вызван фокус.

Пример подключения:

import focusSource from 'ally.js/observe/focus-source';

const handle = focusSource();

После инициализации библиотека начинает отслеживать взаимодействие пользователя.

Получить текущий источник фокуса можно через состояние модуля:

console.log(handle.get());

Возможные значения:

значение описание
keyboard фокус установлен клавиатурой
mouse фокус установлен мышью
touch сенсорное взаимодействие
script программный вызов

Эта информация используется для управления визуальным состоянием интерфейса.


Применение CSS-классов для разных источников фокуса

Часто применяется подход, при котором библиотека добавляет класс на корневой элемент документа.

Пример реализации:

import focusSource from 'ally.js/observe/focus-source';

focusSource({
  context: document.documentElement
});

После этого на <html> добавляются классы:

ally-focus-source-keyboard
ally-focus-source-mouse
ally-focus-source-touch
ally-focus-source-script

Классы автоматически обновляются при изменении способа взаимодействия.


Условные стили интерфейса

Использование CSS-классов позволяет гибко управлять отображением фокуса.

Отображение контура только при навигации клавиатурой

:focus {
  outline: none;
}

.ally-focus-source-keyboard :focus {
  outline: 3px solid #3b82f6;
}

Поведение:

  • при клике мышью фокусный контур скрыт;
  • при перемещении клавиатурой (Tab) контур отображается.

Этот подход сохраняет доступность и избавляет от лишних визуальных эффектов.


Усиленная подсветка для клавиатуры

Для пользователей клавиатуры фокус должен быть максимально заметным.

.ally-focus-source-keyboard button:focus {
  outline: 3px solid #2563eb;
  outline-offset: 2px;
}

Дополнительный outline-offset позволяет избежать наложения на границы элементов.


Отдельные стили для сенсорных устройств

Сенсорные интерфейсы часто требуют более крупных визуальных индикаторов.

.ally-focus-source-touch :focus {
  outline: 4px solid #10b981;
}

Такой подход особенно полезен для гибридных устройств.


Управление состояниями интерфейса

Источник фокуса можно использовать не только для CSS, но и для изменения поведения компонентов.

Пример: меню навигации

Меню может реагировать по-разному в зависимости от метода взаимодействия.

Навигация клавиатурой:

  • меню открывается при фокусе;
  • используется стрелочная навигация.

Навигация мышью:

  • меню открывается при наведении.

Определение источника фокуса:

const handle = focusSource();

document.addEventListener('focusin', (event) => {
  if (handle.get() === 'keyboard') {
    openMenu(event.target);
  }
});

Поведение программного фокуса

Иногда фокус устанавливается через Jav * aScript:

input.focus();

Такой фокус получает источник script.

Это важно учитывать в следующих случаях:

  • автоматический переход между полями формы;
  • открытие модальных окон;
  • перемещение фокуса после закрытия диалогов.

Для таких ситуаций можно задать отдельные стили:

.ally-focus-source-script :focus {
  outline: 2px dashed #6b7280;
}

Сравнение с псевдоклассом :focus-visible

Современные браузеры поддерживают псевдокласс:

:focus-visible

Он автоматически отображает фокус только при навигации клавиатурой.

Пример:

:focus:not(:focus-visible) {
  outline: none;
}

Однако поддержка не во всех браузерах одинакова, особенно в старых версиях.

Преимущества ally.js:

  • работает в старых браузерах;
  • предоставляет информацию о точном источнике взаимодействия;
  • может использоваться в JavaScript-логике.

Комбинирование с другими модулями ally.js

Модуль focus-source часто используется вместе с другими инструментами библиотеки.

ally.maintain.tabFocus

Контроль последовательности фокуса внутри модальных окон.

ally.query.focusable

Определение элементов, которые могут получать фокус.

ally.when.focusable

Ожидание появления фокусируемых элементов в DOM.

Комбинирование этих модулей позволяет создавать интерфейсы с корректной доступностью и адаптивным поведением.


Производительность и обработка событий

Модуль отслеживания источника фокуса использует минимальный набор глобальных обработчиков:

  • keydown
  • mousedown
  • touchstart
  • focus

Обработка выполняется на уровне документа, что снижает нагрузку и не требует добавления слушателей к каждому элементу интерфейса.

Обновление CSS-классов происходит только при изменении источника ввода, что предотвращает лишние перерисовки.


Практический паттерн использования

Часто применяется следующий шаблон:

  1. Подключение focus-source.
  2. Добавление класса на <html>.
  3. Управление стилями через CSS.

Инициализация:

import focusSource from 'ally.js/observe/focus-source';

focusSource({
  context: document.documentElement
});

CSS:

:focus {
  outline: none;
}

.ally-focus-source-keyboard :focus {
  outline: 3px solid #2563eb;
}

Результат:

  • чистый интерфейс при использовании мыши;
  • полноценная индикация для пользователей клавиатуры;
  • соответствие требованиям доступности.

Значение для доступности интерфейсов

Разделение способов взаимодействия позволяет:

  • обеспечить корректную навигацию клавиатурой;
  • уменьшить визуальный шум для пользователей мыши;
  • создать адаптивные интерфейсы для различных устройств;
  • соблюдать рекомендации WCAG по фокусной индикации.

Условные стили на основе источника фокуса являются важным элементом построения доступных пользовательских интерфейсов и широко применяются в современных фронтенд-архитектурах.