Как мы пришли к компилируемому CSS и почему это выглядит логичным финалом ветки CSS-in-JS
Немного контекста
Фронтенд почти никогда не стоит на месте. Мы регулярно упираемся в ограничения инструментов, пробуем новые подходы и время от времени возвращаемся к тем же идеям — но уже с другим пониманием задач.
Когда-то обычного CSS было достаточно. Затем появились препроцессоры, методологии вроде BEM, позже — CSS-modules. Следующим шагом стало естественное желание держать стили рядом с компонентами, ведь именно вокруг компонентов строятся современные интерфейсы.
Так постепенно сформировался CSS-in-JS.
На первый взгляд это выглядело очень логично: стили находятся рядом с кодом компонента, появляются темы, переменные, токены, удобная работа с вариантами.
Но со временем начали проявляться и издержки.
Во многих библиотеках стили создавались прямо во время рендера. Это означало дополнительные аллокации, работу рантайма и усложнение серверного рендеринга. В небольших проектах это почти не заметно, но в крупных приложениях влияние становилось ощутимым.
Stitches стал попыткой решить часть этих проблем. Он сохранил привычный DX и сильно уменьшил объём работы в рантайме. Однако развитие проекта остановилось, а требования к фронтенду продолжили расти.
На этом фоне появился StyleX. И на практике он ощущается не просто как ещё одна библиотека, а как движение в сторону компиляции и более предсказуемой модели работы со стилями.
Разберёмся, как мы к этому пришли.
Как развивался CSS-in-JS
Если разложить эволюцию по этапам, картина получится примерно такой.
Поколение 1. Классический CSS-in-JS
Типичные представители — styled-components, Emotion и похожие решения.
Что они дали:
стили рядом с компонентами
темы и токены
удобный DX
Но за удобством скрывались и проблемы:
стили создаются во время рендера
появляются дополнительные аллокации
усложняется серверный рендеринг
увеличивается время первого рендера
Пока проект небольшой, это редко становится критичным. Но по мере роста приложения подобные вещи начинают влиять на производительность.
Поколение 2. Zero-runtime. Появляется Stitches
Stitches предложил компромисс: сохранить удобство CSS-in-JS, но минимизировать работу в рантайме.
Пример:
import { styled } from '@stitches/react'; const Button = styled('button', { padding: '8px 16px', borderRadius: '6px', background: 'dodgerblue', color: 'white', variants: { variant: { outline: { background: 'transparent', color: 'dodgerblue', border: '1px solid dodgerblue' } } } });
Код получался аккуратным и хорошо типизировался. Variants закрывали значительную часть задач дизайн-систем.
Но технологический контекст продолжал меняться. Появились React Server Components, новые сборщики, более агрессивные оптимизации. И поскольку Stitches перестал активно развиваться, библиотека постепенно начала отставать от новых требований.
Поколение 3. Компиляторы
Следующий шаг оказался довольно логичным: если можно выполнить большую часть работы на этапе сборки, зачем делать её во время рендера?
Вместо того чтобы генерировать стили в рантайме, сборщик анализирует код заранее и подготавливает CSS.
Именно в этой парадигме появился StyleX.
Что такое StyleX и почему это работает
StyleX появился внутри Meta. Он решал вполне типичную для больших компаний задачу: огромная кодовая база, десятки команд, строгие требования к стабильности и размеру бандла.
Основная идея довольно простая:
стили описываются в JS или TypeScript
во время сборки они превращаются в атомарный CSS
рантайм остаётся минимальным
ограничения встроены прямо в API
Простейший пример:
import * as stylex from "@stylexjs/stylex"; import Counter from "./Counter"; import { colors } from "./tokens.stylex"; export default function App() { return ( <div {...stylex.props(styles.container)}> <h1 {...stylex.props(styles.h1)}>Welcome to the StyleX Playground!</h1> <Counter /> </div> ); } const styles = stylex.create({ container: { backgroundColor: colors.bg, padding: 32, margin: 16, borderRadius: 12, }, h1: { color: colors.text, textAlign: "center", fontSize: 32, fontWeight: 600, textShadow: "0 1px 2px rgb(0 0 0 / 10%)", margin: 0, }, });
После компиляции компонент превращается в обычную разметку с атомарными классами:
import * as stylex from "@stylexjs/stylex"; import Counter from "./Counter"; import { colors } from "./tokens.stylex"; export default function App() { return <div className="xmosvpw x15fnm84 xtj3y72 x4pepcl"> <h1 className="xkrvn0m x2b8uid x1s3cmhv x1s688f xr43fr2 x1ghz6dp">Welcome to the StyleX Playground!</h1> <Counter /> </div>; }
А сами стили оказываются в статическом CSS:
@layer priority1, priority2, priority3, priority4, priority5; @layer priority1{ :root, .xtxip2{--xlv11z:light-dark(#fff, #000);--x1gdch5:light-dark(#222, #bbb);} } @layer priority2{ .x1ghz6dp{margin:0} .xtj3y72{margin:16px} .x15fnm84{padding:32px} .xe8ttls{padding:8px} } @layer priority3{ .x4pepcl{border-radius:12px} .x12oqio5{border-radius:4px} .xng3xce{border-style:none} .x1hr3lfm{padding-inline:16px} } @layer priority4{ .x6s0dn4{align-items:center} .xjyslct{appearance:none} .x2t57m4{background-color:#279} .xmosvpw{background-color:var(--xlv11z)} .xkrvn0m{color:var(--x1gdch5)} .x1awj2ng{color:white} .x1ypdohk{cursor:pointer} .x78zum5{display:flex} .x1s3cmhv{font-size:32px} .x1s688f{font-weight:600} .xl56j7k{justify-content:center} .x2b8uid{text-align:center} .xr43fr2{text-shadow:0 1px 2px rgb(0 0 0 / 10%)} .x1g2r6go{transition-duration:.1s} .x11xpdln{transition-property:transform} .xx0mf6y:focus-visible{background-color:#39b} .xe19aex:hover{background-color:#39b} .x1jplu5e:active{transform:scale(.95)} } @layer priority5{ .xvundu9{min-width:54px} }
Компилятор заранее выполняет:
дедупликацию
генерацию атомарных классов
оптимизацию
статический анализ
Это даёт предсказуемость, которой часто не хватает рантайм-решениям.
Stitches и StyleX. Похожая цель, разные результаты
Обе библиотеки пытаются решить похожую проблему — уменьшить накладные расходы CSS-in-JS. Но делают это по-разному.
Stitches
library-first подход
упор на DX и variants
минимальный, но всё же существующий рантайм
проект заморожен
StyleX
compiler-first
строгие ограничения
атомарный CSS
статичность по умолчанию
Например, вариант со variants.
Stitches:
const Title = styled('h1', { fontSize: '20px', variants: { size: { large: { fontSize: '32px' } } } });
StyleX:
import * as stylex from '@stylexjs/stylex'; const styles = stylex.create({ title: { fontSize: 20 }, large: { fontSize: 32 } }); export function Title({ large }: { large?: boolean }) { return ( <h1 {...stylex.props(styles.title, large && styles.large)}> Заголовок </h1> ); }
Кода здесь немного больше, но поведение становится полностью явным и хорошо анализируется компилятором.
Как StyleX смотрится на фоне конкурентов
Чтобы понять, где именно находится StyleX, полезно сравнивать не столько конкретные библиотеки, сколько подходы, из которых они выросли. Тогда становится понятнее, какие задачи каждое решение пытается закрыть.
Vanilla Extract и другие compile-time решения
StyleX — не единственный инструмент, который переносит работу со стилями на этап сборки.
Например, Vanilla Extract тоже генерирует CSS во время компиляции и позволяет описывать стили прямо в TypeScript. Это аккуратное и довольно зрелое решение, которое хорошо интегрируется со сборщиками и практически не добавляет рантайма.
Но философия у него немного другая.
Vanilla Extract даёт разработчику больше свободы. Он не навязывает строгую модель организации стилей и позволяет строить архитектуру вокруг существующих инструментов и процессов команды.
StyleX действует иначе.
Он сознательно вводит ограничения: заставляет работать через токены, поощряет атомарность и делает упор на предсказуемость.
Это не случайное решение — оно направлено на масштабируемость больших кодовых баз.
Tailwind
Tailwind — это utility-first подход, и именно в этом его сила.
Он даёт:
очень быстрый старт
простую ментальную модель
атомарные классы
хороший JIT-компилятор, который удаляет неиспользуемые стили
Но у такого подхода есть и обратная сторона.
Стили пишутся строками классов, и JSX довольно быстро начинает разрастаться. В больших компонентах такие строки могут превращаться в шум.
Кроме того, дизайн-система в Tailwind в основном живёт в конфигурации, а не в коде компонентов. Типизация минимальная, а правила легко обойти, если команда не следит за дисциплиной.
Это не означает, что Tailwind плох — он отлично работает во многих проектах. Но по мере роста приложения всё более важными становятся контроль и единообразие.
styled-components
Когда-то styled-components стал настоящей революцией.
Он дал разработчикам:
локальные стили
декларативный API
темы
хорошую интеграцию с React
Однако у такого подхода есть фундаментальные ограничения.
Стили генерируются во время рендера, что добавляет нагрузку на клиент. Появляется рантайм, усложняется серверный рендеринг, возникают дополнительные аллокации.
Для небольших проектов это редко становится проблемой. Но в больших продуктовых системах такие вещи начинают накапливаться.
StyleX
StyleX не пытается напрямую «заменить» Tailwind или styled-components. Он вырос из другой задачи — управления стилями в очень больших кодовых базах.
Фактически он объединяет несколько идей:
атомарность классов (как у Tailwind)
декларативность CSS-in-JS
преимущества compile-time решений
И при этом добавляет строгие правила.
Главные из них:
compile-time вместо runtime
статический анализ
ограничения, встроенные в API
централизованные токены
Пример с токенами:
import * as stylex from '@stylexjs/stylex'; const tokens = stylex.defineVars({ primary: '#1d4ed8', danger: '#dc2626' }); const styles = stylex.create({ button: { backgroundColor: tokens.primary, color: 'white' } });
Токены остаются в системе координат:
видны в коде
типизированы
участвуют в проверках
попадают в итоговый CSS
Их сложнее «подменить», «обойти» или превратить проект в хаос.
StyleX — это не про скорость любой ценой. Это про долгую жизнь проекта и дисциплину.
И всё это выглядело бы просто как ещё один виток в эволюции стайлинга, если бы не один новый фактор. Всё чаще код, включая стили, пишут не люди.
Когда стили пишет AI: где тут место StyleX
Есть ещё одно изменение, которое постепенно начинает влиять на выбор инструментов.
Всё чаще код — включая стили — пишется не только разработчиками, но и ассистентами. Генераторы интерфейсов, Copilot-подобные инструменты и различные AI-помощники всё активнее участвуют в повседневной работе.
Это уже не экспериментальная история. Сегодня ассистенты регулярно генерируют:
фрагменты разметки
состояние компонентов
варианты стилизации
целые UI-блоки
Но у таких инструментов есть одна особенность: они оптимизируют конкретный участок кода «здесь и сейчас». Их задача — быстро получить рабочий результат, а не поддерживать долгосрочную архитектуру проекта.
Из-за этого со стилями часто происходит одна и та же вещь.
Они начинают:
дублироваться
расходиться по разным вариантам
постепенно терять связь с дизайн-системой
И здесь не так важно, используете ли вы Tailwind, CSS-modules или CSS-in-JS. Если инструмент допускает любую форму записи стилей, ассистент будет активно этим пользоваться.
Со временем это приводит к накоплению шума: похожие стили появляются в разных местах, токены подменяются значениями, а вариации компонентов начинают жить собственной жизнью.
StyleX пытается уменьшить пространство для таких ошибок.
Не за счёт «умности» инструмента, а за счёт ограничений.
В нём:
стили описываются декларативно
токены проходят через типизацию
атомарные классы автоматически переиспользуются
лишние значения сложнее протащить в код
Ошибки перестают растворяться внутри JSX. Они становятся заметны — и часто обнаруживаются ещё на этапе сборки.
StyleX не делает ассистентов умнее и не гарантирует идеальную архитектуру. Но он сужает пространство возможных ошибок.
И в мире, где часть кода генерируется автоматически, это неожиданно важное свойство.
Кому StyleX действительно подходит
Важно понимать: StyleX не пытается стать универсальным решением для всех проектов.
Он особенно хорошо чувствует себя в тех ситуациях, где на первый план выходит долгосрочная поддерживаемость.
Например:
большая кодовая база
несколько команд, работающих над одним продуктом
строгая дизайн-система с токенами
требования к размеру бандла
активное использование SSR и React Server Components
В т��ких проектах главная проблема редко связана со скоростью написания первого компонента. Гораздо важнее становится контроль: чтобы стили оставались предсказуемыми и не превращались в хаотичный набор правил.
StyleX помогает именно в этом.
В небольших проектах картина может быть совсем другой.
Для лендингов, прототипов или небольших приложений он действительно может показаться избыточным.
В таких случаях Tailwind, CSS-modules или лёгкие CSS-in-JS решения часто дают более быстрый результат.
Кроме того, важно учитывать, что StyleX всё ещё развивается. Экосистема постепенно формируется, инструменты вокруг него продолжают появляться, а API местами ещё может меняться.
Поэтому разумный подход — не сразу внедрять его во всё приложение, а сначала познакомиться с инструментом.
Например:
попробовать его на нескольких компонентах
проверить, как он ложится на существующую дизайн-систему
сравнить результат с текущим решением
Если проект долгоживущий и вы уже сталкивались с ростом сложности стилей, StyleX может оказаться очень полезным. Если такой проблемы пока нет — можно спокойно наблюдать за развитием инструмента.
Если вы уже используете Stitches — стоит ли мигрировать на StyleX
Отдельный случай — проекты, которые уже давно используют Stitches.
Stitches по-прежнему остаётся удобным и приятным инструментом. Его API хорошо продуман, variants закрывают множество задач дизайн-систем, а DX остаётся одним из лучших среди CSS-in-JS решений.
Но при этом развитие проекта фактически остановилось.
А новые требования — React Server Components, более строгий SSR, уменьшение рантайма — появляются всё чаще.
Это не означает, что проект нужно срочно переписывать.
На практике наиболее разумная стратегия обычно выглядит так:
не переписывать существующий код
не трогать стабильные модули
постепенно использовать StyleX в новых компонентах
В таких ситуациях переход происходит естественно: новые части системы начинают строиться на compile-time подходе, а старые продолжают работать без изменений.
Пример ниже не стоит воспринимать как инструкцию по миграции — это просто иллюстрация того, как может выглядеть код.
Было:
const Card = styled('div', { padding: 16, borderRadius: 8, background: 'white', variants: { shadow: { true: { boxShadow: '0 6px 20px rgba(0,0,0,.1)' } } } });
Стало:
import * as stylex from '@stylexjs/stylex'; const styles = stylex.create({ card: { padding: 16, borderRadius: 8, backgroundColor: 'white' }, highlighted: { boxShadow: '0 6px 20px rgba(0,0,0,.1)' } }); export function Card({ highlighted }: { highlighted?: boolean }) { return ( <div {...stylex.props(styles.card, highlighted && styles.highlighted)} /> ); }
Иногда переписывание стилей всё равно происходит — например, при обновлении компонентов или рефакторинге дизайн-системы.
И в такие моменты имеет смысл смотреть на StyleX не как на надстройку поверх существующего решения, а как на новую основу.
Но если текущая система работает стабильно и не создаёт проблем, мигрировать только ради самого факта перехода обычно не имеет смысла.
Итог
Stitches сыграл важную роль в развитии CSS-in-JS. Он показал, что стили можно держать рядом с компонентами, не жертвуя производительностью и удобством разработки.
Но за последние несколько лет приоритеты во фронтенде постепенно изменились. Всё больше внимания уделяется не только DX, но и архитектуре на длинной дистанции: размеру бандла, работе с серверным рендерингом, предсказуемости поведения инструментов.
На этом фоне многие решения начинают двигаться в сторону compile-time.
Часть работы, которая раньше выполнялась в рантайме, переносится на этап сборки.
StyleX хорошо вписывается в эту тенденцию.
Он сочетает несколько идей, которые раньше существовали отдельно:
атомарный CSS
декларативное описание стилей в коде
строгие ограничения, встроенные в API
генерацию CSS во время сборки
При этом важно помнить, что StyleX пока не стал универсальным стандартом. Экосистема вокруг него ещё формируется, а API продолжает развиваться.
Поэтому относиться к нему лучше не как к инструменту, который нужно срочно внедрить, а как к направлению, которое стоит изучить.
Если ваш проект уже сталкивается с ростом сложности стилей, большим количеством компонентов и несколькими командами — StyleX может оказаться очень полезным.
Если же текущий стек работает стабильно, торопиться с миграцией нет необходимости.
Но наблюдать за этим подходом точно имеет смысл.
Похоже, что всё больше фронтенд-инструментов движутся именно в сторону compile-time стилей.
