12k
All articles

Panda CSS vs StyleX

Сравните Panda CSS и StyleX: узнайте, как токены, предотвращение конфликтов стилей, сборка, темы и динамические значения влияют на выбор.

OpenReplay Team
OpenReplay Team
Panda CSS vs StyleX

Panda CSS и StyleX компилируют стили в статический атомарный CSS на этапе сборки и не отправляют в браузер никакого рантайма для стилизации. Различаются они задачей, ради которой создавались: Panda заставляет соблюдать дизайн-систему, а StyleX не даёт стилям одного компонента конфликтовать со стилями другого.

Обе библиотеки попадают в шорт-лист, когда команда хочет уйти со styled-components или Emotion, но сохранить типизированные стили рядом с компонентами. При этом их README звучат почти одинаково. В этой статье описано единственное структурное различие между ними, показан один и тот же компонент Button в обоих вариантах и разобрано, как это различие влияет на рабочий процесс, темизацию и динамические значения. Общую картину можно найти в статье о состоянии CSS-in-JS в 2026 году.

Ключевые выводы

  • Panda CSS и StyleX извлекают стили в атомарный CSS на этапе сборки, поэтому ни одна из них не внедряет стили в браузере во время рендеринга.
  • Panda делает дизайн-токены в panda.config.ts единым источником истины и генерирует на их основе типизированные хелперы css(), cva() и styled().
  • В StyleX каждый компонент определяет свои стили через stylex.create(), а stylex.props() объединяет их так, что для каждого свойства побеждает последний переданный стиль.
  • Ни одна из библиотек не может превратить значение, известное только во время выполнения, в атомарный класс. StyleX передаёт такие значения через CSS-переменные, а документация Panda предлагает использовать рецепты, CSS-переменные или data-атрибуты.
  • Panda подходит команде, у которой интерфейс расходится с дизайн-системой, StyleX — большой кодовой базе, где постоянный баг — утечка стилей. Многим проектам не нужна ни та, ни другая.

Что общего у Panda CSS и StyleX

Panda CSS и StyleX — библиотеки CSS-in-JS с нулевым рантаймом (zero-runtime). Вы пишете объекты стилей на TypeScript, на этапе сборки они извлекаются, и браузер получает обычную таблицу стилей из узкоспециализированных атомарных классов. Рантайм-библиотеки, такие как styled-components и Emotion, работают иначе: они генерируют и внедряют стили с помощью JavaScript во время рендеринга. Panda и StyleX делают всю эту работу до того, как код попадёт в продакшен. Более раннюю версию того же противопоставления разбирает статья Linaria vs styled-components.

Поскольку на выходе получается статический CSS, ни одной из библиотек не нужны ни провайдер контекста, ни механизм внедрения стилей во время рендеринга. Поэтому обе работают с React Server Components: при рендеринге на сервере или клиенте никакой код стилизации не выполняется. В атомарном выводе каждый класс задаёт одно свойство, поэтому размер таблицы стилей растёт пропорционально числу уникальных объявлений, а не числу компонентов.

Panda CSS: дизайн-система прежде всего

Panda CSS считает дизайн-систему единым источником истины. Токены, определённые в panda.config.ts, управляют этапом кодогенерации, который создаёт типизированные хелперы css(), cva() и styled() для остальной кодовой базы. Panda разработана командой, стоящей за Chakra UI, и опыт создания библиотеки компонентов здесь хорошо заметен.

Команда panda codegen создаёт в проекте каталог styled-system/. Компоненты импортируют хелперы из этого каталога, а типы в нём генерируются из ваших токенов. Если brand — цветовой токен, то для bg: 'brand' работает автодополнение. С опцией конфигурации strictTokens «сырое» шестнадцатеричное значение становится ошибкой типов.

Рецепты превращают это в API компонентов. cva() принимает базовый стиль base, именованные варианты variants и значения по умолчанию defaultVariants и возвращает функцию, которая сопоставляет пропсы вариантов с именами классов. Дизайн-система определяет, какие варианты существуют, а компоненты выбирают из этого списка.

StyleX: конфликты стилей прежде всего

StyleX считает конфликты стилей ключевой проблемой. Каждый компонент определяет собственные объекты стилей с помощью stylex.create(), а stylex.props() детерминированно объединяет их: если два стиля задают одно и то же свойство, побеждает последний переданный. Порядок в таблице стилей и специфичность селекторов роли не играют. StyleX — открытый проект Meta, и на нём построены дизайн-системы, например Astryx.

const styles = stylex.create({
  a: { color: 'red' },
  b: { color: 'blue' },
})
stylex.props(styles.a, styles.b) // blue
stylex.props(styles.b, styles.a) // red

Правила объединения в StyleX распространяются и на сокращённые свойства. В разделе Thinking in StyleX объясняется, что конфликт между сокращённым и полным свойством всегда разрешается одинаково: по умолчанию marginTop переопределяет margin независимо от того, какое из них передано первым. Компиляцию выполняет Babel-плагин StyleX, а официальные интеграции с бандлерами и PostCSS построены поверх него.

Один и тот же Button в двух вариантах

Компоненты Button на Panda CSS и StyleX ниже предоставляют один и тот же проп intent. Разница в том, где принимаются решения о стилях.

// Panda: panda.config.ts defines theme.extend.tokens.colors.brand = { value: '#2563eb' }
import { cva } from '../styled-system/css'

const button = cva({
  base: { px: '4', py: '2', borderRadius: 'md', fontWeight: 'semibold' },
  variants: {
    intent: {
      primary: { bg: 'brand', color: 'white' },
      ghost: { bg: 'transparent', color: 'brand' },
    },
  },
  defaultVariants: { intent: 'primary' },
})

export function Button({ intent, ...props }) {
  return <button className={button({ intent })} {...props} />
}
// StyleX
import * as stylex from '@stylexjs/stylex'

const styles = stylex.create({
  base: { paddingInline: 16, paddingBlock: 8, borderRadius: 6, fontWeight: 600 },
  primary: { backgroundColor: '#2563eb', color: 'white' },
  ghost: { backgroundColor: 'transparent', color: '#2563eb' },
})

export function Button({ intent = 'primary', style, ...props }) {
  return <button {...props} {...stylex.props(styles.base, styles[intent], style)} />
}

В варианте на Panda bg: 'brand' проходит проверку типов только потому, что brand есть в конфигурации и кодогенерация создала соответствующий тип. В варианте на StyleX каждый вариант — отдельный объект, а style потребителя передаётся последним. Везде, где он задаёт то же свойство, что и базовый стиль или вариант, побеждает значение потребителя, и бороться со специфичностью не приходится. Хелперы Panda ограничивают, какие значения может использовать разработчик. Правило объединения StyleX ограничивает, как стили одного компонента могут переопределять стили другого.

Практические последствия выбора Panda CSS или StyleX

Разница в происхождении проявляется в трёх повседневных аспектах: как устроена сборка, как работает темизация и что происходит со значениями, известными только во время выполнения.

КритерийPanda CSSStyleX
Этап сборкиКодогенерация создаёт styled-system/; CSS извлекает PostCSS-плагин или CLIBabel-плагин компилирует вызовы stylex.create
ТемизацияТокены в конфигурацииdefineVars плюс createTheme
Значения времени выполненияНе извлекаются; используйте рецепты, CSS-переменные, data-атрибутыПередаются через CSS-переменные

Рабочий процесс: кодогенерация vs плагин компилятора

В Panda между конфигурацией и компонентами находится сгенерированный артефакт, поэтому изменение токена требует повторной генерации styled-system/. В StyleX сгенерированного каталога нет. Babel-плагин преобразует каждый модуль там, где он написан, а конфигурация плагина определяет, как разрешаются импорты и переменные.

Темизация: токены vs переменные

В Panda тема — это набор токенов: измените brand в конфигурации, и при следующей сборке изменятся все места его использования. В StyleX темизация — механизм переопределения поверх стилей отдельных компонентов. stylex.defineVars объявляет переменные в файле .stylex.ts, а stylex.createTheme создаёт переопределения, которые применяются к элементу-предку:

// tokens.stylex.ts
import * as stylex from '@stylexjs/stylex'
export const colors = stylex.defineVars({ brand: '#2563eb' })

// app.tsx
import * as stylex from '@stylexjs/stylex'
import { colors } from './tokens.stylex'

const dark = stylex.createTheme(colors, { brand: '#60a5fa' })
<div {...stylex.props(dark)}>{children}</div>

API темизации StyleX работают только после настройки опции unstable_moduleResolution в Babel-плагине.

Динамические значения: где zero-runtime становится неудобным

Экстрактор, работающий на этапе сборки, видит только значения, записанные в исходном коде. Значение, известное лишь во время выполнения, например выбранный пользователем цвет, ни в одной из библиотек не может стать атомарным классом. В руководстве Panda по динамической стилизации объясняется, что экстрактор пропускает всё, что не может прочитать из исходного кода, и предлагается использовать рецепты, CSS-переменные или атрибуты data-*. StyleX обрабатывает динамические стили иначе: компилятор генерирует фиксированный класс, ссылающийся на CSS-переменную, а значение времени выполнения записывается в эту переменную на элементе.

Panda CSS vs StyleX: какая у вас проблема?

Выбирайте, исходя из своей проблемы, а не из списка возможностей. Если интерфейс небольшой команды постоянно расходится с дизайн-системой, это ровно та проблема, для которой создана Panda: токены становятся типами, а значения вне системы перестают компилироваться. Если в большой кодовой базе стили одного компонента постоянно протекают в другой, это ровно та проблема, для которой создан StyleX: каждый компонент владеет своими стилями, а результат определяется порядком объединения в месте вызова.

У многих проектов нет ни той, ни другой проблемы. Им хватит CSS Modules или utility-фреймворка, которым нужно меньше инструментов сборки, чем любой из этих библиотек. Сравнение двух других подходов к стилизации — в статье Tailwind vs Sass.

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

Часто задаваемые вопросы

Чем отличаются состояния hover и медиазапросы в Panda CSS и StyleX?

В StyleX псевдоклассы и медиазапросы указываются внутри значения свойства в виде объекта условий, и в этом объекте обязательна запись default, которой можно присвоить null. Исключение — псевдоэлементы, например ::placeholder: они записываются отдельным ключом на верхнем уровне пространства имён. Panda принимает условия внутри свойства, как в bg: { base: 'red.500', _hover: 'red.700' }, или в виде объектов условий, используя встроенные имена вроде _hover и mobile-first-ключи брейкпоинтов вроде md и lg.

Может ли StyleX стилизовать дочерние элементы так же, как произвольные селекторы в Panda?

Не напрямую. StyleX запрещает произвольные селекторы и стилизацию на расстоянии, тогда как css() в Panda принимает произвольные селекторы, например '& > *'. В StyleX 0.16.0 появились API stylex.when (ancestor, descendant, anySibling, siblingBefore и siblingAfter). Вы помечаете один элемент с помощью stylex.defaultMarker(), и другой элемент меняет стили, когда помеченный элемент находится в состоянии hover, focus или соответствует атрибуту. Варианты descendant, anySibling и siblingAfter зависят от CSS-селектора :has(). Ещё один задокументированный способ — задать переменную defineVars на родительском элементе.

Выполняется ли stylex.props() в браузере?

Иногда, и только как небольшой шаг объединения. Компилятор StyleX превращает вызовы stylex.create() в статический атомарный CSS на этапе сборки, а если стили создаются и используются в одном файле, он может полностью удалить вызов stylex.props(). Когда стили пересекают границы файлов, stylex.props() выполняется как функция, которая объединяет скомпилированные объекты стилей слева направо и возвращает пропсы className и style для React. Она никогда не генерирует и не внедряет CSS в браузере. Для фреймворков, отличных от React, таких как Solid, Svelte, Qwik и Vue, StyleX также предоставляет stylex.attrs().

В чём разница между strictTokens и strictPropertyValues в Panda CSS?

strictTokens относится к свойствам, привязанным к токенам, таким как color, bg и borderColor: если опция включена, в них принимаются только значения токенов. strictPropertyValues относится к фиксированному списку свойств с известными ключевыми значениями, таким как display, position и overflow: если опция включена, Panda выбрасывает ошибку, когда значение не входит в число этих ключевых слов. Обе опции включаются в panda.config.ts. В любом случае обёртка «сырого» значения в квадратные скобки, как в [123px], позволяет пропустить его без ошибки TypeScript.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.