Привет! Меня зовут Вадим Казаченко, я лид фронта дизайн-системы ВТБ. Год назад устроился в банк и получил командную задачу — построить единую библиотеку компонентов, настолько универсальную, чтобы ее можно было использовать в любом продукте дизайн-системы банка, и при этом она не должна становиться «узким горлышком», как это обычно происходит с UI-китами в крупных компаниях. Дело в том, что в ВТБ существует множество дизайн-систем, над которыми работают десятки дизайнеров.

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

К истории стилей

Первую версию CSS мы получили в далеком 1996 году, и с тех пор концептуально ничего не менялось: схема Selector { Property: Value } и полная статичность. Плюсом было только то, что грузятся стили из файла параллельно с загрузкой JS-кода. Если вы, конечно, не произвели весь стайлинг внутри HTML-страницы, что замедлило бы ее загрузку.

Работать со стилями стало приятнее с появлением препроцессоров. Некоторые из них добавляли улучшенный синтаксис, переменные и функции для генерации CSS, другие же, как postCSS, дали возможность совершать манипуляции со стилями.

Популярные расширения postCSS позволили автоматически добавлять префиксы для одинакового отображения стилей в браузерах, уменьшали размерность файлов для загрузки пользователями или же помогали проверять разработчикам код на ошибки линтерами.

Схема работы PostCSS

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

Решением стало использование CSS-модулей в сочетании с методологией нейминга классов БЭМ (Блок, Элемент, Модификатор).

Пример использования классов в БЭМ:

<div class=”card”>

	<h3 class=”card__title”>Заголовок</h3>

	<p class=”card__text”>Текст</p>

</div>

<div class=”card card–large”>

	<h3 class=”card__title”>Заголовок большой карточки</h3>

	<p class=”card__text”>Текст большой карточки</p>

</div>
  1. БЭМ описала правила к наименованию компонентов, его составных частей и модификаций.

  2. CSS-модули позволили безболезненно распиливать стили на разные файлы и ограничивать локальной областью, где к их классам добавился уникальный ключ, исключающий повторы в названиях. 

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

Built-In CSS Support

Итак, при использовании этого подхода получаем следующее:

Плюсы

  1. Компоненты стилизуются модульно.

  2. Нет наложения нейминга классов.

  3. Статичный CSS загружается параллельно с JS.

  4. Нет проблем с SSR.

Минусы

  1. Сложно заводить и поддерживать токены для реализации тем.

  2. Отсутствие типизации и подсказок без настройки IDE.

  3. Чтобы соединить стили с компонентами, нужно маппить классы с пропсами.

Актуальный этап развития CSS = > CSS-in-JS

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

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

Пример использования styled-components

Итого, при использовании этого подхода получаем следующее:

Плюсы:

  1. Не нужно думать о соединении стилей с пропсами.

  2. Темы переключаются на лету.

  3. SSR поддерживается, но есть нюансы.

Минусы:

  1. Отсутствие типизации и подсказок.

  2. Рендер стилей происходит только после загрузки JS.

  3. Бесконечная генерация классов с хаотичным неймингом.

А что лучше, чем CSS-in-JS?

Логичным продолжением развития работы с CSS-in-JS стал переход к CSS-in-TS и его реализация в библиотеке stitches.dev с отличной документацией и широкими возможностями. Какие еще важные для нас преимущества можно отметить?

Пример базового добавления стилей в Stitches

Первый плюс — базовый стайлинг. Обертка styled практически такая же, как в styled-components, только стилизация происходит внутри объекта, а не строки. Для каждого CSS-свойства заданы типы, что позволяет без особого труда выставить стили с подсказками редактора кода и заняться более важными делами.

Вариативность стилей в Stitches

Второй плюс. Динамичность в этой библиотеке реализована с помощью вариантов: пропсы, которые передаются в компонент, переключают статичные объекты стилей. Помните, я упоминал БЭМ ранее в статье? Это по сути и есть его реализация, но без лишних движений!

Пример реализации БЭМ библиотекой Stitches

Плюс третий. Способ реализации stitches.dev исключает возможность использования функций и при этом повышает читаемость кода. В своих же бенчмарках (https://stitches.dev/docs/benchmarks) авторы сравнивают подход вариантов против полностью генерируемых стилей. Это не особо показывает реальную производительность, зато подсвечивает, что в других библиотеках достаточно легко можно допустить ошибку и потерять эффективность. 

Стайлинг для UI-кита

Если библиотека позволяет стилизовать компоненты, это не значит, что ее будет удобно использовать в UI-ките. В вопросах типового стайлинга stitches.dev в целом понятная и удобная система, но давайте рассмотрим ее в рамках работы UI-кита — это непосредственно относится к нашей задаче.

Создание темы в Stitches

Stitches со стартового гайда предлагает создать файл конфигурации, в котором можно определить тему и общие параметры: 

theme: Объявление токенов темы, которые самостоятельно соотносятся с CSS-параметрами.

media: Объявление брейкпоинтов для адаптивной верстки.

utils: Создание уникальных функций-утилит для удобства добавления стилей.

prefix: Добавление префикса для избежания конфликтов.

themeMap: Добавление соотношений кастомных токенов к CSS-параметрам.

Странно, что в других библиотеках темизация спрятана где-то в разделах Advanced, тогда как это уже практически стандарт для сайтов и приложений.

Токены в CSS variables

На этом приятные моменты не закончились. Токены в этой библиотеке — не что-то эфемерное, как переменные в том же SASS, а использование нативных CSS variables!

Достаточно на верхнем уровне передать значения новых токенов, а сами компоненты уже нативным CSS сменят свои стили.

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

Пример обертки компонентов в Stitches

С кастомизацией у stitches также нет проблем — это крайне нативный процесс, происходящий с помощью обертки styled или добавления CSS-пропса. При этом любой вариант кастомизации поддерживает работу токенов.

Добавление темной темы в Stitches

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

Соответственно, для продуктовых команд можно предложить кастомизировать токены темы, не опасаясь их потерять. А предоставив доступ к низкоуровневому API компонентов, мы расширяем возможности кастомизации нашего набора компонентов до предела. Но об этом в другой раз :)

Итого, при использовании этого подхода получаем следующее:

Плюсы:

  1. Типизация и подсказки.

  2. Вариативность стилей по канонам БЭМ.

  3. Нативный стайлинг внутри JS/TS.

  4. Темы переключаются на лету.

  5. Отличная документация.

  6. SSR поддерживается, но есть нюансы.

Из минусов — рендер стилей только после загрузки JS.

Наш опыт

Более чем за шесть месяцев работы со stitches мы столкнулись только с одной проблемой — случайным порядком вариантов при генерации классов. Например, компонент Input имеет булевые варианты Error и Disabled, и стили первого перекрывали стили второго. А нам нужен как раз обратный эффект.

Решением этой проблемы стало банальное добавление !important или использование compoundVariants. Но в будущем рассчитываем, что ситуацию исправит добавление поддержки CSS layers в библиотеку.

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