Extraction rules
Panda reads the source as written. The tag, the prop, and the value have to be names it already knows.
Panda's compiler matches the names in your source. The tag, the prop, and the value have to be names it already knows.
The usual miss:
- You rename
ButtontoRandom - You rename
sizetocircleSize - You pass a value from state
The class shows up. The CSS does not.
Matching the tag
<Button size="lg" /> only emits the button recipe if the tag is Button, or a name you listed in jsx.
export function Random(props) {
return <button className={button(props)} />
}
<Random size="lg" />Extract sees Random, not Button. Export it as Button, or set jsx: ['Random']. That is
Tracking JSX.
cva and sva do not need a matching tag. Defining them already emits every variant.
Matching the prop
Extract reads the prop on the tag. It does not follow a rename.
Style props
There is no config that turns elementWidth into width.
function Carousel({ elementWidth, ...props }) {
return <styled.div width={elementWidth} {...props} />
}
<Carousel elementWidth="300px" />Extract sees elementWidth, not width. Use a utility name, css, or a *Css prop at the call site:
<Carousel width="300px" />
<Carousel css={{ width: '300px' }} />
<Carousel trackCss={{ width: '300px' }} />Extract reads CSS utilities when jsxStyleProps is 'all', plus
css and props that end in Css. For any other name, wrap the object in .raw() so the call site is visible:
<Card style={css.raw({ bg: 'blue' })} />See passing styles to custom components.
Recipe and pattern props
circleSize is not size. buttonSize is not size.
function CustomCircle({ circleSize = '3', ...props }) {
return <Circle size={circleSize} {...props} />
}
<CustomCircle circleSize="4" />function Button({ buttonSize, children }) {
return <button className={button({ size: buttonSize })}>{children}</button>
}
<Button buttonSize="lg" />Extract sees circleSize / buttonSize, not size. Keep the key at the call site.
cva and sva already emit every variant, so a rename there is a value problem, not a missing class. See
Matching the value.
Matching the value
A runtime value is invisible, even when the tag and prop are right.
function Button({ size, ...props }) {
return <button className={button({ size })} {...props} />
}
<Button size={size} />The wrapper looks correct. Extract sees size={size}, not size="lg". You get defaultVariants only.
<Button size="lg" />Pre-generate with staticCss, or put a literal at the call site.
See Dynamic styles and Dynamic variants.
Composition patterns
Expose a name extract already knows, and merge inside the wrapper. Don't invent elementWidth.
Using css and *Css
The wrapper takes a style object. Extract reads css and any prop that ends in Css at the call site.
function Card({ css: cssProp, ...props }) {
return <div className={css({ bg: 'red' }, cssProp)} {...props} />
}
<Card css={{ bg: 'blue' }} />Named parts use rootCss, bodyCss, and so on. Same extract rule, one object per slot.
function Card({ rootCss, bodyCss, ...props }) {
return (
<div className={css({ bg: 'red' }, rootCss)} {...props}>
<p className={css(bodyCss)} />
</div>
)
}
<Card rootCss={{ bg: 'blue' }} bodyCss={{ color: 'white' }} />Using className
The consumer already has a class. The wrapper only concatenates.
function Card({ className, ...props }) {
return <div className={cx(card(), className)} {...props} />
}
<Card className={css({ bg: 'blue' })} />Extract sees the css() at the call site. The wrapper never has to know bg.
Use css / *Css when the wrapper should merge style objects. Use className when the consumer already has a class
(a recipe, css(), or something external).
See Merging styles.