xstyle vs className vs style Props in Astryx Components: The Complete Guide
Astryx components accept three distinct styling mechanisms: xstyle for compile-time StyleX objects with pseudo-class support, className for arbitrary CSS class strings appended after internal styles, and style for inline React objects that override all other values.
In the facebook/astryx repository, the component architecture exposes a three-layer styling system that balances design-system consistency with flexible overrides. Understanding how xstyle, className, and style interact is essential for optimizing bundle size via static analysis while maintaining compatibility with utility-first CSS workflows.
Understanding the xstyle Prop
The xstyle prop is not a CSS class name string. Instead, it accepts typed StyleX objects created via stylex.create(), enabling compile-time optimizations that standard CSS cannot achieve.
Compile-Time Processing and Static Analysis
When you pass an xstyle value, the component forwards it to stylex.props() for merging at compile-time. This processing enables dead-code elimination, automatic deduplication of styles, and deterministic class name generation. According to the styling documentation in packages/cli/docs/styling.doc.mjs (lines 38-44), the xstyle prop supports pseudo-classes like :hover and :focus-visible expressed directly inside the stylex.create object.
Only values produced by stylex.create() are accepted—attempting to pass a raw string or inline style object triggers a TypeScript error. This type safety ensures design-token consistency across the application.
Implementation in Core Components
In packages/core/src/Table/TableHeader.tsx, the component forwards received xstyle values to the StyleX compiler:
export function TableHeader({ref, children, xstyle}: TableHeaderProps) {
return (
<thead
ref={ref}
{...mergeProps(themeProps('table-header'), stylex.props(xstyle))}
>
{children}
</thead>
);
}
This pattern appears throughout the codebase, including in packages/core/src/ToggleButton/ToggleButtonGroup.tsx where the xstyle?: StyleXStyles type is defined, and packages/lab/src/Stepper/Stepper.tsx which demonstrates passing xstyle through nested component hierarchies.
How className Works in Astryx
The className prop accepts arbitrary strings of CSS class names, including Tailwind utilities or custom framework classes. Unlike xstyle, these values are not processed by the StyleX compiler.
Classes passed via className are appended after the component’s internal class list, as documented in packages/cli/docs/styling.doc.mjs (lines 30-33). This ordering allows utility-first classes to layer on top of the component’s built-in token classes without interfering with the design system’s base styles.
The style Prop and Specificity
The style prop accepts plain React style objects (e.g., {margin: 8, backgroundColor: 'red'}). These inline styles are merged after both xstyle and className, giving them the highest specificity in the cascade.
Because inline styles win on any overlapping property, use this prop for one-off pixel values or dynamic calculations where defining a CSS class would be excessive.
Processing Order and Specificity Hierarchy
Astryx applies styles in the following sequence, with each layer potentially overriding the previous:
xstyle– StyleX rules compiled to deterministic class namesclassName– Arbitrary CSS classes appended to the class liststyle– Inline styles applied last, winning specificity conflicts
This hierarchy means a style={{backgroundColor: 'pink'}} declaration will override a background color set via xstyle, regardless of the order props are declared in JSX.
Practical Code Examples
Component-Specific Overrides with xstyle
import * as stylex from '@stylexjs/stylex';
import {Card, Button} from '@astryxdesign/core';
const overrides = stylex.create({
card: {
maxWidth: 400,
marginBlock: 16,
':hover': { '@media (hover: hover)': { boxShadow: '0 4px 12px rgba(0,0,0,0.1)' } },
},
saveButton: {
alignSelf: 'flex-end',
},
});
export function Example() {
return (
<>
<Card xstyle={overrides.card}>Content</Card>
<Button label="Save" xstyle={overrides.saveButton} />
</>
);
}
Source: packages/cli/docs/styling.doc.mjs (lines 48-57)
Utility-First Styling with className
<div className="flex gap-3 p-4 bg-surface text-primary rounded-container">
<Button label="Save" variant="primary" />
<Button label="Cancel" variant="secondary" />
</div>
Inline Tweaks with style
<Card
xstyle={overrides.card}
style={{border: '2px solid var(--color-accent)'}}
>
Content
</Card>
Combining All Three Props
<Button
xstyle={overrides.saveButton}
className="hover:scale-105 transition-transform"
style={{backgroundColor: 'var(--color-success)'}}
/>
In this example, the button receives StyleX token-based styling (xstyle), Tailwind utility classes for hover effects (className), and an explicit inline background color (style). The final CSS cascade follows the order: StyleX → Tailwind → Inline.
When to Use Each Prop
| Situation | Recommended Prop |
|---|---|
| Override design tokens (spacing, color, border radius) while preserving pseudo-class support | xstyle |
| Add layout utilities or integrate external CSS frameworks (Tailwind) | className |
| Apply single, one-off pixel values or CSS properties not covered by the design system | style |
Summary
xstyleaccepts StyleX objects fromstylex.create(), enabling compile-time deduplication, dead-code elimination, and pseudo-class support viastylex.props()processing.classNameappends arbitrary CSS class strings after internal component classes, supporting utility-first frameworks like Tailwind.styleapplies inline React style objects with highest specificity, overriding any conflicting properties fromxstyleorclassName.- The processing order is
xstyle→className→style, with each subsequent layer winning specificity conflicts.
Frequently Asked Questions
Can I use Tailwind CSS with Astryx components?
Yes. Pass Tailwind utility classes to the className prop. These classes are appended after the component’s internal StyleX-generated classes, allowing utilities to override layout and spacing while preserving the component’s design-token base styles.
Why does my inline style override my xstyle definition?
Because the style prop is merged after xstyle in the component’s internal logic. Inline styles have higher specificity in CSS than class-based styles, so a style={{backgroundColor: 'red'}} declaration will always win over a background color defined in your xstyle object, regardless of source order.
Can I pass a CSS class string to the xstyle prop?
No. The xstyle prop is strictly typed to accept only StyleX objects created via stylex.create(). Passing a string like xstyle="my-class" results in a TypeScript compilation error. Use the className prop for arbitrary CSS class strings.
Does xstyle support responsive breakpoints and media queries?
Yes. StyleX supports media queries and container queries within the stylex.create() definition. You can define responsive behavior by nesting @media rules inside your style objects, which the compiler transforms into proper CSS selectors while maintaining type safety.
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 →