Мы в Яндекс Лавке завершили одну из самых масштабных фронтенд-миграций: перевели сотни файлов
*.css.ts с Linaria на CSS Modules. Ни один продуктовый поток не остановился, визуальная регрессия обуздана в допустимых пределах, сборки стали проще, а поддержка стилей — предсказуемее. Но главное — мы автоматизировали большую часть работы с помощью LLM и сегодня расскажем, как это устроено под капотом.🈶 Исторически часть интерфейсов мы оформляли через Linaria в файлах
*.css.tsЭто давало декларативность и типизацию рядом с компонентом, но добавляло слой препроцессинга и зависимость от специфики интеграции в сборку. И с ростом продукта накопились проблемы:
🟡 Сложный пайплайн. Babel/TS/плагины, генерация артефактов стилей, непростой порядок применения
🟡 Флейки в CI. Препроцессинг падал на пограничных кейсах
🟡 Высокий порог входа. Новым разработчикам и сторонним командам было сложно разбираться со спецификой сборки
CSS Modules хорошо решают эти проблемы, вписываются в стандартный CSS-пайплайн, а также облегчают работу дизайнерам и QA.
🈶 Как мигрировать и не сломать продакшен
Мы поставили себе определённые условия:
🟡 Визуальный паритет. Интерфейс должен выглядеть идентично до и после миграции
🟡 Инкрементальность. Мигрируем пакеты и файлы малыми партиями, чтобы не затрагивать соседние модули
🟡 Контроль качества. В каждом PR мы проводим обязательную визуальную проверку ключевых сценариев
Перед стартом мы подготовили инфраструктуру: включили поддержку
*.module.css в сборке и CI, синхронизировали линтеры и оформили дизайн-токены через CSS-переменные.🈶 Сердце миграции — автоматизация с помощью LLM
Ручной перевод сотен файлов занял бы очень много времени. Так что мы построили автоматизированный пайплайн:
🟡 Драйвер. Внутренняя CLI-утилита, которая получала список файлов и управляла процессом
🟡 Исполнители. grok-code-fast-1 для быстрой обработки, GPT-5 для сложных кейсов
🟡 Чанкование. Батчи по 50 файлов с параллелизмом 4–8 задач
🟡 Quality Gates. Статический анализ, сборка, E2E-тесты и скриншотные сравнения
Некорректные файлы автоматически откатывались в ручную корзину.
🈶 Самый сложный этап — заставить LLM генерировать предсказуемый код. Потребовалось несколько этапов:
🟡 Мы удалили лишние JS-конструкции из файлов css.ts в полуавтоматическом режиме с помощью агента
🟡 Здесь мы генерировали CSS-переменные из дизайн-токенов, а ещё написали ESlist-плагин, чтобы завалидировать эти переменные, и VSCode-плагин для автокомплита
🟡 После мы создавали промпты и итеративно их улучшали, когда натыкались на различные корнер-кейсы миграции с Linaria на CSS-модули
🔜 Например, использование
@custom-media вместо инлайн-брейкпойнта//былоimport {breakpoints} from 'tokens'
export const bg = css`
@media (min-width: ${breakpoints.mobile}){
...
}
//стало
@media (--breakpoints-mobile){
...
}🔜 Или описание мапы с классами в файле компонента вместо файла css.ts с переименованием самих классов
//было
import {breakpoints} from 'tokens'
export const variants = {
small: css`
font-size: 12px;
`,
medium: css`
font-size: 14px;
`
}
// стало
// name.module.css
.variants-small{
font-size: 12px;
}
.variants-medium{
font-size: 12px;
}
// компонент.tsx
import * as c from 'name.module.css'
const variants = {
small: c['variants-small'],
medium: c['variants-medium']
}Все корнер-кейсы требуют подробного и чёткого описания, чтобы получать устойчивый результат от агента.
🈶 Что мы получили в результате:
🟡 −40% времени сборки. За счёт отказа от препроцессинга Linaria
🟡 Предсказуемость ревью. Стили ближе к CSS, и легче подключать внешние команды
🟡 Ускорение онбординга. CSS Modules знаком большинству фронтендеров
🟡 Чище архитектура. Меньше JS-ветвлений, больше CSS-переменных
Миграция завершена, но развитие архитектуры продолжается. Уже сейчас мы работаем над атомизацией стилей на уровне билда и стабилизацией порядка их применения к элементам DOM-дерева.
Подписывайтесь:
💬 @Yandex4Frontend
