Привет, Хабр.
Я — Senior Product Designer в проекте GlueLeague, зарубежной беттинг-платформе, сфокусированной на live-сегменте. В этом посте расскажу, как мы подошли к проектированию быстрого и предсказуемого интерфейса, в котором пользователи могут делать ставки в режиме реального времени, не теряя ориентир в хаосе обновляющихся коэффициентов. Под "мгновенным откликом" мы подразумевали не только скорость анимации или рендеринга, но и общее ощущение контроля и стабильности, особенно при высокочастотных обновлениях.
Контекст и вызов
Live-раздел в букмекерской платформе — это среда с максимальной плотностью изменений. Пользователь наблюдает за коэффициентами, которые обновляются каждые 200–500 мс, и принимает решение на основе динамичного контекста. Именно в этом сценарии критически важно минимизировать когнитивную нагрузку: элементы не должны смещаться, данные — исчезать, а действия — блокироваться. UX в live-ставках — это UX под давлением. Поэтому мы сформулировали следующие задачи:
Обеспечить стабильность layout при частых обновлениях данных;
Исключить неявные изменения интерфейса без визуального подтверждения;
Обеспечить предсказуемость клика: пользователь должен быть уверен, что он сделал ставку именно по тому коэффициенту, который видел.
Исходная архитектура и проблемы
В начальной версии интерфейс был построен на React с использованием SSR-рендеринга и периодического polling'а (1 раз в 3 секунды) для обновления коэффициентов. Это приводило к нескольким проблемам:
Полное обновление DOM при получении новых данных приводило к "прыжкам" карточек матчей;
Коэффициенты визуально менялись под курсором, из-за чего пользователи часто кликали не туда;
Не было различия между "успешной ставкой" и "ошибкой ставки": UI выглядел одинаково в обоих случаях.
Всё это приводило к фрустрации, высоким показателям rage click и росту количества обращений в поддержку.
Новый подход: реактивность и слой взаимодействия
В новой версии мы перешли к полной реактивной архитектуре на базе WebSocket-подписок. Каждая ставка, каждый коэффициент — это изолированный компонент, подписанный на конкретные события от сервера. Мы использовали Redux Toolkit с RTK Query для кэширования и нормализации данных. Обновления шли в батчах, агрегированных на бэкенде.
Для предотвращения визуальных скачков:
Визуальное обновление коэффициентов происходит через throttle (250–300 мс);
При изменении значения применяется анимация фоновой заливки (
greenFade
,redFade
), но геометрия и положение DOM-узлов остаются фиксированными;Обновления происходят через
requestAnimationFrame
и перерисовываются строго в пределах одного компонента, без каскадных перерисовок вверх по дереву.
UX-паттерны и принципы стабильности
Мы переосмыслили не только техническую сторону, но и UX-паттерны. Каждый компонент интерфейса, связанный с live-ставками, проектировался по принципу "максимальной визуальной инерции": если пользователь взглянул на экран, он должен в течение хотя бы секунды видеть ту же структуру.
Все блоки — карточки матчей, маркеты, кнопки ставок — имеют фиксированную высоту и padding;
Кнопки не исчезают: даже при блокировке ставки остаётся индикация "была попытка";
Обновление коэффициента никогда не приводит к визуальному сдвигу layout;
Sticky-бар с ключевыми действиями всегда закреплён внизу экрана и не перекрывается системными событиями.
Отдельный фокус на мобильный UX
Около 70% live-трафика — с мобильных устройств. Поэтому мы внедрили несколько решений, адаптированных под ограничения и сценарии смартфонов:
Свайпы влево/вправо переключают матч, свайп вверх — раскрывает маркеты;
Все активные действия доступны в зоне большого пальца (bottom-oriented CTA);
Кнопка ставки имеет увеличенную зону касания и блокируется на 800 мс после подтверждения действия;
Анимации упрощены до fade/scale, чтобы избежать перегрузки GPU при постоянных обновлениях.
Подтверждение действий и UX-ошибки
Ключевая UX-проблема — это отсутствие обратной связи в моменты, когда пользователь ожидает чёткой реакции от системы. Мы сосредоточились на проработке сценариев, где легко возникает неуверенность: изменился ли коэффициент, сработала ли ставка, произошла ли ошибка. Ниже — подробнее о внедрённых решениях.
При клике на коэффициент система сразу фиксирует намерение пользователя и отображает модальное окно. В нём отображается выбранный коэффициент, статус соединения и расчётное время ответа от сервера. Это помогает визуально зафиксировать, что ставка действительно в процессе и данные не "убежали".
Если коэффициент изменился между тем, как пользователь нажал кнопку и система попыталась отправить ставку, интерфейс не отменяет silently, а показывает всплывающее уведомление: "Коэффициент изменился. Подтвердите ставку повторно". Это диалоговое окно содержит как старое, так и новое значение, плюс кнопки выбора: согласиться на изменение или отказаться.
В случае, если сервер возвращает ошибку (например, ставка не принята, событие уже завершилось или превышен лимит), UI переключает карточку ставки в состояние "Ошибка". Отображается пояснение, включая тип ошибки (например, "истекшее событие"), и предлагается действие: обновить матч, выбрать другой исход или вернуться к списку событий. Это снижает фрустрацию и поддерживает ощущение управляемости происходящего.
Цифры и результат
После запуска новой версии мы собрали метрики с использованием собственного события ux:bet:attempt
. Вот, что получилось:
Метрика |
До редизайна |
После |
---|---|---|
Rage clicks на live-экранах |
12.4% |
3.1% |
Успешные ставки после 1 клика |
71% |
91% |
Средняя скорость выбора ставки |
1.9 сек |
1.2 сек |
Оценка удобства (по опросу) |
3.7/5 |
4.6/5 |
Оценка платформы GlueLeague с позиции дизайна
Плюсы:
Гибкая архитектура. Фронт-энд и бэкенд работают через событийную модель. Все ключевые взаимодействия реализованы по принципу pub/sub поверх WebSocket и внутреннего события-роутера. Благодаря этому, каждая сторона может независимо выкатывать фичи, минимизируя риски. Документация по событиям ведётся через Swagger, а для удобства тестирования используется собственный Viewer.
Открытая коммуникация в команде. Мы используем связку Notion + Figma + Linear, где дизайнеры могут документировать UX-гипотезы, разработчики — сразу же брать их в работу и выкатывать на отдельные инстансы. Все процессы прозрачные, нет вертикального контроля — никто не блокирует улучшения ради формального «одобрения».
Поддержка UX-инициатив. UX-решения инициируются самими участниками команды. Например, freeze-механизм при изменении коэффициента полностью разработан по инициативе дизайна и поддержан без сопротивления. В команде есть доверие к экспертизе, а не борьба за приоритеты.
Минусы:
Отсутствие централизованной дизайн-системы. У нас нет общей библиотеки компонентов или системной стилизации (design tokens). Каждый продукт решает UI-задачи своими силами, что порождает дублирование, расхождение в поведении и лишние баги при масштабировании или рефакторинге.
Слабая типизация событий. Форматы WebSocket-сообщений не унифицированы и часто меняются. Из-за отсутствия общего типа-описания приходится использовать runtime-валидацию (через zod/ajv), что добавляет рисков и снижает надёжность при CI-деплое.
Устаревшая админка. Инструмент для внутренних операций не позволяет тестировать пограничные UX-кейсы. Например, невозможно быстро выставить нестандартный коэффициент или смоделировать задержку отклика. Это замедляет сценарное тестирование и требует обходных решений через backend-инженеров.
Вывод
Проектирование live-интерфейса — это не про «быстро и красиво». Это про создание среды, в которой интерфейс не мешает. Где пользователь не тратит лишние секунды, не пересматривает, не переспрашивает. Где он чувствует уверенность в моменте. И да, мы всё ещё улучшаем эту систему — но уже сейчас могу сказать: она действительно работает под нагрузкой и приносит пользователям то, чего им не хватает в других БК — контроль и предсказуемость.