Посетитель нажимает «Купить», ничего видимо не происходит — и он жмёт ещё раз, и ещё. В корзине оказываются три одинаковых товара, а в 1С — задвоенный заказ, который менеджер потом вычищает вручную. Причина банальна: кнопка не показала, что клик засчитан и запрос уже выполняется. Состояния интерактивных элементов — это не украшение, а язык, на котором интерфейс разговаривает с пользователем.
В этой статье разберём, как грамотно проектировать состояния кнопок и элементов в интернет-магазине на 1С-Битрикс: hover, active, focus, disabled и loading. Покажем, где эти состояния завязаны на AJAX-запросы и обмен с учётной системой, и почему их стоит закладывать вместе с общей оптимизацией витрины и 1С, а не докручивать в последний момент.
Коротко
- У кнопки пять состояний: обычное, hover, active, focus, disabled — плюс loading для асинхронных действий.
- Hover недоступен на мобильных и клавиатуре — обязательно стилизуйте focus-visible отдельно.
- Loading защищает от повторных кликов при добавлении в корзину и оформлении заказа.
- Снимайте состояние загрузки и на успехе, и на ошибке, иначе кнопка «залипнет» в спиннере.
Почему состояния кнопок — это про деньги
Кнопки «Купить» и «Оформить заказ» — это точки, где посетитель превращается в покупателя, и всё вокруг клика по ним влияет на конверсию. Если человек не понимает, что кнопка кликабельна, он её не нажмёт. Если после клика ничего не меняется, он подумает, что сайт сломался. Если кнопка позволяет нажать себя дважды, магазин получает дубли заказов и лишнюю работу менеджерам.
Состояния решают все три проблемы. Hover и focus говорят «на меня можно нажать», active даёт ощущение нажатия, loading сообщает «работаю, подожди», disabled честно предупреждает «сейчас нельзя». Вместе они складываются в ощущение, что интерфейс живой и надёжный.
Пять базовых состояний интерактивного элемента
Прежде чем стилизовать, полезно держать в голове полную карту состояний. У любого интерактивного элемента их как минимум пять, а для асинхронных действий добавляется шестое.
| Состояние | Когда возникает | Что сообщает |
|---|---|---|
| Обычное (default) | По умолчанию | Элемент доступен |
| Hover | Наведение мыши | На это можно нажать |
| Active | Момент нажатия | Клик засчитан |
| Focus | Переход клавиатурой | Элемент выбран |
| Disabled | Действие недоступно | Сейчас нельзя |
| Loading | Ожидание ответа | Идёт обработка |
Проектируя кнопку, стоит сразу отрисовать все её состояния, а не только «как выглядит по умолчанию». Тогда разработчик не будет додумывать hover на глаз, а тестировщик проверит каждое из них по списку.
Hover: подсказка о кликабельности
Hover — самое привычное состояние: при наведении курсора кнопка слегка меняет фон, тень или подчёркивание. Задача hover — подтвердить, что перед пользователем именно кликабельный элемент, а не статичный текст. Здесь важна умеренность: резкий скачок цвета или размера раздражает, а плавный переход в 150–200 мс воспринимается естественно.
- Меняйте заметно, но не агрессивно. Достаточно сдвига фона на один тон или появления тени.
- Используйте плавный transition. Резкое переключение выглядит дёшево и дёргано.
- Не завязывайте на hover ничего критичного. Всё, что доступно только при наведении, недоступно на мобильных.
Главная ошибка — считать hover универсальным. На смартфонах и планшетах наведения нет: палец либо не касается экрана, либо сразу нажимает. Поэтому hover — приятное дополнение, а не единственный способ показать интерактивность.
Active: подтверждение нажатия
Состояние active длится доли секунды — пока кнопка нажата. Его часто недооценивают, а зря: именно active даёт ощущение «физического» отклика. Лёгкое «вдавливание» кнопки (сдвиг на 1 пиксель вниз, уменьшение тени, потемнение фона) подтверждает, что палец или курсор попали точно и клик произошёл.
Без active-состояния на медленном соединении возникает неприятный эффект: пользователь нажал, но пока идёт запрос, кнопка выглядит нетронутой, и человек не уверен, сработал ли клик. Active закрывает этот зазор между нажатием и появлением loading, давая мгновенную реакцию ещё до ответа сервера.
Focus и доступность с клавиатуры
Focus возникает, когда на элемент переходят клавишей Tab или программно. Для пользователей клавиатуры и скринридеров focus — единственный ориентир, где они сейчас находятся на странице. Убрать обводку фокуса ради «чистоты дизайна» — распространённая и вредная ошибка: сайт становится непроходимым без мыши.
Современный подход — стилизовать :focus-visible: браузер показывает заметное кольцо фокуса при навигации клавиатурой и не показывает его при обычном клике мышью. Так дизайн остаётся аккуратным, а доступность сохраняется. Кольцо фокуса должно быть контрастным и не сливаться с фоном кнопки.
outline: none без замены. Если убираете стандартную обводку, обязательно добавьте собственный видимый индикатор фокуса — иначе теряете часть аудитории и проваливаете доступность.
Disabled: как блокировать без вреда
Disabled сообщает, что действие сейчас недоступно: не заполнены обязательные поля, товара нет в наличии, идёт другая операция. Визуально такую кнопку приглушают — снижают контраст, убирают hover, меняют курсор на not-allowed. Но одного серого цвета мало: пользователь должен понимать, почему кнопка недоступна и что сделать, чтобы её разблокировать.
- Объясняйте причину. Рядом с disabled-кнопкой или в подсказке — «Заполните поле телефон», «Нет в наличии».
- Не прячьте кнопку. Исчезающая кнопка сбивает с толку сильнее, чем видимая, но заблокированная.
- Держите контраст читаемым. Приглушённый текст всё равно должен оставаться различимым.
Отдельный нюанс — доступность: атрибут disabled убирает элемент из последовательности фокуса, и скринридер о нём не сообщит. Если важно, чтобы пользователь знал о недоступной кнопке, применяют aria-disabled с пояснением.
Loading: состояние ожидания ответа
Loading — самое важное состояние для интернет-магазина, потому что почти все ключевые действия асинхронны. Добавление в корзину, применение купона, оформление заказа, подгрузка товаров — всё это отправляет запрос на сервер, ответ на который приходит не мгновенно. На это время кнопка должна перейти в состояние загрузки.
Хорошее loading-состояние делает три вещи одновременно: показывает индикатор (спиннер или анимацию), меняет текст на осмысленный («Добавляем…», «Оформляем…») и блокирует повторный клик. Последнее критично: именно блокировка защищает от дублей. При этом важно не «замораживать» весь интерфейс, а показывать загрузку локально — на самой кнопке, которую нажали.
Loading при AJAX и обмене с 1С
В 1С-Битрикс кнопки корзины и заказа работают через AJAX-компоненты, а часть операций упирается в обмен с учётной системой — проверку наличия, актуализацию цены, резервирование. Эти ответы приходят не всегда быстро, особенно если 1С в этот момент занята. Поэтому корректная обработка loading здесь — вопрос не только UX, но и надёжности.
- По клику включите состояние. Добавьте кнопке класс-состояние, поставьте
disabled, покажите спиннер и смените текст. - Отправьте запрос. AJAX к компоненту корзины или к вашему REST-эндпоинту.
- Обработайте успех. Снимите loading, покажите результат — «В корзине», обновите счётчик.
- Обработайте ошибку. Обязательно снимите loading и покажите понятное сообщение, если сервер не ответил.
- Используйте finally. Снятие состояния в общем блоке гарантирует, что кнопка не зависнет ни при каком исходе.
Скорость ответа сервера напрямую влияет на то, как долго пользователь видит спиннер. Если запросы к каталогу или обмену тормозят, страдает не только loading, но и вся витрина. Ускорять бэкенд системно помогает аудит и оптимизация 1С, а надёжность самих асинхронных эндпоинтов разбираем в материале про REST, вебхуки и безопасность в Битрикс.
Защита от двойного клика на заказе
Двойной клик по кнопке оформления заказа — одна из самых частых причин задвоенных заказов. Пользователь нажал, ответ задержался, он нажал снова — и создались два заказа с одинаковой корзиной. Дальше эти дубли попадают в 1С, менеджер тратит время на разбор, а клиент рискует получить двойное списание.
Защита проста и обязательна: на время отправки заказа кнопку переводят в disabled с индикатором загрузки и разблокируют только после ответа сервера. На бэкенде дополнительно защищаются идемпотентностью — например, проверкой, не создан ли уже заказ с этой корзиной за последние секунды. Клиентская блокировка снимает 90% проблемы, серверная закрывает остальное.
Состояния в компонентах 1С-Битрикс
Штатные компоненты Битрикса — catalog.element, корзина, оформление заказа — уже содержат базовую логику AJAX и какие-то состояния кнопок, но их оформление обычно минимально. Чтобы состояния выглядели и работали как надо, их кастомизируют в шаблоне компонента, не трогая ядро.
- Классы-состояния в шаблоне. Добавляйте
is-loading,is-addedи подобные в кастомном шаблоне компонента, а стили держите в CSS темы. - JS в template.js компонента. Логику включения и снятия состояний размещайте в скриптах шаблона, а не в глобальных файлах.
- Не правьте /bitrix/components. Копируйте компонент в свой шаблон и меняйте копию — обновления ядра не затрут вашу работу.
Такой подход — часть общей культуры разработки без правки ядра. Как выстраивать деплой кастомных шаблонов и переносить их между стендами, мы описали в статьях про CI/CD и деплой в Битрикс и про организацию инфраструктуры на BitrixVM.
Доступность и мобильные устройства
Состояния должны работать на всех устройствах и для всех пользователей, а не только для человека с мышью на десктопе. Каждый канал предъявляет свои требования.
- Мобильные. Hover не работает; полагайтесь на active и достаточный размер зоны нажатия (минимум 44×44 px).
- Клавиатура. Focus-visible обязателен, порядок обхода Tab логичен, по кнопке срабатывает Enter/Space.
- Скринридеры. Состояние loading и disabled сообщайте через ARIA (
aria-busy,aria-disabled), а не только визуально. - Контраст. Все состояния, включая disabled, должны оставаться различимыми при слабом зрении.
Доступность здесь не абстрактная «правильность», а расширение аудитории: чем больше людей могут без проблем нажать вашу кнопку, тем выше конверсия.
Частые ошибки
- Стилизован только hover. На мобильных и клавиатуре кнопка не даёт обратной связи.
- Убрана обводка фокуса. Сайт непроходим без мыши, проваливается доступность.
- Нет состояния loading. Пользователь жмёт повторно и создаёт дубли заказов.
- Loading не снимается при ошибке. Спиннер крутится вечно, если сервер не ответил.
- Disabled без объяснения. Серая кнопка непонятна: неясно, что сделать для разблокировки.
- Заморожен весь интерфейс. Вместо локального спиннера на кнопке блокируется вся страница.
- Состояния завязаны на правку ядра. При обновлении Битрикса кастом слетает.
Чек-лист внедрения
- Отрисованы все состояния. Default, hover, active, focus, disabled и loading для каждой ключевой кнопки.
- Focus-visible стилизован. Заметное контрастное кольцо фокуса вместо убранной обводки.
- Loading защищает от повтора. Кнопка блокируется на время AJAX-запроса и обмена с 1С.
- Ошибки обработаны. Состояние снимается в блоке finally, пользователю показывается сообщение.
- Заказ защищён от дублей. Блокировка кнопки на фронте плюс проверка на сервере.
- Кастом без правки ядра. Классы и JS живут в шаблоне компонента и темы.
- Проверено на мобильном и клавиатуре. Тесты на медленной сети, тачскрине и Tab-навигации.
Вывод
Состояния кнопок кажутся мелочью, но именно они превращают набор элементов в понятный, надёжный интерфейс. Hover и focus зовут нажать, active подтверждает клик, loading удерживает от повторов, disabled честно предупреждает о недоступности. В интернет-магазине на 1С-Битрикс эти состояния тесно связаны с AJAX и обменом с учётной системой, поэтому проектировать их нужно вместе с бэкендом, а не докручивать поверх.
Заложите полный набор состояний, обрабатывайте и успех, и ошибку, защищайте оформление заказа от двойного клика — и уберёте целый класс проблем: дубли заказов, ощущение «сломанного» сайта и потери на самых денежных кнопках. Дешёвая работа с высокой отдачей.