Обратная связь

  • Показ и отклик
  • 4 мин
  • компонентов: 7
  • пар: 3

Семь компонентов отвечают на один вопрос — куда идёт сообщение. Ошибка в выборе и есть причина, по которой интерфейс кажется неотзывчивым.

Ревизия 2026-09-05

На этой странице

Что решает

Всякое сообщение интерфейса — ответ на вопрос «что сейчас произошло и касается ли это меня». Трудность не в том, чтобы написать текст, а в том, чтобы положить его туда, куда человек уже смотрит, и в тот момент, когда он спрашивает.

Этим заняты семь компонентов, и они не взаимозаменяемы. Каждый отвечает на свой вопрос, и условие для каждого записано в библиотеке:

  1. Сообщение относится к месту на странице — GrAlert. Держится, пока не изменится обстоятельство, потому что обстоятельство никуда не делось.
  2. Действие завершилось — GrToaster. Появился, прочитан, ушёл; на странице ничего не поменяло формы.
  3. Запрос упал — GrResponseErrorBanner. Берёт сырую ошибку и превращает статусы, разбор валидации и сетевые сбои в заголовок, сообщение и повтор — не зная, что это был за запрос.
  4. Список пуст с самого начала — GrEmptyState. Это не ошибка: место, куда ещё ничего не положили, и подсказка, что с этим делать.
  5. Контента ещё нет — GrSkeleton. Форма приезжает раньше данных, и страница не прыгает, когда они приедут.
  6. Контент уже есть и обновляется — GrLoading. Отличается от скелета: есть на что смотреть, и оно не должно исчезать.
  7. Доля выполненного известна — GrProgressBar. Только когда действительно известна: полоса, которая гадает, — ложь с числом внутри.

Анатомия

У обратной связи три свойства, и они есть у каждого сообщения — выбирали вы их или нет.

Место. Привязано к тому, о чём говорит, или висит над страницей. Первое — GrAlert и ошибка внутри поля, второе — GrToaster. Сообщение о конкретном поле, висящее над страницей, человек сопоставляет сам.

Срок жизни. До закрытия, до изменения обстоятельств или несколько секунд. Правило простое: сообщение, требующее действия, живёт, пока действие не сделано. Тост, несущий единственную возможность отменить, — ловушка с таймером.

Вес. Цвет, иконка и формулировка говорят, насколько всё плохо. Трёх уровней достаточно: нейтральный, предупреждение, ошибка. Четвёртый придумывают те, кто называл ошибкой всё подряд, а потом понадобилось что-то громче.

Пример

Так и не так

Так — Тост о завершившемся, алерт о том, что всё ещё верно

«Сохранено» кончается в момент прочтения — тост. «Проект только для чтения» верно, пока что-то не изменится, — алерт на странице. Вопрос не в важности, а в длительности.

Так не надо — Тост для того, что требует решения

Сообщение, исчезающее по таймеру, не может нести единственную «отмену». Человек, отвернувшийся на секунду, потерял возможность и не знает, что она была. Нужно решение — сообщение остаётся, пока его не примут.

Так — Скелет, когда нет ничего; индикатор, когда есть

GrSkeleton резервирует форму до прихода данных, и ничего не прыгает, когда они придут. GrLoading говорит, что существующий блок обновляется, — содержимое остаётся читаемым всё это время.

Так не надо — Крутилка вместо всей страницы

Подменяя содержимое крутилкой, вы выбрасываете то, что человек читал, и не сообщаете, чего ждать. Отличить медленную страницу от сломанной он не может: и то и другое выглядит как кружок.

Так — Говорить, что делать дальше, — особенно в пустоте

Пустой список — вопрос, а не отказ: «счетов пока нет» и кнопка, создающая первый. GrEmptyState существует потому, что первый экран нового аккаунта — самый частый экран любого продукта и наименее продуманный.

Так не надо — «Что-то пошло не так»

Не говорит ничего и не предлагает ничего. Сервер прислал статус, а часто и причину; GrResponseErrorBanner их читает. Если причина и правда неизвестна — скажите, что человек может сделать: повторить или написать в поддержку, — и оставьте честную фразу короткой.

Доступность

Появившееся сообщение надо объявить. Тост, который плавно возник и растаял без живой области, для скринридера не существует: действие завершилось, и никто об этом не сказал.

Срочность — свойство сообщения, а не цвета. Настойчивое объявление прерывает то, что читают сейчас: верно для ошибки, которая мешает работать, и неверно для «сохранено». Перебить человека посреди фразы, чтобы сообщить, что всё хорошо, — хуже, чем промолчать.

Загрузка должна восприниматься без движения. Анимированная крутилка ничего не значит для того, у кого движение выключено, а prefers-reduced-motion — не редкость. Смысл несут текст и зарезервированное место; анимация его повторяет.

Время показа — единственное, чего за вас не решит ни один компонент. Тост, живущий четыре секунды, нечитаем для того, кто читает медленно, и правильного числа тут нет — поэтому важное не должно от него зависеть.

Компоненты паттерна

Список строится из frontmatter, а не из ссылок в тексте: переименование слага здесь не теряется.

Клавиатура

Контракты приходят из библиотеки. Паттерн их не пересказывает — он на них ссылается.