How StyleX is Integrated into Astryx Components: A Complete Technical Guide
Astryx integrates StyleX by importing the library into component files, defining static style objects with stylex.create(), and applying them at render time using stylex.props() merged with design-system utilities like mergeProps() and themeProps().
The Astryx component library leverages StyleX as its zero-runtime CSS-in-JS engine to generate static, type-safe styles. This integration pattern appears consistently across the facebook/astryx repository, enabling components to consume shared design tokens while supporting dynamic runtime overrides through CSS variables.
Setting Up StyleX Imports in Component Files
Every styled component in Astryx begins by importing the StyleX library. In packages/lab/src/SVGIcon/SVGIcon.tsx, the pattern starts with:
import * as stylex from '@stylexjs/stylex';
This import provides access to the core APIs including stylex.create, stylex.props, and stylex.defineVars. Components then import design tokens from the centralized theme definition to ensure consistency with the broader design system.
Defining Design Tokens with stylex.defineVars
Centralized theme values live in packages/core/src/theme/tokens.stylex.ts. Here, Astryx establishes design tokens using stylex.defineVars to create typed CSS custom properties:
import {colorVars} from '@astryxdesign/core/theme/tokens.stylex';
// Referencing tokens in component logic
const iconColor = colorVars['--color-icon-primary'];
These variables propagate throughout the component tree, allowing a single source of truth for colors, spacing, and typography. When StyleX compiles, these definitions generate the actual CSS custom properties while maintaining TypeScript autocomplete support.
Creating Component Styles Using stylex.create
Component-specific styles are declared using stylex.create, which returns an object mapping keys to generated class names. The implementation in SVGIcon.tsx demonstrates this pattern:
const styles = stylex.create({
root: {
display: 'inline-flex',
width: iconVars['--icon-size']
},
});
const colorStyles = stylex.create({
primary: {
color: colorVars['--color-icon-primary']
},
secondary: {
color: colorVars['--color-icon-secondary']
},
});
Each property object describes static CSS rules. StyleX extracts these at build time to generate atomic CSS classes, ensuring zero runtime overhead for style calculation.
Applying Styles with stylex.props and mergeProps
At render time, components transform StyleX objects into React-compatible props using stylex.props. Astryx combines this with mergeProps from @astryxdesign/core/utils to unify design-system metadata, StyleX classes, and consumer-provided props:
import {mergeProps} from '@astryxdesign/core/utils';
import {themeProps} from '@astryxdesign/core/utils';
function Button({variant = 'primary', ...rest}) {
return (
<button
{...mergeProps(
themeProps('button', {variant}),
stylex.props(buttonStyles.base, buttonStyles[variant]),
)}
{...rest}
>
Click me
</button>
);
}
The themeProps utility injects data attributes like data-variant and data-size for theming hooks, while stylex.props returns a { className, style } object that mergeProps intelligently combines without className collisions.
Handling Dynamic Values and CSS Variables
While StyleX generates static classes, Astryx supports runtime overrides by extracting raw CSS variable names from tokens. The SVGIcon component demonstrates this when handling custom strokeWidth values:
function SVGIcon({strokeWidth, ...props}) {
const strokeVar = (iconVars['--icon-stroke-width'] as string).replace(
/^var\((.+)\)$/,
'$1',
);
const override = strokeWidth
? {[strokeVar]: String(strokeWidth)}
: undefined;
return (
<svg
{...stylex.props(iconStyles.root, colorStyles[props.color])}
style={override}
// ...other props
/>
);
}
This technique preserves the static class-based architecture while allowing per-instance customization through inline styles that override CSS variables.
Advanced Patterns: Markers and Conditional States
For complex selectors like hover states, focus rings, or ancestor-dependent styling, Astryx uses StyleX markers and conditional APIs. The Table component in packages/core/src/Table/table.stylex.ts implements this with stylex.defineMarker and stylex.when.ancestor:
const tableRowMarker = stylex.defineMarker();
const rowStyles = stylex.create({
row: {
borderBottom: '1px solid',
borderColor: colorVars['--color-border'],
},
lastRow: {
borderBottom: 'none',
},
});
// Applying conditional styles based on ancestor state
stylex.when.ancestor(':last-child', tableRowMarker)
These utilities generate scoped, specific CSS selectors without leaking state across component boundaries or requiring runtime JavaScript for state detection.
Animation and Utility Functions
Astryx extends StyleX integration to animations and fallback handling. In packages/core/src/hooks/useEntryAnimation.ts, the library defines keyframes:
const fadeIn = stylex.keyframes({
from: { opacity: 0 },
to: { opacity: 1 },
});
const styles = stylex.create({
animated: {
animationName: fadeIn,
animationDuration: '300ms',
},
});
The stylex.firstThatWorks utility provides CSS fallbacks for properties like display, ensuring cross-browser compatibility while maintaining the static extraction model.
Summary
- StyleX is imported as
* as stylex from '@stylexjs/stylex'in every component requiring styles, establishing the foundation for zero-runtime CSS generation. - Design tokens are centralized in
packages/core/src/theme/tokens.stylex.tsusingstylex.defineVars, enabling type-safe access to theme values via CSS variables. - Style objects are created with
stylex.create(), defining static rules that compile to atomic CSS classes at build time. - Style application occurs through
stylex.props(), which merges multiple style objects and is combined withmergeProps()andthemeProps()to integrate design-system metadata. - Dynamic overrides are achieved by extracting CSS variable names from tokens and applying them via inline styles, allowing runtime customization without sacrificing static extraction.
- Conditional states utilize
stylex.defineMarkerandstylex.when.*APIs to handle pseudo-selectors and ancestor-based styling with scoped, compile-time generated CSS.
Frequently Asked Questions
What is StyleX and why does Astryx use it instead of other CSS-in-JS libraries?
StyleX is a zero-runtime CSS-in-JS library developed by Meta that generates static CSS files at build time. Astryx uses StyleX because it provides type-safe style objects, atomic CSS generation for optimal bundle sizes, and full compatibility with server-side rendering without requiring runtime style injection. Unlike traditional CSS-in-JS solutions that execute JavaScript in the browser to compute styles, StyleX extracts all CSS during compilation, resulting in better performance and no hydration mismatches.
How can I override Astryx component styles at runtime if StyleX generates static CSS?
Override specific CSS variables extracted from design tokens. Astryx components expose customizable properties through CSS variables defined in tokens.stylex.ts. By extracting the raw variable name from a token (removing the var() wrapper) and applying it to the element's style prop, you can modify specific values like colors or stroke widths while keeping the static class names intact. This pattern appears in SVGIcon.tsx where the strokeWidth prop dynamically updates the --icon-stroke-width variable.
What is the difference between stylex.create and stylex.defineVars in Astryx?
stylex.create defines component-specific style objects that map to CSS class names, while stylex.defineVars creates design tokens that compile to CSS custom properties. Use stylex.create in component files to define layouts, spacing, and component-specific aesthetics. Use stylex.defineVars in theme files like packages/core/src/theme/tokens.stylex.ts to establish reusable values (colors, spacing scales) that multiple components can reference and override at the theme level.
How does Astryx handle hover, focus, and responsive states with StyleX?
Astryx uses stylex.defineMarker combined with stylex.when.* APIs to handle conditional states. Instead of inline event handlers or runtime state classes, Astryx defines markers that StyleX uses to generate specific CSS selectors at compile time. For example, stylex.when.ancestor(':last-child', tableRowMarker) generates CSS that applies styles when a row is the last child of its parent. This approach maintains the zero-runtime guarantee while supporting complex pseudo-selectors and media queries through static CSS generation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →