Merging Styles
Learn how to merge multiple styles without conflicts.
Merging css objects
You can merge multiple style objects together using the css function.
import { css } from 'styled-system/css'
const style1 = {
bg: 'red',
color: 'white',
}
const style2 = {
bg: 'blue',
}
const className = css(style1, style2) // => 'bg_blue c_white'
In some cases though, the style object might not be colocated in the same file as the component. In this case, you can
use the css.raw function to preserve the original style object.
css.raw(...) and a config recipe's .raw(...) are identity functions: they return their input, and serve as a hint
to the compiler that the value is a style object. cva(...).raw(...) and sva(...).raw(...) are not — they resolve
the variants and return the merged style object, which is what makes the cva example below work.
style.js
import { css } from 'styled-system/css'
export const style1 = css.raw({
bg: 'red',
color: 'white',
})
// component.js
import { css } from 'styled-system/css'
import { style1 } from './style.js'
const style2 = css.raw({
bg: 'blue',
})
const className = css(style1, style2) // => 'bg_blue c_white'Spreading css.raw objects
Added in v1.6.1
You can also spread css.raw objects within style declarations. This is particularly useful for reusing styles in
nested selectors, conditions, and complex compositions:
Child selectors
import { css } from 'styled-system/css'
const baseStyles = css.raw({ margin: 0, padding: 0 })
const component = css({
'& p': { ...baseStyles, fontSize: '1rem' },
'& h1': { ...baseStyles, fontSize: '2rem' },
})
Nested conditions
import { css } from 'styled-system/css'
const interactive = css.raw({ cursor: 'pointer', transition: 'all 0.2s' })
const card = css({
_hover: {
...interactive,
_dark: { ...interactive, color: 'white' },
},
})
Merging cva + css styles
The same technique can be used to merge an inline cva recipe and a style object.
import { css, cx, cva } from 'styled-system/css'
const overrideStyles = css.raw({
bg: 'red',
color: 'white',
})
const buttonStyles = cva({
base: {
bg: 'blue',
border: '1px solid black',
},
variants: {
size: {
small: { fontSize: '12px' },
},
},
})
const className = css(
// returns the resolved style object
buttonStyles.raw({ size: 'small' }),
// add the override styles
overrideStyles,
)
// => 'bg_red bd_1px_solid_black c_white fs_12px'
Merging sva + css styles
The same technique can be used to merge an inline sva recipe and a style object.
import { css, sva } from 'styled-system/css'
const overrideStyles = css.raw({
bg: 'red',
color: 'white',
})
const buttonStyles = sva({
slots: ['root'],
base: {
root: {
bg: 'blue',
border: '1px solid black',
},
},
variants: {
size: {
small: {
root: { fontSize: '12px' },
},
},
},
})
// returns the resolved style object for all slots
const { root } = buttonStyles.raw({ size: 'small' })
const className = css(
root,
// add the override styles
overrideStyles,
)
// => 'bg_red bd_1px_solid_black c_white fs_12px'
Merging config recipe and style object
With the Vite compiler, an analyzable cx() composes the recipe and utility StyleSets before allocating shared atoms.
Later arguments win, and losing declarations are not emitted for that composition.
import { css, cx } from 'styled-system/css'
import { button } from 'styled-system/recipes'
const className = cx(
// resolves the selected recipe declarations
button({ size: 'small' }),
// add the override styles
css({ bg: 'red' }), // => 'bg_red'
)
// => a shared atom string containing the final red background
That override works because Vite can see and compose both Bamboo StyleSets. If one argument is an arbitrary runtime
class, cx remains a string join and makes no conflict guarantee — see
Classname concatenation.
Merging within JSX component
Use statically available objects for composition. For component choices that vary at runtime, expose a finite recipe variant instead of accepting an arbitrary style object:
import { cva } from '../styled-system/css'
const card = cva({
base: { color: 'white' },
variants: {
tone: {
red: { bg: 'red.500' },
blue: { bg: 'blue.500' },
},
},
defaultVariants: { tone: 'red' },
})
function Card({ title, tone }: { title: string; tone?: 'red' | 'blue' }) {
return (
<div className={card({ tone })}>
<h1>{title}</h1>
</div>
)
}
function Demo() {
return <Card title="Hello World" tone="blue" />
}
css(base, props.css) with an open runtime prop cannot be compiled. Renaming the prop or wrapping its value in
css.raw() does not change that requirement. For values such as a dragged offset or user-selected color, use an inline
style or a CSS variable as described in dynamic styling.