Композиция и темизация компонентов

Что это такое

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

Почему это важно

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

Ключевые идеи

Компонуйте вложением, а не настройкой

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

<Card>
  <Card.Header>
    <Avatar user={user} />
    <span>{user.name}</span>
  </Card.Header>
  <Card.Body>{bio}</Card.Body>
</Card>

Делите контракт стилей через пользовательские свойства

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

.compact {
  --control-pad: 0.25rem 0.5rem;
  --control-radius: 0.25rem;
}

.button { padding: var(--control-pad); border-radius: var(--control-radius); }

Темизируйте токенами, а не переопределениями

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

Держите поверхность темизации документированной и версионированной

Всё, что потребитель может задать, — публичный API. Запишите его, назовите единообразно и считайте, что переименование ломает совместимость. Библиотеки компонентов, которые об этом забывают, в итоге получают два класса для одной цели, потому что переименовать первый было слишком рискованно.

Дайте вызывающему расширять, а не форкать

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

Практика

Соберите компактную тему без правки компонентов

Возьмите два-три существующих компонента, которые делят значения отступов и радиуса. Вынесите эти значения в пользовательские свойства и добавьте класс-обёртку, который задаёт их в более плотном масштабе.

Готово, когда: внутри обёртки компоненты выглядят компактно, снаружи — без изменений, и ни один файл компонента не был отредактирован.

Расширьте компонент слотом

Выберите компонент, который вызывающие однажды скопировали. Добавьте слот, который покрыл бы разницу, и переведите одну копию на его использование.

Готово, когда: копия удалена, оригинал правильно рисует оба варианта, а имя слота задокументировано.

Частые ошибки

  • Темизация переопределением селекторов компонента — специфичность растёт с каждой темой, и компонент нельзя отрефакторить; исправить: откройте значения пользовательскими свойствами и позвольте теме их задавать.
  • Свойство на каждую новую вариацию — API растёт без предела, а недопустимые сочетания множатся; исправить: добавьте слот или токен и дайте вызывающему менять вариацию.
  • Протечка темы в компонент — компонент теперь знает об одном бренде и не может обслуживать второй; исправить: пусть компонент читает только свои свойства и их запасные значения.
  • Недокументированная поверхность темизации — потребители угадывают имена свойств и лезут во внутренности; исправить: документируйте каждое публичное свойство и считайте изменения версионируемыми.
  • Форк компонента ради одного экрана — исправление доходит только до одной копии, и копии расходятся; исправить: расширьте оригинал слотом вместо копирования.

Что читать дальше

  • Проектирование API компонента (02-frontend/12-ui-components/01-designing-a-component-api) — свойства и значения по умолчанию, которые темизирует этот урок; перечитайте его, прежде чем добавлять новую вариацию.
  • Современные возможности CSS (02-frontend/05-css/04-modern-css) — пользовательские свойства и поведение каскада, на котором держится подход с токенами.
  • Дизайн-токены (03-design-ux/41-design-tokens) — как называть и упорядочивать значения, которые задаёт тема.