Дизайн-система поверх чужой библиотеки компонентов

Админка Gravity Sales собрана на Material UI, но между библиотекой и продуктом лежит своя обвязка в два уровня. Расскажу, из чего она и зачем.

Первый уровень - тема. Палитра ступенями, типографика и переопределения для тех компонентов MUI, что используются как есть - кнопки, поля, чекбоксы, свитчи, табы, тултипы, чипы. Сейчас таких переопределений около двадцати. Там же свои варианты типографики, добавленные через расширение типов MUI. Тогда variant="tableHeader" не строка наугад, а вариант, который проверяет компилятор.

Второй уровень - свои компоненты, их около тридцати. Часть из них обёртки над MUI. Селект с плейсхолдером, кнопка, которая умеет рендериться ссылкой Next.js, текстовое поле со своим лейаутом и анимацией подсказки. Часть - то, чего в библиотеке нет вообще. Ввод кода и цвета, телефон и время, нижний лист для мобильных, загрузка файлов, текст с обрезкой по ширине. Токены при этом живут в CSS-переменных, а не только в объекте темы.

Чтобы граница не размывалась, прямой импорт из @mui/material запрещён линтером - правило no-restricted-imports с подсказкой брать компонент из общего слоя. Без него обвязка живёт до первого дедлайна. Дёрнуть компонент напрямую всегда быстрее, и дизайн-система тихо превращается в папку с примерами.

Зачем так, а не напрямую MUI.

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

Обёртка позволяет обновлять библиотеку, не превращая это в отдельный проект. С форком так не выйдет.

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

Отдельная часть системы - справочник токенов, который я написал для работы с макетами. В нём таблицы соответствия - такой кегль даёт такой вариант типографики, такая высота кнопки даёт такой размер, плюс правило пересчёта для скриншотов Figma, которые выгружаются в двойном разрешении. Писал его для себя, а пользуется им в основном coding-агент. По скриншоту макета он берёт вариант из системы, а не сочиняет пиксели. Это ровно та часть работы, где AI даёт скорость. Раскладку по токенам делает он, решения и сверку с макетом оставляю себе.

До этого в VK я собирал общую UI-библиотеку поверх VKUI со Storybook - её использовал сайт «Студии».

Что вынес из двух заходов. Границу «что уходит в библиотеку, а что живёт в продукте» лучше проводить рано, переносить потом дороже. Обёртка лучше форка. И дизайн-система перестаёт быть документацией, когда её варианты типизированы - тогда неправильный вариант просто не собирается.