/design-sync отучает Claude Design выдумывать кнопки

Попросите Claude Design нарисовать экран, и вместо вашего Button он соберёт кнопку из ручных классов вроде px-4 py-2 rounded bg-blue-600. Выглядит она достаточно похоже, чтобы её одобрить, но разработчику всё равно придётся пересобирать каждую деталь. В разборе на Nitayneeman показано, как команда /design-sync из Claude Code меняет такие двойники на настоящие компоненты и сколько мелочей для этого нужно настроить.
Коротко
- Команда /design-sync собирает из репозитория скомпилированную копию библиотеки компонентов со стилями, шрифтами и копией React и после вашего подтверждения загружает её в Claude Design как дизайн-систему.
- Перед загрузкой синк открывает каждое превью в Chromium, снимает скриншот и оценивает его. Со Storybook превью сверяются с историями, и в примере автора на трёх компонентах shadcn все девять историй получили оценку match.
- Зеркало остаётся односторонним снимком: изменения в коде не попадают в Claude Design до следующего запуска. Утилиты разметки в Tailwind v4 приходится явно добавлять через @source inline(...).
Если вы не следили: Claude Design встроен прямо в Claude и превращает описание словами в экран, прототип, слайды или одностраничник. Работать с ним можно в обычном диалоге, на холсте claude.ai/design или из Claude Code командой /design, а дорабатывать результат комментариями к элементам и правкой текста. Из коробки он не знает вашу дизайн-систему: React-компоненты, токены (именованные цвета и отступы) и типографику.
Зеркало содержит бандл, стили, шрифты и копию React
Синк создаёт внутри Claude Design зеркало библиотеки. Исходников в нём нет. Там лежат один скомпилированный бандл со всеми компонентами, таблица стилей, шрифты и копия React, чтобы компоненты рендерились сами. Каждый компонент получает папку с типами, по которым Claude узнаёт пропсы и варианты, отдельную страницу превью, которая показывается карточкой, и сгенерированную инструкцию. Сверху лежит README с вашими правилами использования.
Настройки команда берёт из папки .design-sync/ в репозитории. Готовое зеркало появляется в аккаунте в разделе Settings > Design systems, и Claude может пользоваться им в любом диалоге, включая Claude Code. На Pro и Max оно личное. На Team и Enterprise публикация делает его общим для организации, а админы Enterprise могут ограничить круг публикующих, назначить систему по умолчанию или удалить её.
Компонент без экспорта во входном файле до Claude Design не доедет
Пример автора намеренно минимальный: проект на Vite с React и TypeScript, Tailwind v4 в виде плагина Vite, три компонента shadcn (Button, Card, Input) и Storybook. Попутно автор предупреждает, что старые инструкции добавляют в tsconfig параметр baseUrl, а TypeScript 6 считает его устаревшим и роняет сборку.
Синк собирает один входной файл, а shadcn такого не создаёт, потому что рассчитан на приложение, которое импортирует каждый компонент по пути. Автор пишет src/index.ts вручную и реэкспортирует три компонента вместе с хелпером cn() для склейки классов. Компонент, которого нет в этом файле, просто не попадает в Claude Design, и никто об этом не предупреждает.
Второе условие касается определений типов. Через них синк находит компоненты, а без них не находит ничего и выбрасывает все истории. Поэтому package.json указывает на dist/index.d.ts, а сами типы генерирует команда сборки из конфига.
Tailwind v4 кладёт в CSS только классы из исходников, и разметку приходится добавлять вручную
Превью в Claude Design нужен настоящий CSS-файл. Storybook с @tailwindcss/vite его не выдаёт, потому что подгружает стили из JavaScript-чанка во время работы. Поэтому стили компилируют через Tailwind CLI в файл ds.css, который пересобирается при каждом синке и уходит в .gitignore.
Подвох в том, что скомпилированный CSS содержит только утилиты, найденные в исходниках. Три компонента дают пару сотен классов, но ни одного из тех, которыми Claude размечает экран вокруг них: grid-cols-3, gap-6, max-w-4xl. В Tailwind v4.1 для этого появился @source inline(...), который генерирует утилиты по шаблонам вроде {sm:,md:,lg:,}gap-{0,1,2,3,4,6,8,12}. Стили тяжелеют, но автор считает это дешёвой ценой.
Вторая ловушка прячется в импорте токенов. Строковый @import Tailwind v4 встраивает в итоговый файл, а @import url() оставляет внешней ссылкой. Локально работают оба варианта, но после загрузки токены остаются только у первого, и без них все компоненты shadcn приезжают без стилей.
Синк сверяет каждое превью со скриншотом истории из Storybook
Перед загрузкой синк раздаёт превью с локального сервера, открывает их в Chromium через Playwright и снимает скриншоты. Без Storybook у компонентов есть только карточки-заглушки, сравнивать не с чем, и синк ищет явные провалы: пустое превью, почти пустое или варианты, неотличимые друг от друга. Остальное Claude оценивает по рубрике, а итог проверяете вы сами.
Со Storybook истории, то есть сохранённые состояния компонента вроде основной кнопки, становятся и превью, и эталоном. Это напоминает сверку дубликата ключа с оригиналом: Claude кладёт превью и историю рядом, ставит match, close или mismatch и чинит, что может. Оценка close зачётом не считается. Синк берёт до шести историй на компонент и сопоставляет их по title, который должен совпадать с экспортированным именем: Button, а не Buttons.
Одну строку автор добавляет в .storybook/preview.tsx руками: импорт src/index.css. Без неё эталонные скриншоты тоже выходят без стилей, и зачёт означает лишь совпадение голого превью с голым эталоном. На запрос об экране входа Claude собрал его из Card, CardHeader и Button variant=\"outline\" без самодельных div.
Шрифты едут через extraFonts, правила через файл конвенций
Шрифты, импортированные из CSS, ссылаются в node_modules и загрузку не переживают. Пресет shadcn по умолчанию тянет Geist, поэтому его путь прописан в extraFonts в .design-sync/config.json: синк копирует файлы woff2 в зеркало и переписывает правила @font-face. После первого запуска там же сохраняется projectId, и повторный синк обновляет ту же дизайн-систему вместо создания новой.
Последнее поле конфига указывает на conventions.md, который автор называет самым дешёвым способом поднять качество. Превью показывают, как компоненты выглядят, а конвенции объясняют, как ими пользоваться: брать variant и size, оставлять className для разметки, собирать Card из частей, склеивать классы через cn(). Без этих правил Claude видит правильный Button и всё равно дописывает ему bg-blue-600 px-4.
На первом запуске синк сам дописывает в этот файл таблицу утилит, реально существующих в скомпилированных стилях. Таблица и блок @source inline(...) описывают один словарь и должны меняться вместе, иначе Claude пишет классы, которые ничего не делают.
Главное ограничение автор признаёт сам: зеркало остаётся снимком, а повторный синк не всегда подчищает удалённые компоненты, так что список файлов приходится проверять руками. Грейдер тоже не скажет, что идеальный скриншот снят с неправильного состояния, например с закрытого выпадающего меню. Странно, на наш взгляд, что компонент, забытый в src/index.ts, пропадает молча, хотя синк и так строит список компонентов по типам и историям.
Когда зеркало отстанет от кода. Любая правка компонента останется невидимой для Claude Design до следующего запуска /design-sync, так что повторный синк придётся встроить в собственный цикл выпуска библиотеки. Если вы держите отдельную CSS-точку входа для синка, автор советует сравнивать её с оригиналом перед каждым запуском: новое правило @layer base копия не увидит. Автоматическое обновление зеркала в разборе не описано, а обратный путь из дизайна в код идёт отдельно, через /design.
Читайте также
- 57% субагентов Claude Code наследуют Bash вызвавшего
- Toolog ведёт локальный журнал вызовов Claude Code
- Stemma компилирует CLAUDE.md и AGENTS.md из одного источника
- Portal by Spotify уводит чтение файлов из Claude Code
- Context Engineering Kit: /reflect и спецификации arc42
- ccswitch переключает аккаунты Claude Code одной командой
Комментарии
Пока никто не написал. Будьте первым.
Присоединяйтесь к разговору
Войдите через Google, чтобы оставить комментарий. Имя и аватар подставятся из вашего профиля Google, а комментарий появится после модерации.
