Пустые состояния: три режима и иерархия CTA
Разведи first-run, no-results и error в три разных дизайна с правильной иерархией действий.
Спроектируй три разных пустых состояния для: {{surface}}. Главное действие в обычной жизни: {{primaryAction}}.
Иронглавный тезис: "пусто" — это всегда три разные истории. Один шаблон на всех — провал.
Шаг 1. Различи режим по сигналу
| Режим | Сигнал | Психология |
|---|---|---|
| First-run | данных никогда не было (createdAt коллекции свежий) | "Что это вообще делает?" |
| No-results | данные есть, но фильтр/поиск/контекст обнуляет | "Я что-то сломал?" |
| Error / forbidden | запрос упал, нет прав, удалено | "Это не моя вина" |
Если ты не отличаешь их в коде — ты не отличишь их в дизайне.
Шаг 2. Структура каждого режима
A. First-run (educate)
- Заголовок: что появится здесь, в одном предложении ("Здесь будут ваши заказы")
- Подзаголовок: одно предложение про ценность ("Каждый — со ссылкой на статус и счёт")
- Превью: мини-скриншот / 1-2 фантомных строки, опционально
- Primary CTA: {{primaryAction}} — то самое первое действие
- Secondary: "Импортировать", "Смотреть пример", "Доки"
- НЕ нужно: красивая иллюстрация на пол-экрана
B. No-results (recover)
- Заголовок: что именно не нашлось ("Ничего не подходит под фильтр «Активные за май»")
- Подзаголовок: что попробовать конкретно
- Primary CTA: сбросить ближайшее препятствие ("Очистить фильтр") — НЕ {{primaryAction}}
- Secondary: скорректировать запрос (расширить даты, убрать тег)
- НЕ нужно: онбординг-копи, "Добро пожаловать"
C. Error / forbidden (explain + escape)
- Заголовок: что произошло без жаргона ("Нет доступа к этому разделу")
- Подзаголовок: почему, если можем сказать
- Primary CTA: действие, которое реально разблокирует ("Запросить доступ")
- Secondary: уйти безопасно ("Вернуться к списку")
- НЕ нужно: оптимистичный CTA "Создать" — он не работает
Шаг 3. Иерархия CTA
Правило: один primary, один secondary, опционально один tertiary (текстовая ссылка). Три primary рядом = ни одного.
[ Primary CTA ] Secondary CTA tertiary link
Primary — solid + bg-accent. Secondary — outline или ghost. Tertiary — underline.
Шаг 4. Размер и место
- Высота состояния = высота родителя, но контент центрирован в верхней трети, не вертикально по центру (читается выше)
- Ширина текстового блока — 320-440px, чтобы строки не растягивались
- Иконка/иллюстрация — опционально, ≤ 64px, монохромная, чтобы не отбирать вес у CTA
Формат вывода
Для каждого из трёх режимов:
### Режим: [First-run | No-results | Error]
**Сигнал в данных:** ...
**Копия:**
H1: "..."
body: "..."
**CTA:** Primary "..." / Secondary "..."
**JSX-скелет:**
```tsx
<EmptyState>...</EmptyState>
В конце — **матрица решений** в одной таблице: какой сигнал → какой режим → какая копия → какой primary CTA.
## Анти-паттерны
- Один и тот же экран с разной картинкой для всех трёх режимов
- Primary CTA "Начать" — не глагол + объект
- Stock-человечек грустит
- На no-results показать онбординг — пользователь уже работал
- На forbidden показать "Создать" — некуда
Прокачать CTA на странице
Найти все CTA, оценить и переписать с конкретикой и глаголами действия.
Billing-страница
Что показывать: план, история, способ оплаты, инвойсы, отмена.
Сгенерировать варианты UI-компонента
Создать 4-6 разных подходов к компоненту с разным визуальным языком и трейд-оффами.