Взаимодействие и состояния: обратная связь, загрузка, ошибки, пустые экраны
В дизайн-системах мы собрали набор компонентов. Но компонент в библиотеке — это не то, что видит человек. Человек видит компонент в момент времени: пустым, наполовину загруженным, с ошибкой, заблокированным, с фокусом от клавиатуры. И вот тут выясняется неприятное: макет, который согласовали, показывает ровно одно из этих состояний — счастливый путь с идеальными данными.
Всё остальное всё равно окажется в проде. Просто нарисует его не дизайнер, а разработчик — в спешке, за пять минут до релиза, по своему вкусу. Дальше начинается «а почему тут спиннер посреди экрана», «а почему ошибка стёрла всё, что я ввёл», «а почему на пустом экране написано No data».
Эта статья — про то, как проектировать интерфейс как машину состояний, а не как набор красивых кадров. Разберём: как устроена петля обратной связи, какие интервалы времени человек воспринимает по-разному, чем скелетон отличается от спиннера и когда он вреден, как устроена нормальная ошибка, что должно быть на пустом экране, и главное — как всё это передать в разработку так, чтобы не спорить про два пикселя отступа.
1. Первый принцип: две пропасти Нормана
Дон Норман в The Design of Everyday Things описал взаимодействие человека с любой системой через две «пропасти» (gulfs):
- Пропасть исполнения (gulf of execution) — расстояние между «я хочу вот это» и «я знаю, на что нажать». Человек смотрит на экран и не понимает, каким действием добиться цели.
- Пропасть оценки (gulf of evaluation) — расстояние между «я нажал» и «я понял, что произошло». Система что-то сделала, но человек не может считать результат.
Практически весь дизайн взаимодействия — это сужение этих двух пропастей. Первая закрывается означающими (signifiers): подсказками, которые сообщают о возможности действия — подчёркиванием ссылки, тенью кнопки, курсором, надписью. Вторая закрывается обратной связью: любое действие должно иметь видимый отклик, и чем быстрее, тем лучше.
Норман отдельно предостерегает от путаницы: аффорданс — это объективное отношение между объектом и человеком (кнопку можно нажать), а означающее — воспринимаемый сигнал об этом отношении (кнопка выглядит нажимаемой). Плоский квадрат с текстом на сайте обладает аффордансом нажатия, но может не иметь ни одного означающего. Тогда человек его просто не увидит — и это не его вина.
Отсюда первая эвристика Якоба Нильсена — видимость состояния системы, самая нарушаемая из десяти. Пользователь всегда должен понимать, что происходит сейчас и что произошло в результате его действия. Не «через секунду поймёт», а сейчас.
Проверочный вопрос на любом ревью макета: «Пользователь нажал. Что изменилось на экране в течение 100 миллисекунд?» Если ответа нет — состояние не спроектировано.
2. Пять состояний экрана: UI Stack
Скотт Хёрфф предложил модель «стека интерфейса»: любой экран, который зависит от данных, существует минимум в пяти состояниях. Модель простая, но она закрывает большинство дыр.
Разберём, что обычно ломается.
Blank — это не «пусто», это доли секунды между открытием и запросом. Ошибка здесь — показать пустое состояние («У вас пока нет заказов») до того, как данные пришли. Человек видит «нет заказов», уходит, через 300 мс список появляется — но его уже никто не смотрит.
Partial — самое недооценённое. Дизайнер рисует таблицу с двенадцатью строками, потому что так красивее. Реальный новый пользователь видит одну строку в таблице высотой в экран, три уровня заголовков, панель фильтров и сортировку. Ощущение — «здесь что-то сломалось». Лечится тем, что макет рисуется минимум в трёх наполнениях: 0 объектов, 1 объект, 200 объектов.
Error — почти всегда рисуют один вариант, хотя ошибки принципиально разные (см. раздел 6).
Ideal — единственное состояние, которое обычно попадает в презентацию для заказчика. И именно оно реже всего встречается у реального пользователя в первый месяц.
3. Матрица состояний компонента
Уровнем ниже экрана то же самое происходит с каждым компонентом. У кнопки не одно состояние, а шесть-семь. У поля ввода — примерно столько же. Перемножьте на число вариантов (primary, secondary, destructive) — и получите десятки ячеек.
Комбинаторика здесь работает против вас: 6 типов элементов × 7 состояний = 42 ячейки, из которых в типичном макете нарисовано меньше половины. Хорошая новость — большинство ячеек закрываются один раз на уровне дизайн-системы, а не на каждом экране. Плохая — если в системе их нет, они не появятся сами.
Практический минимум, ниже которого спускаться нельзя:
| Состояние | Зачем | Частая ошибка |
|---|---|---|
default |
базовый вид | — |
hover |
сигнал «сюда можно нажать» | делают единственным признаком интерактивности; на тач-экране hover не существует |
focus-visible |
навигация с клавиатуры | убирают «чтобы не было синей рамки»; после этого продукт непроходим без мыши |
active / pressed |
подтверждение касания | пропускают на мобильных, где нет hover и это единственный отклик |
disabled |
действие сейчас недоступно | не объясняют причину; человек не понимает, что делать |
loading |
операция идёт | забывают заблокировать повторный клик — два заказа вместо одного |
error / invalid |
ввод не принят | помечают только цветом рамки — не видно дальтоникам и не читается скринридером |
read-only |
значение видно, но не меняется | путают с disabled: read-only должен оставаться в порядке табуляции |
Отдельно про disabled. Заблокированная кнопка — почти всегда плохое решение по трём причинам: она обычно вылетает из порядка фокуса, она не объясняет причину блокировки, и она заставляет человека угадывать, чего не хватает. Дизайн-система GOV.UK прямо не рекомендует блокировать кнопку отправки формы: лучше оставить её активной, а на нажатие показать, что именно не заполнено. Исключение — операции с необратимыми последствиями и случаи, когда причина очевидна и написана рядом.
4. Время: почему 100 мс, 1 с и 10 с — разные миры
Три границы восприятия задержки известны с 1968 года (Роберт Миллер), уточнены Стюартом Кардом в 1991-м и с тех пор повторяются в каждом руководстве. Это редкий случай, когда за цифрами стоят измерения, а не вкус.
- До 100 мс — отклик воспринимается как мгновенный. Человек ощущает прямую манипуляцию: не «я дал команду», а «я подвинул объект». Никаких индикаторов, они здесь только мешают.
- 100 мс – 1 с — задержка заметна, но ход мысли не прерывается. Достаточно локального отклика внутри самого элемента: кнопка «утонула», строка подсветилась.
- 1 – 10 с — внимание начинает уходить. Нужен явный индикатор, причём в том месте, где появится результат, а не в центре экрана.
- Больше 10 с — человек переключается на другую задачу. Нужен прогресс с процентами, оценка оставшегося времени, возможность отменить и уведомление о завершении. Обещание «скоро будет» без цифр здесь не работает.
Отдельно — порог Догерти: в исследовании IBM (Догерти и Тадани, 1982) сокращение времени отклика системы ниже примерно 400 мс давало непропорционально большой рост производительности оператора: люди начинали работать быстрее, чем предсказывала экономия ожидания. Это одна из причин, почему борьба за 200 мс вместо 600 мс имеет смысл даже там, где «и так вроде быстро».
Два правила, которые важнее выбора самой картинки индикатора:
// Хук: показать индикатор не сразу и не короче минимума.
// delay — сколько молчим, надеясь, что ответ придёт раньше
// minVisible — сколько держим индикатор, если уже показали
function useDelayedPending(pending: boolean, delay = 350, minVisible = 500) {
const [visible, setVisible] = useState(false);
const shownAt = useRef<number | null>(null);
useEffect(() => {
let timer: ReturnType<typeof setTimeout>;
if (pending) {
// операция началась — ждём delay перед показом
timer = setTimeout(() => {
shownAt.current = Date.now();
setVisible(true);
}, delay);
} else if (visible) {
// операция кончилась, но индикатор уже виден:
// досиживаем оставшееся время, чтобы он не моргнул
const left = minVisible - (Date.now() - (shownAt.current ?? 0));
timer = setTimeout(() => {
shownAt.current = null;
setVisible(false);
}, Math.max(0, left));
}
return () => clearTimeout(timer);
}, [pending, visible, delay, minVisible]);
return visible;
}
Почему это важно. Если 80% запросов возвращаются за 200 мс, а индикатор показывается сразу, пользователь видит вспышку длиной в кадр. Мозг читает вспышку не как «быстро», а как «что-то дёрнулось» — субъективно интерфейс становится менее стабильным, хотя объективно быстрее. Это стандартный приём в зрелых продуктах и в библиотеках вроде React Router и TanStack Query, где такая задержка встроена.
Про то, как реально ускорить ответ (а не только его показ), — трек Frontend и общий трек Производительность.
5. Загрузка: спиннер, скелетон, прогресс, оптимизм
Четыре разных инструмента под четыре разные ситуации. Выбирать надо не по красоте, а по тому, что известно системе.
Спиннер сообщает ровно один бит: «система жива». Он не сообщает, сколько ждать. Крутящийся кружок в центре пустого экрана — худший из массово применяемых паттернов: он стирает контекст, не даёт оценки и не мешает человеку решить, что всё зависло. Если спиннер всё же нужен — ставьте его в зону, где появится результат, и подписывайте: «Ищем рейсы», а не безымянный кружок.
Скелетон (серые плейсхолдеры формы будущего контента) выигрывает не потому, что «выглядит быстрее» — тут маркетинговых обещаний больше, чем данных. Его настоящие преимущества измеримы и приземлённы: он сохраняет раскладку (нет прыжка контента, лучше CLS), сообщает форму будущего результата и не создаёт ощущения модального ожидания. Вред скелетона тоже реален: если данные приходят за 150 мс, он превращается в мигание; если структура результата неизвестна (список может быть пустым), он врёт о будущем; и если скелетон анимирован «переливом», на длинных ожиданиях это утомляет.
Детерминированный прогресс — единственный вариант, где есть исследовательская база по форме анимации. Харрисон и коллеги (UIST 2007, CHI 2010) экспериментально показали: при одинаковой реальной длительности прогресс-бары с ускоряющимся к концу движением воспринимаются как более короткие, а с замедляющимся — как более длинные, разница достигает нескольких процентов субъективного времени. Практический вывод скромный: не делайте прогресс, который «застревает» на 90%, — это ровно тот случай, который воспринимается хуже честного линейного.
Оптимистичное обновление — самый сильный приём: интерфейс меняется мгновенно, как будто запрос уже прошёл, а сеть догоняет в фоне. Так работают лайки, чекбоксы в списке дел, перетаскивание карточек. Цена — надо спроектировать откат.
подтверждение уже было else Сеть недоступна S--xUI: timeout UI-->>U: чекбокс возвращается + плашка «Не сохранено. Повторить» Note over UI,S: повтор с той же идемпотентной операцией,
ввод пользователя не потерян else Конфликт — задачу уже удалили S-->>UI: 409 Conflict UI-->>U: строка исчезает + «Задача удалена другим участником» end
Ключевое правило оптимизма: применяйте его только там, где вероятность отказа мала, а откат дёшев и понятен. Оптимистично «оплатить заказ» нельзя — цена ошибочного подтверждения слишком высока. Оптимистично «поставить лайк» — можно: откат стоит одного кадра анимации. Механика реализации разобрана во Frontend: загрузка данных.
6. Ошибки: таксономия важнее формулировки
Самая частая ошибка в работе с ошибками — считать их одним состоянием. На деле у них разная причина, разный адресат и разное место показа.
Разное происхождение требует разного места показа:
| Тип | Где показывать | Что обязательно |
|---|---|---|
| Ошибка одного поля | под полем, рядом с причиной | что не так + как исправить |
| Ошибки формы при отправке | сводка сверху + якоря к полям + фокус на сводку | сколько ошибок и ссылки на них |
| Отказ фоновой операции | неблокирующая плашка в зоне операции | действие «повторить» |
| Отказ всей страницы | экран целиком | что случилось, что делать, куда уйти |
| Успешное действие с последствиями | тост с «Отменить» | окно отмены 5–10 секунд |
Три правила, которые дают больше пользы, чем любые формулировки:
1. Никогда не теряйте ввод. Ошибка сервера, разлогин, обновление страницы — введённые данные должны остаться. Форма, которая после 500-й ошибки показывает пустые поля, — это не техническая проблема, а проектная: состояние «ошибка» нарисовали без данных пользователя.
2. Ошибка обязана содержать следующий шаг. «Что-то пошло не так» не содержит ничего. Рабочая структура: что произошло → почему → что сделать. «Не удалось сохранить: нет связи с сервером. Черновик сохранён локально, попробуйте ещё раз через минуту.» Подробно про формулировки — в следующей статье про текст в интерфейсе.
3. Отмена лучше подтверждения. Аза Раскин в классической статье «Never Use a Warning When you Mean Undo» формулирует: диалог «Вы уверены?» перекладывает работу на пользователя и почти мгновенно перестаёт читаться — человек кликает «Да» рефлекторно. Если действие обратимо, выполняйте его сразу и дайте «Отменить» на 5–10 секунд (так работает отправка письма в Gmail). Подтверждение оставьте для действительно необратимого — и тогда требуйте осмысленного действия: ввести имя удаляемого проекта, а не нажать «ОК».
Про безопасное поведение при повторах — идемпотентность, ключи запросов, защита от двойного клика — смотрите Распределённые системы; на стороне интерфейса минимум такой: кнопка блокируется на время запроса и повтор отправляет ту же операцию, а не новую.
Разбор: почему эта форма неудобна
Реальный паттерн, который встречается постоянно. Форма регистрации: email, пароль, телефон, промокод. Что в ней не так:
- Валидация на каждый символ. Человек напечатал
aв поле email — под полем немедленно красное «Некорректный адрес». Он ещё не закончил. Правильный момент: молчать до потери фокуса, а после первой показанной ошибки перепроверять на каждый символ, чтобы человек видел, когда починил. - Обязательные поля не помечены, помечены необязательные — или наоборот, звёздочки везде. Если обязательны 9 полей из 10, помечайте одно необязательное словом «необязательно», а не восемь остальных звёздочкой.
- Плейсхолдер вместо подписи. Человек начал вводить — подпись исчезла. При проверке перед отправкой он не помнит, что в каком поле, а скринридер не всегда прочитает placeholder как имя поля.
- Требования к паролю показаны только в тексте ошибки. Их надо показывать до ввода и отмечать выполненными по мере набора.
- Ошибки показываются только сверху, списком, без ссылок. На длинной форме человек не находит проблемное поле. Нужны якоря и перенос фокуса.
- Промокод сверху. Поле для промокода в начале формы отправляет часть людей искать промокод в другой вкладке — и они не возвращаются.
- Кнопка блокируется до полной валидности. Человек не понимает, чего не хватает: неактивная кнопка не объясняет причину.
Техническая сторона всего перечисленного — Frontend: формы и валидация. Здесь важно другое: все семь пунктов — это решения о состояниях, а не о вёрстке. Их принимают на этапе дизайна или не принимают вовсе.
Разбор: почему пользователь не нашёл кнопку
Второй классический случай с юзабилити-теста: функция есть, но её не находят. Причины по частоте:
- Нет означающего. Элемент интерактивен, но выглядит как текст: нет подчёркивания, рамки, изменения курсора. Особенно часто — «плоские» кнопки без границ на пёстром фоне.
- Ниже линии сгиба, а страница не выглядит прокручиваемой. Экран заканчивается ровно на границе блока — визуальный сигнал «здесь всё». Лечится тем, что следующий блок должен быть частично виден.
- Слепота к баннерам. Кнопка стоит в правой колонке, оформлена ярко и с иконкой — то есть похожа на рекламу. Люди систематически не смотрят в зоны, которые выглядят как реклама.
- Не то слово. Кнопка называется термином из внутреннего языка компании. Человек ищет «Скачать», на кнопке — «Экспорт выгрузки».
- Конкуренция акцентов. На экране четыре одинаково ярких кнопки. Когда акцентировано всё, не акцентировано ничего — про иерархию подробно в основах визуального дизайна.
- Слишком далеко от контекста задачи. Кнопка «Добавить участника» находится в настройках проекта, а ищут её на экране списка участников. Это вопрос информационной архитектуры и пользовательских потоков.
- Слишком маленькая цель. Иконка 16×16 без увеличенной области нажатия — по закону Фиттса время наведения растёт с уменьшением цели и увеличением расстояния. На тач-экране это ещё и просто промахи.
Порядок диагностики: сначала проверьте формулировку и место (это чинится дёшево и даёт больше всего), потом контраст и размер, и только потом трогайте оформление.
7. Движение: когда оно объясняет, а когда развлекает
Анимация в интерфейсе оправдана, когда выполняет одну из трёх работ:
- Сохраняет непрерывность. Элемент не исчезает и не появляется, а перемещается — человек не теряет объект из виду и не перестраивает ментальную карту экрана.
- Показывает причинность. Панель выезжает из кнопки, которую нажали, — связь «нажал здесь, появилось это» считывается без слов.
- Направляет внимание. Появившееся сообщение об ошибке слегка сдвигается — глаз находит его быстрее.
Всё остальное движение — накладные расходы. Ориентиры по длительности (это уже отчасти конвенция, а не измерение): 100–150 мс для мелких элементов вроде hover и переключателей, 200–300 мс для панелей и модальных окон, до 400 мс для полноэкранных переходов. Дольше 400 мс интерфейс начинает ощущаться медленным, даже если данные уже пришли. Easing: вход быстрый с плавным замедлением, выход быстрее входа — уходящий объект не должен задерживать.
Обязательная часть, которую пропускают: уважение к prefers-reduced-motion. Для части людей вестибулярные расстройства делают параллакс, зумы и большие сдвиги причиной физического головокружения и тошноты. Это не гипербола и не редкость.
/* Базовое поведение: движение есть */
.panel {
transition: transform 240ms cubic-bezier(0.2, 0, 0, 1), opacity 160ms linear;
}
/* Системная настройка «уменьшить движение» — убираем перемещение,
но НЕ убираем обратную связь: замена появлением через прозрачность */
@media (prefers-reduced-motion: reduce) {
.panel {
transition: opacity 120ms linear;
transform: none;
}
}
Важный нюанс: «уменьшенное движение» не означает «никакой обратной связи». Заменяйте перемещение изменением прозрачности или мгновенной сменой состояния — но состояние должно оставаться видимым.
8. Доступность на этапе дизайна: минимум, который решается в макете
Полностью тема раскрыта в отдельном треке Доступность и в статье трека Доступность на этапе дизайна. Здесь — только то, что относится к состояниям и решается именно в макете, а не в коде.
- Состояние никогда не передаётся одним цветом. Ошибка — это рамка и иконка и текст. Выбранный элемент — заливка и галочка. Иначе состояние исчезает для людей с нарушением цветовосприятия и в чёрно-белой печати.
- Индикатор фокуса нарисован явно. Не «браузерный дефолт как-нибудь справится»: у своих компонентов на тёмных или цветных фонах системное кольцо часто сливается. По WCAG 2.2 индикатор должен иметь контраст не ниже 3:1 к соседнему фону и не быть перекрыт липкими шапками.
- Порядок фокуса совпадает с визуальным порядком. Если в макете колонка справа читается первой, а в разметке она идёт последней — на клавиатуре получится хаос. Это решается на схеме, а не в CSS.
- Асинхронные изменения объявляются. Появился тост, обновился счётчик результатов, пришла ошибка — незрячий пользователь ничего не увидит. В спецификации состояния должно быть написано: «сообщение объявляется вежливо», «ошибка объявляется настойчиво». Технически это
aria-live, разобрано в Скринридеры. - Размер цели. WCAG 2.2 требует минимум 24×24 CSS-пикселя (уровень AA), практические рекомендации мобильных платформ — 44 pt для iOS и 48 dp для Android. Область нажатия может быть больше видимой иконки — это нормально и правильно.
- Никаких состояний только по hover. Информация, доступная лишь при наведении, недоступна на тач-экране и с клавиатуры.
9. Совместная работа с разработкой: спецификация вместо картинок
Здесь ломается больше проектов, чем на всех предыдущих этапах вместе. Сформулируем прямо: макет — это не задание на разработку. Задание — это спецификация поведения.
Почему «пиксель в пиксель» — плохая цель
Требование точного совпадения реализации с макетом кажется профессиональным, но на практике вредно:
- Макет статичен, интерфейс — нет. У макета одна ширина, у продукта — все ширины от 320 до 2560. Совпасть можно только в одной точке; остальные точки в макете просто не нарисованы, и требование пиксель-перфекта ничего о них не говорит.
- Текст меняется. Локализация удлиняет строки, реальные имена длиннее «Иван И.», числа бывают семизначными. Пиксель-перфект по макету с идеальным текстом ломается на первом же реальном пользователе.
- Платформа имеет право на своё. Нативный
selectна iOS, системный шрифт, настройка размера текста в ОС, режим высокой контрастности — попытка воспроизвести макет точно ломает это всё. - Он смещает разговор. Ревью превращается в спор про 2 пикселя отступа вместо вопроса «что происходит, когда список пустой».
Что ставить вместо: соответствие системе и поведению. Отступы взяты из шкалы токенов, цвета — из палитры, размер шрифта — из типографической шкалы, все состояния реализованы, поведение при пустых/длинных/ошибочных данных совпадает со спецификацией. Отклонение на 2 пикселя, но из шкалы, — не дефект. Отсутствующее состояние focus-visible — дефект, даже если экран выглядит идентично макету.
Как выглядит нормальная передача
смотрят на поток вместе] A2[Проговариваются ограничения:
какие данные вообще есть,
что стоит дорого, что уже в системе] end subgraph B[Во время] B1[Дизайн собирается из компонентов
дизайн-системы, а не рисуется заново] B2[Промежуточный показ на середине:
дешевле переделать сейчас] end subgraph C[Передача] C1[Макет + таблица состояний
+ правила адаптива + тексты] C2[Живой разговор 20 минут,
а не ссылка в трекере] C3[Открытые вопросы записаны
и у каждого есть владелец] end subgraph D[После] D1[Дизайн-ревью на реальном билде,
а не на скриншоте] D2[Замечания разделены:
дефект / отличие / улучшение] D3[Найденные дырки возвращаются
в дизайн-систему, а не чинятся на месте] end A1 --> A2 --> B1 --> B2 --> C1 --> C2 --> C3 --> D1 --> D2 --> D3 D3 -.обновление системы.-> B1
Главный сдвиг: передача — это не событие «кинул ссылку», а тонкая непрерывная линия. Если разработчик впервые видит макет в момент передачи, вы уже проиграли: половина решений окажется дорогой или невозможной, и переделывать придётся дизайн, а не код.
Что физически должно быть в передаче
Не «красивая презентация», а конкретный набор:
- Экраны в состояниях — все ячейки матрицы, которые нетривиальны. Пустой, с одним элементом, с длинным текстом, с ошибкой, в загрузке.
- Таблица поведения — по одной строке на интерактивный элемент. Это самая полезная часть и её почти никогда не делают:
# Спецификация поведения кнопки «Сохранить» на экране профиля
element: profile_save_button
label: "Сохранить"
enabled_when: "форма изменена" # неизменённая форма — кнопка неактивна
on_click:
optimistic: false # результат влияет на другие экраны
immediate_feedback: "состояние loading в самой кнопке, повторный клик игнорируется"
spinner_delay_ms: 350 # раньше — не показываем ничего
states:
loading: { label: "Сохраняем", disabled: true }
success: { toast: "Профиль сохранён", duration_ms: 4000, announce: polite }
error_network:
message: "Не удалось сохранить: нет связи. Изменения не потеряны."
actions: ["Повторить"]
keep_form_values: true # критично: ввод не стирается
announce: assertive
error_validation:
focus: "первое поле с ошибкой" # фокус переезжает туда
summary: true # сводка ошибок над формой
keyboard: "Enter в любом поле формы = отправка"
- Правила адаптива словами. Не три макета на три ширины, а правило: «карточки: 3 в ряд от 1200, 2 от 768, 1 ниже; фильтры уезжают в шторку ниже 768». Правило покрывает бесконечность ширин, три макета — три точки.
- Тексты в виде текста, а не картинками — их надо копировать, переводить и согласовывать.
- Ссылки на компоненты системы: «это
Button/primary/medium», а не «нарисовано вот так». - Список открытых вопросов. «Что показывать, если у пользователя больше 500 проектов — не знаем, предлагаю виртуализацию, решаем вместе».
Разделение ответственности на ревью
Полезная договорённость, снимающая большинство конфликтов: замечания на дизайн-ревью делятся на три корзины и обсуждаются по-разному.
- Дефект — расходится с явной спецификацией или ломает состояние (нет фокуса, ошибка стирает ввод, пустой экран пустой). Чинится всегда.
- Отличие — визуальное расхождение в рамках допустимого (отступ из шкалы, но другой шаг). Обсуждается, часто принимается как есть.
- Улучшение — идея, которой не было в спецификации. Идёт в бэклог, а не в текущую задачу.
Про инструменты, токены и то, как это связано с жизнью дизайн-системы, — Дизайн-системы; про карьерную и процессную сторону — Передача в разработку и карьера.
10. Честно: где исследования, а где вкусовщина
Тема интерактивных состояний — редкое место, где часть решений действительно опирается на измерения, а часть — чистая конвенция. Смешивать их нечестно: «по исследованиям» звучит убедительно, но если исследования нет, вы просто продавливаете свой вкус авторитетом науки.
Что стоит за этим распределением:
- Есть данные. Пороги времени восприятия (Миллер, Кард, Догерти). Закон Фиттса — многократно воспроизведённая количественная модель. Требования к контрасту и размеру цели — измеримые пороги в WCAG. Момент показа ошибки в форме — сравнительные исследования Baymard Institute и Михаэля Коньевича показывают заметную разницу в успешности заполнения.
- Данные есть, но слабее, чем принято думать. Скелетон против спиннера: индустриальная практика широкая, публичных контролируемых сравнений мало, и результаты неоднозначны. Форма кривой прогресс-бара измерена (Харрисон), но эффект небольшой и практическая ценность близка к нулю.
- Вкусовщина, оформленная как правило. Радиусы, тени, конкретные длительности анимации внутри разумного диапазона, положение тостов, стиль иконок. Это нормально — просто называйте это конвенцией. У конвенции своя ценность: единообразие экономит внимание пользователя. Но защищать её надо словами «так принято в нашей системе», а не «так показали исследования».
- Отдельная категория — закон Якоба: люди проводят большую часть времени в чужих продуктах и ожидают, что ваш будет работать так же. Это не про красоту, а про перенос навыка. Поэтому «сделать не как у всех» — почти всегда решение с ценой, и цену надо назвать вслух.
Правильный способ закрыть спор о вкусе — не победить в споре, а перевести вопрос в проверяемый: сформулировать гипотезу и проверить на юзабилити-тесте или в метриках. Если проверять дорого и цена ошибки мала — подбросьте монетку и зафиксируйте решение в системе, это дешевле трёхдневной дискуссии.
11. Типичные ошибки
- Нарисован только счастливый путь. Проверка: попросите себя показать макет с нулём объектов и с ошибкой сети. Если их нет — работа не закончена.
- Пустой экран без действия. «У вас пока нет проектов» — и всё. Пустое состояние должно объяснять, что здесь появится, зачем это нужно и содержать одну главную кнопку. Для «ноль результатов поиска» — показывать сам запрос, предлагать снять фильтры и давать альтернативы.
- Спиннер вместо контекста. Полноэкранный кружок стирает всё, что человек уже видел. Загружайте частями, оставляя каркас.
- Ошибка съедает ввод. Самый дорогой дефект в списке — прямо конвертируется в брошенные формы.
- Мигающий индикатор. Показали на 80 мс — интерфейс кажется дёрганым.
- Двойная отправка. Кнопка не блокируется на время запроса — два платежа, две заявки, две записи к врачу.
disabledбез объяснения. Человек видит серую кнопку и не знает, чего не хватает.- Состояние только цветом. Красная рамка без иконки и текста.
- Убитый
focus-visible. «Убрали синюю рамку, она некрасивая» — продукт стал непроходим с клавиатуры. - Тост как единственный носитель важной информации. Он исчезает через 4 секунды, его можно не заметить, и он недоступен, если человек в этот момент читал другую часть экрана. Важное — на месте, а не в тосте.
- Модальное окно поверх модального. Признак того, что поток не спроектирован — см. пользовательские сценарии.
- Разные шаблоны загрузки на соседних экранах. Здесь скелетон, там спиннер, там ничего. Ощущение несобранного продукта; лечится решением на уровне системы, а не экрана.
12. Чек-лист перед передачей экрана в разработку
Пробегитесь по нему — он занимает пять минут и снимает большую часть будущих вопросов:
- Что видно до прихода данных? Скелетон, каркас, ничего?
- Что видно, если данных ноль? Есть ли текст, картинка и главное действие?
- Что видно, если объект один? Экран не выглядит сломанным?
- Что видно, если объектов очень много — 500, 10 000? Есть пагинация или виртуализация?
- Что видно, если текст вдвое длиннее ожидаемого? Если это одно слово в 40 символов?
- Что происходит при отказе сети во время действия? Ввод сохраняется?
- Что происходит при повторном нажатии во время запроса?
- Как выглядит фокус у каждого интерактивного элемента?
- Как объявляются асинхронные изменения незрячему пользователю?
- Есть ли действие, которое нельзя отменить? Оно защищено осмысленно или диалогом «вы уверены»?
- Что будет при
prefers-reduced-motion? - Все ли отступы, цвета и размеры взяты из системы?
Мини-итог
- Интерфейс — машина состояний. Один макет = одно состояние из десятков; остальные всё равно будут существовать, вопрос только в том, спроектировал их дизайнер или додумал разработчик.
- Обратная связь закрывает пропасть оценки. Правило простое: любое действие даёт видимый отклик в пределах 100 мс, даже если результат придёт через 10 секунд.
- Пороги 100 мс / 1 с / 10 с — не вкусовщина, а измеренная особенность восприятия. Из них следуют задержка перед показом индикатора и его минимальная длительность.
- Скелетон, спиннер, прогресс и оптимистичное обновление решают разные задачи; выбор диктуется тем, что система знает о будущем результате.
- У ошибок есть таксономия. Место показа определяется типом; ввод пользователя не теряется никогда; отмена почти всегда лучше подтверждения.
- Пустых состояний четыре, и каждое требует своего текста и своего действия.
- Передача в разработку — это спецификация поведения и живой разговор, а не набор картинок. «Пиксель в пиксель» — плохая цель; хорошая — соответствие системе и полное покрытие состояний.
- Отделяйте измеренное от принятого. Вкус — нормально, выдавать вкус за исследование — нет.
Источники
- Don Norman. The Design of Everyday Things, revised ed. Basic Books, 2013 — пропасти исполнения и оценки, аффордансы и означающие
- Jakob Nielsen. 10 Usability Heuristics for User Interface Design и Visibility of System Status
- Jakob Nielsen. Response Times: The 3 Important Limits
- Robert B. Miller. Response time in man-computer conversational transactions. AFIPS, 1968
- Stuart Card, George Robertson, Jock Mackinlay. The information visualizer, an information workspace. CHI, 1991
- Laws of UX. Doherty Threshold, Fitts’s Law, Jakob’s Law
- Chris Harrison et al. Rethinking the Progress Bar. UIST, 2007
- Scott Hurff. Why your user interface is awkward — you’re ignoring the UI stack
- Nielsen Norman Group. Error Message Guidelines и Progress Indicators Make a Slow System Less Insufferable
- Aza Raskin. Never Use a Warning When you Mean Undo. A List Apart
- GOV.UK Design System. Error message, Error summary, Buttons
- Apple. Human Interface Guidelines — Loading и Feedback
- Material Design 3. Progress indicators и Motion
- Luke Wroblewski. Mobile Design Details: Avoid The Spinner
- Baymard Institute. Inline form validation research
- W3C. WCAG 2.2 — Target Size (Minimum), Focus Appearance
- MDN. prefers-reduced-motion
- Val Head. Designing Safer Web Animation For Motion Sensitivity
- Stripe. Idempotent requests — как устроена защита от повторной отправки на стороне API
Что дальше
Мы спроектировали, когда и где система говорит с человеком. Осталось решить, что именно она говорит: в состояниях выше почти каждая ячейка содержит текст — подпись кнопки, сообщение об ошибке, заголовок пустого экрана. Плохая формулировка обесценивает идеально спроектированное состояние. Дальше — Текст в интерфейсе: формулировки, сообщения об ошибках, тон.