К содержимому
--:--
местное время
EVS.CODER / navigation

Навигация

На связи

TelegramGitHubLinkedIn

ENGINEERING NOTES / Design System

Design System, которая выдерживает давление продукта

Токены, API, доступность, процессы и изменения, которые не разрушают общую библиотеку.

Статья на русском языке

Библиотека компонентов — ещё не система

Button, Input, Modal и набор цветов можно собрать относительно быстро. Настоящая проверка начинается позже: маркетинг просит необычный CTA, дизайнер меняет визуальный язык, мобильная команда вводит новые паттерны, а несколько продуктов начинают использовать одни и те же компоненты по-разному. Если каждое требование превращается в очередной флаг или CSS-исключение, библиотека постепенно становится препятствием.

Design System — не только код. Это визуальные правила, токены, интерфейсы компонентов, рекомендации по использованию, документация, правила изменений и распределение ответственности. Её задача — ускорять создание согласованных интерфейсов, не запрещая продукту развиваться. Слишком жёсткая система порождает обходные решения; слишком свободная перестаёт обеспечивать единообразие.

Продуктовое исключение или системная потребность?

Допустим, промостраница требует огромную кнопку с градиентом и анимацией. Можно добавить в базовый Button параметры `isPromo`, `specialGradient`, `largeMobile` и `animated`. Но через несколько месяцев компонент рискует превратиться в набор конфликтующих условий. Разумнее сначала определить, является ли требование общим паттерном или локальной композицией.

Базовая кнопка отвечает за семантику, клавиатуру, состояния, фокус и предсказуемые варианты. Промо-композиция может использовать её как примитив, не заставляя всю систему поддерживать одноразовое оформление. Если одинаковое исключение повторяется у разных команд, это сигнал пересмотреть базовый контракт.

**Вопросы перед добавлением варианта:**

  • Повторяется ли сценарий в нескольких продуктах?
  • Можно ли собрать его из существующих примитивов?
  • Какие новые комбинации параметров появятся?
  • Кто будет поддерживать вариант через год?
  • Есть ли правила его применения и миграции?

Токены как слой визуальной архитектуры

Значение `#2563eb` говорит о цвете, но ничего не сообщает о назначении. Семантическое имя `color-action-primary` фиксирует роль цвета в продукте. Обычно полезно различать примитивные токены (палитра, шкала размеров), семантические токены (текст, поверхность, действие) и компонентные токены там, где требуется независимая настройка.

При смене бренда или темы компонент продолжает обращаться к смысловому имени. Но не создавайте десятки уровней ссылок ради абстракции: каждое имя должно помогать управлять реальными изменениями. Для обмена токенами между инструментами существует спецификация Design Tokens Community Group; её стабильная версия 2025.10 опубликована как формат сообщества, а не как стандарт W3C Standards Track.

TypeScript / пример
:root {
    --blue-600: #2563eb;
    --gray-900: #111827;
    --white: #fff;

    --color-action-primary: var(--blue-600);
    --color-text-primary: var(--gray-900);
    --color-surface-primary: var(--white);

    --button-primary-background: var(--color-action-primary);
    --button-primary-text: var(--white);
}

[data-theme='dark'] {
    --color-action-primary: #60a5fa;
    --color-text-primary: #f9fafb;
    --color-surface-primary: #111827;
}

.button--primary {
    color: var(--button-primary-text);
    background: var(--button-primary-background);
}

API компонента — долгосрочный контракт

Набор boolean-флагов вроде `primary`, `danger`, `large`, `compact`, `rounded` быстро допускает противоречивые комбинации. Явные `variant` и `size` позволяют выразить поддерживаемые варианты и упростить типизацию. При этом не стоит превращать базовый компонент в универсальный конструктор любого макета: сложные сценарии лучше решать композицией.

Это сокращённый пример API, а не готовая реализация всех состояний: индикатор загрузки должен сохранять доступное название кнопки, а визуальные состояния требуют отдельной проверки. Для ссылок используйте семантику ссылок, а не кнопку с ручной имитацией навигации. Компонент должен принимать стандартные атрибуты платформы и не смешивать базовый UI с логикой конкретной формы или заказа.

TypeScript / пример
import type { ButtonHTMLAttributes, ReactNode } from 'react';

type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
    variant?: 'primary' | 'secondary' | 'danger';
    size?: 'sm' | 'md' | 'lg';
    loading?: boolean;
    children: ReactNode;
};

export function Button({
    variant = 'primary',
    size = 'md',
    loading = false,
    disabled,
    children,
    ...props
}: ButtonProps) {
    return (
        <button
            {...props}
            type={props.type ?? 'button'}
            disabled={disabled || loading}
            aria-busy={loading || undefined}
            data-variant={variant}
            data-size={size}
        >
            {children}
        </button>
    );
}

Доступность — свойство общего контракта

Ошибка в одном общем Dialog тиражируется на десятки экранов. Поэтому клавиатурная навигация, фокус, доступное имя, семантика, состояния ошибок и контрастность должны проектироваться вместе с API, а не добавляться после релиза. Для сложных примитивов — Dialog, Combobox, Menu — важно проверять ожидаемые паттерны взаимодействия по WAI-ARIA Authoring Practices и тестировать реальными устройствами ввода.

Автоматические проверки с axe помогают находить часть нарушений, но не заменяют ручные тесты: сценарии работы с экранным диктором, логический порядок фокуса и понятность текстов требуют человеческой оценки. Компонент, прошедший автоматическую проверку, не обязательно удобен в реальном пользовательском сценарии.

**Минимальный набор проверок:** клавиатура без мыши; видимый фокус; корректное название и описание; ошибки формы, связанные с полями; контрастность во всех темах; поведение при увеличенном масштабе и reduced motion.

Документация должна отвечать «когда», а не только «как»

Список пропсов не объясняет, когда выбрать Modal вместо Drawer, какую кнопку считать основным действием и что делать при длинном тексте. Полезная документация показывает назначение, антипаттерны, допустимые варианты, доступность, примеры композиции и ограничения. Storybook позволяет хранить состояния компонентов рядом с кодом и дополнять их документацией.

Для каждого базового компонента полезно описать: назначение; правила выбора; состояния default/hover/focus/disabled/loading/error; responsive-поведение; API; требования доступности; реальные примеры; историю изменений. Документация должна обновляться вместе с компонентом в одном pull request. Иначе она быстро перестаёт быть источником истины.

Дизайн и код: один язык, а не два независимых мира

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

Синхронизация не означает, что любой макет автоматически должен стать новым компонентом. Дизайн-макет может содержать эксперимент, а кодовая библиотека — поддерживаемый контракт. Нужен понятный процесс продвижения решения из эксперимента в систему и обратной связи от разработчиков к дизайнерам.

Версионирование и миграции

Общий пакет становится инфраструктурой: изменение в нём затрагивает множество потребителей. Следуйте осмысленному версионированию, публикуйте changelog и выделяйте breaking changes. Для сложной миграции подготовьте codemod, примеры «до/после» и период совместимости, если он экономически оправдан.

Не путайте номер версии с гарантией качества. Перед выпуском проверяйте публичные типы, визуальные регрессии, сценарии доступности и приложения-потребители. Если компоненты выпускаются независимо, явно документируйте совместимость пакетов и правила обновления.

TypeScript / пример
// Было:
<Button primary large>Сохранить</Button>

// Стало:
<Button variant="primary" size="lg">Сохранить</Button>

Качество: визуальные тесты и контрактные проверки

Unit-тест проверит обработчик клика, но может не заметить сломанную сетку или обрезанный текст. Визуальные тесты помогают обнаружить изменения внешнего вида, однако требуют контролируемых шрифтов, размеров экрана и данных, иначе команда утонет в ложных срабатываниях. Дополняйте их функциональными тестами взаимодействия, проверкой доступности и тестированием интеграции в реальных продуктах.

Особенно важны состояния, которые легко забыть: длинные названия, пустые данные, ошибка, загрузка, узкий экран, локализация, увеличенный шрифт и тёмная тема. Демонстрационная кнопка на белом фоне — недостаточное доказательство готовности компонента к продукту.

Управление системой без бюрократии

Нужен понятный путь предложения изменений. Команда продукта описывает проблему и сценарии, владельцы системы проверяют повторяемость и влияние на контракт, затем решение проходит дизайн- и техническое ревью. Маленькие исправления не должны ждать многонедельного комитета, а breaking changes — проходить незаметно.

Ведите список запросов, причины отказов и исключения. Повторяющиеся обходы показывают, где система не соответствует продукту. Полезные показатели — доля использования поддерживаемых компонентов, число дублирующихся реализаций, время внедрения типового сценария и стоимость миграций. Не превращайте процент покрытия компонентами в самоцель: иногда специализированный UI действительно нужен.

План внедрения без остановки продукта

Начните с инвентаризации существующих интерфейсов и выявления повторяющихся паттернов. Определите базовые токены и небольшой набор наиболее востребованных компонентов. Добавьте документацию и проверки. Переведите один реальный продуктовый поток, соберите обратную связь и только затем расширяйте систему.

Не пытайтесь сразу стандартизировать всё. Редко используемый сложный виджет может оставаться продуктовым, пока не появится доказанная потребность в переиспользовании. Успешная система не отменяет разнообразие продукта — она делает повторяющиеся решения надёжными и оставляет место для осмысленных исключений.

Итог

Design System выдерживает давление продукта, когда умеет отличать устойчивые правила от временных требований. Её основа — понятные токены, ограниченные и расширяемые API, встроенная доступность, живая документация, проверяемые релизы и прозрачный процесс изменений. Не количество компонентов, а способность развиваться без накопления бесконечных исключений определяет её зрелость.

Ещё одна заметкаБыстрый на тестах, медленный у пользователей: где frontend теряет производительность