Кнопка выглядит отлично на макете. Но стоит навести курсор, нажать Tab или получить ошибку сервера - и интерфейс рассыпается: цвет не меняется, фокус невидим, заблокированный элемент выглядит как активный. Пользователь не понимает, что происходит, и либо кликает снова, либо уходит.
Большинство таких дыр появляются не из-за плохого вкуса, а из-за системного пропуска. Дизайнер прорабатывает дефолтный вид и забывает про остальные шесть-восемь вариантов поведения элемента. Разработчик реализует то, что видит на макете. В итоге продукт работает, но не до конца.
Эта статья - практический разбор каждого состояния интерактивного элемента: что оно означает, как его оформить, где чаще всего ошибаются и как проверить компонент перед передачей в разработку.
Коротко:
- Каждый интерактивный элемент имеет минимум 6-8 состояний: default, hover, focus, active, disabled, error, loading, success/selected.
- Пропуск даже одного из них создает визуальную или функциональную дыру - особенно заметную на клавиатурной навигации и в граничных сценариях.
- Hover сигнализирует о кликабельности, focus - о текущей позиции при навигации с клавиатуры, disabled - о недоступности без объяснения причины.
- Ошибка в error-состоянии - это коммуникация, а не просто красная рамка: важно где, что и почему.
- В Figma каждое состояние должно быть отдельным вариантом компонента, а не отдельным фреймом.
- Перед хендофом проверяй компонент по чеклисту - это экономит время на правках после разработки.
Почему состояния ломаются чаще, чем кажется
Дизайнер работает в статичном инструменте. Figma показывает один момент времени, и соблазн нарисовать «красивый экран» сильнее, чем желание перебрать все сценарии взаимодействия. Джуны и мидлы часто сдают компонент с двумя вариантами: default и hover. Остальное «очевидно».
На практике очевидного не существует. Разработчик реализует ровно то, что видит. Если focus не нарисован - его не будет. Если disabled выглядит как default с opacity 0.5 - именно так и будет выглядеть в продукте, даже если это нечитаемо на темном фоне.
Второй источник проблем - несогласованность между компонентами. Кнопка меняет цвет при наведении, а карточка - нет, хотя тоже кликабельна. Поле ввода подсвечивает ошибку красным, а чекбокс - нет. Пользователь не может предсказать поведение интерфейса, и это разрушает доверие.
Полная карта состояний: что должно быть у каждого компонента
Набор состояний зависит от типа элемента, но базовый список выглядит так:
| Состояние | Когда активно | Что должно измениться визуально |
|---|---|---|
| Default | Элемент доступен, курсор не на нём | Базовый вид без изменений |
| Hover | Курсор наведён | Цвет фона, обводка, тень, курсор pointer |
| Focus | Элемент выбран с клавиатуры или тапом | Видимый контур (outline), никогда не убирать |
| Active / Pressed | Момент нажатия | Сдвиг, затемнение, scale вниз |
| Disabled | Действие недоступно | Пониженная непрозрачность, cursor not-allowed |
| Error | Валидация не прошла | Красная рамка, иконка, текст ошибки |
| Loading | Ожидание ответа сервера | Спиннер или skeleton, блокировка повторного клика |
| Success / Selected | Действие выполнено или элемент выбран | Зелёный акцент, галочка, изменение лейбла |
Не каждый компонент требует всех восьми. У статичного текстового поля не будет active в том же смысле, что у кнопки. У тоггла не будет error в базовом сценарии. Но прежде чем исключить состояние, стоит явно ответить: «это точно не нужно здесь» - а не просто забыть о нём.
Hover: сигнал о кликабельности
Наведение курсора - первый момент, когда пользователь понимает, можно ли с элементом взаимодействовать. Если ничего не меняется, человек не уверен: это кнопка или просто блок?
Хороший hover state дизайн строится на двух принципах: изменение должно быть заметным, но не кричащим, и оно должно быть консистентным для всех кликабельных элементов одного типа.
Типичные способы обозначить наведение:
- Изменение фона кнопки на 10-15% темнее или светлее
- Появление тени (box-shadow) у карточки
- Подчёркивание у ссылки
- Смена иконки или её цвета
- Лёгкое масштабирование (scale 1.02) для карточек
Главная ошибка - делать hover только для кнопок и игнорировать карточки, иконки, строки таблицы и другие кликабельные блоки. Если элемент реагирует на клик, он должен реагировать и на наведение.
Антипаттерн: убирать hover на мобильных устройствах «потому что там нет мыши» и забывать добавить его обратно для десктопной версии. Hover - это десктопный паттерн, но в адаптивном дизайне важно явно разделять поведение для touch и pointer устройств через CSS-медиазапросы @media (hover: hover).
Focus: состояние, которое чаще всего убивают
Focus state - это индикатор текущей позиции при навигации с клавиатуры. Он нужен не только людям с нарушениями моторики, но и всем, кто пользуется Tab, стрелками или скринридером.
Самая распространённая ошибка в focus state интерфейс - это outline: none в CSS. Разработчик убирает стандартный синий контур браузера, потому что он «некрасивый», и не заменяет его ничем. В результате пользователь с клавиатуры теряет ориентацию: непонятно, на каком элементе он находится.
Правила для focus:
- Контур должен быть виден на любом фоне - используй достаточный контраст (минимум 3:1 по WCAG 2.1)
- Толщина outline - минимум 2px, лучше 3px
- Не используй только цвет как единственный индикатор: добавь форму или тень
- Focus-ring должен отличаться от hover - это разные состояния с разным смыслом
- Для кастомных компонентов (дропдаун, модалка, слайдер) прописывай focus явно - браузер не сделает это автоматически
В Figma удобно использовать отдельный вариант компонента с именем «focus» и явным outline. Это помогает разработчику понять, что состояние существует и как оно должно выглядеть.
Active и Pressed: момент нажатия
Это самое короткое по времени состояние - оно длится пока кнопка зажата. Но без него интерфейс кажется «мёртвым»: пользователь нажимает и не понимает, сработало ли.
Для кнопок active обычно означает:
- Небольшой сдвиг вниз (translateY 1-2px) - имитация физического нажатия
- Более тёмный фон по сравнению с hover
- Уменьшение тени или её исчезновение
На мобильных устройствах это состояние особенно важно, потому что у пользователя нет курсора и hover недоступен. Тактильный отклик через визуальное изменение - единственный способ подтвердить касание.
Disabled: недоступность с объяснением
Disabled состояние UX - одна из самых недооценённых тем. Дизайнеры часто ставят opacity 0.5 и считают задачу выполненной. Но у заблокированного элемента есть несколько проблем, которые нужно решить заранее.
Проблема 1: пользователь не понимает, почему элемент недоступен. Кнопка «Отправить» серая - почему? Не заполнено поле? Нет прав? Нужно подождать? Без подсказки человек будет кликать снова и снова или уйдёт.
Проблема 2: disabled элемент может быть нечитаемым. Текст на кнопке с opacity 0.5 на светлом фоне теряет контраст. По WCAG декоративные неактивные элементы освобождены от требований контраста, но если на кнопке есть текст с информацией - пользователь должен его прочитать.
Проблема 3: disabled и read-only - разные вещи. Read-only поле показывает значение, но не даёт его изменить. Disabled поле вообще исключается из формы и не отправляется с данными. Визуально они могут выглядеть похоже, но семантически и функционально - нет.
Пример: кнопка «Продолжить» в форме регистрации заблокирована, пока не заполнены обязательные поля. Вместо того чтобы просто делать её серой, добавь тултип при наведении: «Заполните email и пароль». Это снижает фрустрацию и объясняет логику.
Альтернативный подход - не делать кнопку disabled вообще, а показывать ошибки валидации при попытке отправить незаполненную форму. Это спорный момент в UX-сообществе, но он работает лучше в сценариях, где пользователь не понимает, что именно нужно исправить.
Error: красная рамка - это не решение
Ошибка в интерфейсе - это момент коммуникации. Пользователь сделал что-то не так или система не смогла выполнить действие. Задача дизайнера - объяснить что случилось, где проблема и как её исправить.
Типичный антипаттерн: красная рамка вокруг поля без текста. Пользователь видит, что что-то не так, но не знает что именно. Особенно болезненно это в длинных формах, где несколько полей подсвечены сразу.
Правила для error-состояния:
- Всегда добавляй текст ошибки под полем - конкретный, не «Ошибка ввода»
- Используй иконку вместе с цветом - не полагайся только на красный (дальтоники)
- Сохраняй введённое значение - не очищай поле при ошибке
- Показывай ошибку рядом с полем, а не только в шапке формы
- Для серверных ошибок (недоступность API, конфликт данных) используй отдельный паттерн - не тот же, что для валидации
Отдельный вопрос - когда показывать ошибку. Три подхода: при потере фокуса (blur), при отправке формы, или в реальном времени (live validation). Каждый подходит для разных сценариев. Подробнее об этом - в статье про валидацию форм, которая уже есть в блоге.
Loading: блокировка и обратная связь
Когда пользователь нажал кнопку и ждёт ответа сервера, интерфейс должен показать два вещи: действие выполняется, и повторный клик заблокирован.
Если кнопка остаётся активной во время загрузки, пользователь нажмёт ещё раз. Это дублирует запросы, может создать несколько одинаковых записей в базе и приводит к непредсказуемому поведению.
Для проектирования состояний кнопок в loading-режиме:
- Замени текст на спиннер или добавь спиннер рядом с текстом
- Заблокируй кнопку (disabled) на время запроса
- Если загрузка долгая (больше 3 секунд) - добавь прогресс или текст «Обрабатываем...»
- После завершения явно переходи в success или error - не возвращайся молча в default
Selected и Success: подтверждение действия
Selected - это состояние выбранного элемента в группе: таб, чекбокс, радиокнопка, фильтр. Success - подтверждение завершённого действия (форма отправлена, файл загружен, настройка сохранена).
Частая ошибка - не различать selected и hover визуально. Пользователь наводит курсор на уже выбранный таб и не понимает, выбран он или нет, потому что оба состояния выглядят одинаково.
Для success важно решить, как долго показывать подтверждение. Если кнопка «Сохранить» на секунду показывает галочку и возвращается в default - это нормально. Если форма исчезает и появляется пустой экран без объяснений - пользователь не уверен, что данные сохранились.
Как организовать состояния в Figma
UI states Figma - это не просто набор фреймов с разными видами кнопки. Правильная организация экономит время при обновлениях и делает хендоф понятным.
Базовые принципы:
- Каждое состояние - отдельный вариант (variant) внутри компонента, а не копия в другом месте файла
- Называй варианты консистентно: Default, Hover, Focus, Active, Disabled, Error, Loading, Success
- Используй component properties для переключения состояний - это позволяет разработчику видеть все варианты в одном месте
- Для интерактивных компонентов (кнопки, поля) настраивай Interactive Components с переходами между состояниями
- Документируй в аннотациях, какое состояние когда активируется - особенно для нестандартных случаев
Если в проекте есть дизайн-токены, привязывай цвета состояний к токенам, а не к hex-значениям напрямую. Так при смене темы или ребрендинге всё обновится автоматически.
Типичные ошибки при проработке состояний
| Ошибка | Последствие | Как исправить |
|---|---|---|
| Убрать outline у focus | Клавиатурная навигация невозможна | Заменить на кастомный focus-ring с контрастом 3:1+ |
| Disabled без объяснения причины | Пользователь не знает, что делать | Добавить тултип или подсказку рядом с элементом |
| Error только через цвет | Дальтоники не видят ошибку | Добавить иконку и текст ошибки |
| Кнопка активна во время загрузки | Дублирование запросов | Блокировать кнопку и показывать спиннер |
| Hover только у кнопок | Кликабельные карточки выглядят статично | Добавить hover для всех интерактивных элементов |
| Selected и hover выглядят одинаково | Пользователь не понимает текущий выбор | Разделить визуально: разные цвета или веса |
Чеклист перед хендофом
Прежде чем передавать компонент в разработку, пройдись по этому списку:
- Все восемь состояний нарисованы или явно исключены с обоснованием
- Focus-ring виден на всех фонах, контраст минимум 3:1
- Disabled элементы имеют подсказку о причине недоступности
- Error-состояние включает текст ошибки, иконку и не только цвет
- Loading блокирует повторный клик и показывает индикатор
- Hover добавлен для всех кликабельных элементов, не только кнопок
- Selected визуально отличается от hover
- Все варианты организованы как variants в Figma, а не разрозненные фреймы
- Цвета состояний привязаны к токенам или переменным
- Аннотации объясняют нестандартное поведение (например, когда показывать error - при blur или submit)