# 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._