# Atomic Recipe
Colocated multi-variant styles with cva. Every variant emits as atomic CSS when you define the recipe.
An atomic recipe is a `cva` call: base styles and named variants, next to the component. Every variant turns into CSS as
soon as you save the file, whether or not your app ever calls it.
```tsx
button({ size: 'sm', visual: 'solid' })
```
That returns a class string, one class per style: `d_inline-flex bg_blue.500 px_3 ...` Panda's `cva` generates these
classes for you, so you never write them by hand.
The name `cva` comes from [Class Variance Authority](https://cva.style/), a popular library for the same pattern.
Panda's version resolves a variant like `bg: 'blue.500'` against a color token, not a raw CSS value.
Sharing this across a whole design system instead? Use a [config recipe](/docs/recipes/config-recipe).
## Defining the recipe
Put whatever every look shares in `base`. Name each look under `variants`. Set `defaultVariants` so `button()` alone
still renders a fully styled button.
```tsx
import { cva } from '../styled-system/css'
const button = cva({
base: {
display: 'inline-flex',
alignItems: 'center',
rounded: 'md',
fontWeight: 'semibold'
},
variants: {
visual: {
solid: { bg: 'blue.500', color: 'white' },
outline: { borderWidth: '1px', borderColor: 'blue.500', color: 'blue.500' }
},
size: {
sm: { px: '3', py: '1.5', fontSize: 'sm' },
lg: { px: '5', py: '3', fontSize: 'md' }
}
},
defaultVariants: {
visual: 'solid',
size: 'sm'
}
})
```
If you need a style that only applies when two variants combine, say `visual: 'outline'` with `size: 'lg'`, reach for
[compound variants](/docs/recipes/compound-variants).
## Boolean variants
Some variants are just an on/off switch, like `outlined` below, no named options needed. Panda converts between the two.
```ts
const button = cva({
base: { rounded: 'md' },
variants: {
outlined: {
true: { borderWidth: '1px', borderColor: 'gray.300' }
}
}
})
```
```tsx
```
## Using the recipe
Atomic recipes like `button()` only knows the variants you gave it. It does not take any `css` or `className`.
```tsx
export function Button({ visual, size, children }) {
return
}
```
This means an extra key like `color: 'red.500'` gets ignored. To merge styles, use [`raw()`](#using-raw) or wrap the
recipe with [`styled()`](/docs/recipes/styled-factory).
## CSS Output
You do not need a literal `button({ size: 'lg' })` somewhere in the app for `lg` to exist.
Everything in `base`, every variant option, every `compoundVariants` entry lands in `@layer utilities` as atomic
classes, one class per CSS property, shared across your app:
```css
@layer utilities {
.d_inline-flex {
display: inline-flex;
}
.bg_blue\.500 {
background-color: var(--colors-blue-500);
}
.px_3 {
padding-inline: var(--spacing-3);
}
}
```
That only works because Panda reads your source. It doesn't run it. A ternary still works, since both sides are literal
values sitting right there:
```ts
const button = cva({
variants: {
size: {
// Panda emits CSS for both 'xs' and 'sm', even though only one runs at a time
sm: { fontSize: isCompact ? 'xs' : 'sm' }
}
}
})
```
It breaks down for a config built at runtime, or pulled from something that isn't a plain object literal: Panda can't
see inside a function call, so none of it reaches your CSS.
```ts
// none of these variants show up in the CSS, Panda can't see inside the function
const button = cva(getButtonConfig(theme))
```
`cva` has no responsive variant props. `button({ size: { base: 'sm', md: 'lg' } })` does not resolve. See
[responsive variants](/docs/recipes/responsive-variants).
## Using raw()
Say you want the recipe's styles plus one override, such as a red color for an error state. You can't pass that to
`button()`. Instead, use `button.raw()`. This gives you the resolved style object instead of a class string, so you can
drop it into `css()` next to the override.
```tsx
import { css, cx } from '../styled-system/css'
css(button.raw({ size: 'sm' }), { color: 'red.500' })
```
A wrapper that takes a `css` prop does the same thing:
```tsx
export function Button({ visual, size, css: cssProp, children }) {
return
}
```
`cx()` only joins class strings. It doesn't resolve conflicts the way `css()` does. Reach for it when the extra class
doesn't touch a property the recipe already sets, like a conditional class:
```tsx
cx(button({ size: 'sm' }), isDisabled && css({ opacity: '0.5' }))
```
If the extra style does conflict with something in the recipe, like `color` above, use `raw()` and `css()` instead so
Panda can actually merge them.
## Exported types
`RecipeVariant` makes every key required. `RecipeVariantProps` makes them optional, which is what you want for JSX
props.
```ts
import { cva, type RecipeVariant, type RecipeVariantProps } from '../styled-system/css'
type ButtonVariant = RecipeVariant
// { visual: 'solid' | 'outline'; size: 'sm' | 'lg' }
type ButtonProps = RecipeVariantProps
// { visual?: 'solid' | 'outline'; size?: 'sm' | 'lg' }
```
## Helpers
`cva` returns a function, plus a handful of extra properties:
| Helper | What it does |
| -------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `variantKeys` | The variant names, e.g. `['visual', 'size']` |
| `variantMap` | Variant names mapped to their possible values, e.g. `{ visual: ['solid', 'outline'], size: ['sm', 'lg'] }` |
| `splitVariantProps(props)` | Splits an object into `[variantProps, restProps]`, so a wrapper can tell `size` apart from `onClick` |
| `getVariantProps(props)` | Fills in `defaultVariants` for any variant key you didn't pass |
| `raw(props)` | Resolves to the style object, covered above under [Using raw()](#using-raw) |
| `merge(other)` | Combines two `cva` recipes, including their variants and compound variants |
| `config` | The original object you passed to `cva()` |
`splitVariantProps` is the one you want in a wrapper, so `onClick` never looks like a variant. `variantMap` is handy for
Storybook `argTypes`.
Wrapping a headless or third-party component? Use [Recipe context](/docs/recipes/recipe-context) instead.
## See also
- [Recipes overview](/docs/recipes/overview)
- [Merging styles](/docs/styling/merging-styles)
- [Styled factory](/docs/recipes/styled-factory)
- [Recipe context](/docs/recipes/recipe-context)
- [Compound variants](/docs/recipes/compound-variants)
- [cva() reference](/docs/reference/css#cva)
---
_This content is automatically generated from the official Panda CSS documentation._