Соберите дизайн-систему из ролей и токенов
Построение дизайн-системы с нуля или аудит существующей. Типографика, цвета, компоненты, сетка, spacing. Используйте при создании нового продукта или когда дизайн-система нуждается в систематизации.
Как агент работает
Сначала проверяется, окупается ли система вообще: она обязательна, если UI пишут трое и больше, экранов от тридцати, на одном бренде живут два продукта, нужна тёмная тема или white-label либо интерфейс отдаётся интеграторам как контракт. Дальше идёт измерение разнобоя в клоне через sandbox_bash, а не разговор о вкусах: до 40 уникальных цветов здорово, свыше 90 — разнобой; px-значений здорово до 15, тревожно 15-30; начертаний не больше трёх; радиусов и теней до четырёх; файлов с inline-стилями не больше 5%.
Тема переключается только через средний уровень токенов, потому что тема — переопределение ролей, а не значений: если в компоненте записано конкретное значение серого, для тёмной темы придётся решать для каждого места отдельно, стало оно фоном страницы, карточки или разделителем. В тёмной теме роль, указывающая на ступень N, указывает на 1000 минус N того же тона, насыщенность акцента снижается на 10-20%, фон страницы берётся 900-950 и никогда не чистый чёрный, дающий на OLED ореолы вокруг светлого текста. Имена описывают роль, а не вид.
Контраст — проверяемое требование, а не мнение: WCAG 2.1 и 2.2 уровня AA, с которыми гармонизирован российский ГОСТ Р 52872-2019, дают 4.5:1 для обычного текста, 3:1 для крупного от 24 px или 18.66 px полужирного и 3:1 для границ полей, иконок-действий и фокус-кольца. Проверка всегда находит одно и то же: приглушённый серый на цветной поверхности, белый текст на жёлтой или оранжевой кнопке-warning, почти невидимую границу поля около 1.5:1 и состояние, закодированное только цветом. Снятие outline без замены — дефект, а не стиль.
Кириллица ломает шкалы, собранные под латиницу: русский текст на 10-15% длиннее, а интерфейсные слова вдвое, поэтому кнопки проектируются от контента с min-width и проверяются на самой длинной реальной подписи. При line-height 1 браузер обрезает надстрочные знаки у «Й» и «Ё», минимум для заголовков — 1.15-1.25. Символ рубля выпадает из урезанных сабсетов, tabular-nums обязателен для колонок с суммами, а переносы работают только при lang=ru. Шкала отступов строится от 4 px, и обязательно проверяется ширина 360 px — макет, сделанный на 390, ломается именно там.
Навык не выдаёт картинки компонентов за документацию: изображение расходится с кодом в день правки, поэтому живая документация генерирует таблицу токенов из файла токенов и показывает матрицу состояний рядом с копируемым кодом. Правило, которое нельзя проверить линтером, будет нарушено, поэтому внедрение идёт фазами без остановки разработки: инвентаризация, токены поверх существующего без визуальных изменений, семантический слой, ядро компонентов по частоте использования, миграция по правилу касания и гард в CI на изменённых строках диффа.
Дизайн-система: построить с нуля или привести в порядок
1. Сначала посчитай, окупается ли
Не нужна вовсе: меньше 15 новых экранов на полгода и 1–2 фронтендера; один продукт, бренд и тема; фаза поиска модели с выбрасыванием экранов раз в две недели. Замена: 30–50 строк CSS-переменных, три компонента (кнопка, поле, карточка), одностраничный DESIGN.md — 1–2 дня и 80% пользы. «Система не нужна» — правильный ответ, а не отказ от работы.
Обязательна при любом из: ≥ 3 человек пишут UI; ≥ 30 экранов; два продукта на одном бренде (кабинет, приложение, виджет в Битрикс24); нужна тёмная тема, вторая плотность или white-label; UI отдаётся интеграторам — система работает как контракт.
2. Инвентаризация: посчитай реальный разнобой
Начинай с измерения — sandbox_bash в клоне (git clone --depth=1):
| Показатель | Здорово | Тревожно | Разнобой |
|---|---|---|---|
| Уникальных цветов | ≤ 40 | 40–90 | > 90 |
| Уникальных px-значений | ≤ 15 | 15–30 | > 30 |
| Начертаний шрифта | ≤ 3 | 4 | ≥ 5 |
| Радиусов / теней | ≤ 4 | 5–8 | ≥ 9 |
Файлов с inline style={{}} | ≤ 5% | 5–15% | > 15% |
3. Три уровня токенов
Тема — переопределение ролей, не значений. bg-gray-50 в компоненте для тёмной темы расходится на три исхода (фон страницы gray-950, карточка gray-900, разделитель gray-800) — ручной проход по всему коду, ровно то, что система должна была отменить. С ролями тема — один блок:
4. Палитра
Тёмная тема — зеркало лестницы: роль со ступени N в тёмной указывает на 1000 − N того же тона. Поправки: насыщенность акцента (C) −10–20%, иначе звенит на тёмном; фон страницы 900–950, но не #000000 (ореолы на OLED). Тени в тёмной теме почти не работают — иерархию поверхностей передавай светлотой фона и границей.
Состав: нейтраль и бренд по 11 ступеней, интенты success/warning/danger/info по 4–5, отдельная палитра графиков (6–8 цветов, различимых при дейтеранопии) — красный в графике значит категорию, а не ошибку.
5. Контраст: проверяемое требование
Минимумы (WCAG 2.1/2.2 AA; ГОСТ Р 52872-2019 гармонизирован с ними): обычный текст 4.5:1; крупный (≥ 24 px или ≥ 18.66 px полужирного) 3:1; границы полей, иконки-действия, индикаторы состояния, фокус-кольцо 3:1 к соседнему фону.
Стабильные провалы: text-muted на цветной поверхности (серый рассчитан на белый, на bg-info-subtle ~3:1); белый текст на кнопке-warning (жёлтый/оранжевый 500 почти никогда не держат 4.5:1); граница поля gray-200 на белом (~1.5:1 — поля невидимы); состояние, закодированное только цветом (красная рамка без иконки не читается при дальтонизме). Фокус-кольцо — один токен --color-focus-ring (3:1 к обеим поверхностям) и :focus-visible; снятие outline без замены — дефект.
6. Типографическая шкала
Для интерфейсов отношение 1.2, реже 1.25; 1.333 и 1.5 — лендинги. 1.333 от 16 px (16 → 21.3 → 28.4 → 37.9) не даёт промежуточных размеров плотного интерфейса — их добирают на глаз, отсюда 40 уникальных размеров из инвентаризации. 1.2 даёт 16 → 19.2 → 23 → 27.6 → 33.2 — 7–8 значений.
| Токен | Размер | Line-height | Назначение |
|---|---|---|---|
text-xs | 12 | 16 | служебные метки, единицы измерения |
text-sm | 14 | 20 | подписи полей, плотные таблицы |
text-base | 16 | 24 | основной текст, поля ввода |
text-lg | 20 | 28 | заголовок карточки, метрика |
text-xl | 24 | 32 | заголовок раздела |
text-2xl | 30 | 38 | заголовок страницы |
Поля ввода на мобильных — не меньше 16 px: Safari на iOS зумит страницу при фокусе в меньший кегль. Начертаний ≤ 3 (400/500/700), длина строки 60–75 знаков (max-width: 65ch).
7. Кириллица
Русский текст на 10–15% длиннее английского, интерфейсные слова — вдвое (Save → «Сохранить»). Кнопки — от контента с min-width; в библиотеке держи пример с самой длинной реальной русской подписью.
«Й» и «Ё» несут надстрочные знаки, и line-height: 1 (leading-none для крупных заголовков) их обрезает. Минимум для заголовков — 1.15–1.25.
Безопасный выбор: Golos Text (Paratype, SIL OFL, свободен и для коммерции), PT Sans / PT Mono, Inter, Manrope, Onest. Фирменные шрифты экосистем и банков лицензированы под владельцев.
Числа и текст:
- ₽ (U+20BD) в урезанных сабсетах отсутствует — «тофу» (□). Не вырезай при сабсеттинге; формат —
1 250 ₽с неразрывным пробелом. font-variant-numeric: tabular-numsобязателен для колонок с суммами и процентами — токен системы, а не решение автора таблицы.hyphens: autoработает только сlang="ru"; для артикулов —overflow-wrap: anywhere.uppercaseна кириллице — только сletter-spacing: 0.04em. «ё» в подписях пишется, в поиске нормализуется, иначе «Королёв» не найдётся по «Королев».
8. Сетка, отступы, правило внутреннего и внешнего
База — 4 px (восьмипиксельная груба: между иконкой и текстом в строке таблицы нужно 4 или 6). Шкала: 0, 2, 4, 8, 12, 16, 20, 24, 32, 40, 48, 64, 80; значений вне шкалы в коде быть не должно. Радиусов четыре (none, sm 4–6, md 8–10, full), внешний радиус = внутренний + отступ между ними. Теней ≤ 4, каждая привязана к уровню поверхности.
Расстояние между элементами всегда меньше расстояния вокруг группы — единственный механизм, по которому глаз понимает принадлежность. Подпись от поля 8 → поле от следующей пары 16 → группа от секции 32; соотношение соседних уровней ≥ 1.5×. Самый частый дефект: подпись прижата к предыдущему полю сильнее, чем к своему, — форма читается со смещением на строку.
Сетка: десктоп 12 колонок, gutter 24, максимум 1200–1440; мобильный 4 колонки, gutter 16. Брейкпоинты 640 / 768 / 1024 / 1280 / 1536, и обязательно 360 px — большая доля бюджетных Android у российской аудитории; макет с 390 ломается на 360 в длинных русских подписях.
9. Состояния компонента: полный набор
Интерактивный элемент: default, hover, active, focus-visible, disabled, loading, selected, error, read-only. Контейнер данных: loading (скелетон по размеру реального контента), empty (как наполнить), no-results (кнопка сброса фильтра), error (причина + «повторить»), partial, overflow.
Забывают, в порядке частоты:
focus-visible— снеслиoutlineради вида, клавиатурная навигация умерла.no-resultsотдельно отempty— «У вас пока нет заказов» при активном фильтре читается как потеря данных.loadingна кнопке — без блокировки повторного нажатия форма уходит дважды; в биллинге это двойное списание.disabledбез объяснения причины.- Длинный контент — обрезка, перенос или сжатие соседей выбраны, а не случились.
- Hover на таче (залипает после тапа —
@media (hover: hover)) иprefers-reduced-motion, выключающий анимации целиком.
Комбинаций «варианты × размеры × состояния» у кнопки 4×3×10 = 120: их не рисуют, их генерируют циклом по спискам, чтобы новый вариант сам появился везде.
10. Именование
Формат: <категория>-<роль>-<модификатор>-<состояние> (color-border-input-invalid). Имя описывает роль, не вид: color-text-danger, не color-text-red — при смене красного на терракотовый red станет ложью. Не кодируй значение в имени: space-16 запрещает менять шкалу. Модификаторы интенсивности фиксируй заранее — subtle → default → emphasis → inverse; пропсы по ролям (variant, size, tone) — булевы (isPrimary, isDanger) допускают невозможные сочетания. Имена primary2, blueNew, cardV2 значат: нужной роли нет — заведи её, а не наращивай суффикс.
11. Документация, которой пользуются
Мёртвая документация — картинки компонентов: картинка расходится с кодом в день правки. Живая — генерирует таблицу токенов из файла токенов, показывает компонент живым и матрицей состояний рядом с копируемым кодом, называет случаи когда компонент НЕ применять (самая полезная и почти всегда отсутствующая секция) и записывает решения с причинами: не «радиус 8», а «радиус 8, потому что при 12 в плотных таблицах углы съедают строку».
Правило, которое нельзя проверить линтером, будет нарушено. Документ живёт рядом с файлом токенов (edit_file), не в вики.
12. Внедрение без остановки разработки
- Фаза 0 (0.5 дня). Инвентаризация — раздел 2: таблица разнобоя, базовая метрика.
- Фаза 1 (1–2 дня). Токены поверх существующего — 15–20 доминирующих значений: визуально ничего не меняется, появляется словарь, ноль конфликтов с чужими ветками.
- Фаза 2 (2–3 дня). Семантический слой и тема — роли + переопределения тёмной, даже если тема пока не включается.
- Фаза 3 (5–10 дней). Ядро компонентов по частоте: почти всегда Button → Input → Select → Modal → Table → Toast; первые три покрывают половину вхождений. Частоту даёт
rg -o '<[A-Z][A-Za-z]+' src | sort | uniq -c | sort -rn. - Фаза 4 (постоянно). Миграция по правилу касания — новый код только на системе, старый переписывается, когда его открыли по задаче.
- Фаза 5. Гард в CI — запрет новых hex вне токенов на изменённых строках:
14. Шаблон DESIGN.md
Отдавай заполненным: раздел, где нечего написать, — нерешённый вопрос.
15. Протокол работы
Не предлагай непроверяемого. Вместо «улучшить визуальную иерархию» — какой токен на каком элементе меняется, с какого значения на какое и по какому измеримому признаку станет видно улучшение.
Похожие навыки
Попробуйте этот навык
Зарегистрируйтесь и используйте навык «Дизайн-консультация» бесплатно.