Recipes

Recipe: Migrating from StyleX

StyleX (Meta) and traceless-style share a lot of design DNA — both are zero-runtime atomic CSS for React. The main differences:

StyleXtraceless-style
Babel plugin requiredYesNo
Bundler integrationsWebpack, Rollup, esbuild+ Vite, raw esbuild, Next.js Turbopack
Variable interpolationAllowed via stylex.create({ x: { color: token } }) (token vars only)Same — token vars / tl.cssVar only
Conditional stylesstylex.props(a, b) returns mergedtl.merge(a, b) returns merged
Dark modemanual @media (prefers-color-scheme)Auto-derived per color value (compiler-built)
RTLmanual logical propertiesAuto compiler rewrite
Contrast validationNoneBuild-time WCAG audit
LintExternalBuilt-in (no inline styles, no class strings, no Tailwind, no CSS modules)
Server ComponentsSupportedSupported

Direct API mapping

StyleXtraceless-style
stylex.create({ btn: { color: "red" } })tl.create({ btn: { color: "red" } })
stylex.props($.btn, $.primary)tl.merge($.btn, $.primary) (returns string, not {className,style})
stylex.defineVars({ brand: "#3b82f6" })tl.defineTokens({ brand: { primary: "#3b82f6" } })
stylex.createTheme(vars, { brand: "#60a5fa" })tl.createTheme("dark", { brand: { primary: "#60a5fa" } })
stylex.keyframes({ from, to })tl.keyframes("name", { from, to })
stylex.firstThatWorks("backdrop-filter", "filter")not built-in — use the standard property

Side-by-side example

StyleX

import * as stylex from "@stylexjs/stylex";

const styles = stylex.create({
  base:    { padding: 8, color: "white" },
  primary: { background: "blue" },
  danger:  { background: "red" },
});

function Button({ variant, ...props }) {
  return (
    <button {...stylex.props(styles.base, styles[variant])} {...props} />
  );
}

traceless-style

import { tl } from "traceless-style";

const $ = tl.create({
  base:    { padding: "8px", color: "white" },
  primary: { background: "blue" },
  danger:  { background: "red" },
});

function Button({ variant, ...props }: { variant: "primary" | "danger" }) {
  return (
    <button className={tl.merge($.base, $[variant])} {...props} />
  );
}

The main code-shape difference: stylex.props(...) returns { className, style } because StyleX uses inline style for CSS-variable theming. traceless-style returns a single class string (theming is done via createTheme overrides, not inline style).

Migration steps

1. Replace the import

- import * as stylex from "@stylexjs/stylex";
+ import { tl } from "traceless-style";

2. Rewrite stylex.create calls

- const styles = stylex.create({
-   btn: { padding: 8 }
- });
+ const $ = tl.create({
+   btn: { padding: "8px" }
+ });

3. Rewrite stylex.props calls

- <button {...stylex.props(styles.btn)} />
+ <button className={tl.merge($.btn)} />

(tl.merge is sufficient when there's only one input. For composition, pass multiple.)

4. Rewrite defineVars / createTheme

- export const colors = stylex.defineVars({
-   brand: "#3b82f6",
- });
- export const dark = stylex.createTheme(colors, {
-   brand: "#60a5fa",
- });
+ export const tokens = tl.defineTokens({
+   brand: { primary: "#3b82f6" },
+ });
+ export const dark = tl.createTheme("dark", {
+   brand: { primary: "#60a5fa" },
+ });

Token references at use sites:

- color: colors.brand
+ color: tokens.brand.primary

5. Drop the Babel plugin

Remove @stylexjs/babel-plugin and StyleX's webpack plugin. Add the traceless-style integration instead — see Next.js / Webpack / Vite.

6. Take advantage of features StyleX doesn't have

  • Auto-dark-mode: Most _dark: {...} blocks become unnecessary.
  • Auto-RTL: Logical properties get derived from physical ones automatically.
  • WCAG contrast: Surface color/background pairs that fail accessibility at build time.

What's similar

  • Atomic CSS semantics (one class per property:value).
  • Zero runtime style injection.
  • Server Components support.
  • Build-time class-name hashing.
  • Theme-via-CSS-variables architecture.

What's different

  • No Babel plugin, period. The hand-rolled scanner + optional SWC AST cover the same ground in less code and with no consumer-side Babel config.
  • Built-in dark-mode derivation, RTL rewriting, WCAG contrast — these are out-of-scope for StyleX core.
  • A single className string return value (instead of StyleX's {className, style} pair).

See also

See also