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(...) is an identity function: it returns its input, and serves as a hint to the compiler that the value is a
style object. Recipes have no .raw() — to combine a recipe with other styles, see the cx() sections below.
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
Compose an inline cva recipe with a style object through cx(). The compiler merges both StyleSets before allocating
classes, so the later argument wins.
import { css, cx, cva } from 'styled-system/css'
const buttonStyles = cva({
base: {
bg: 'blue',
border: '1px solid black',
},
variants: {
size: {
small: { fontSize: '12px' },
},
},
})
const className = cx(
// the selected recipe declarations
buttonStyles({ size: 'small' }),
// add the override styles
css({ bg: 'red', color: 'white' }),
)
// => 'bg_red bd_1px_solid_black fs_12px c_white'
A recipe's .raw() is not a way to do this: the compiler erases the recipe, so reading buttonStyles.raw fails the
build with runtime-binding.
Merging sva + css styles
The same works for a slot of an inline sva recipe.
import { css, cx, sva } from 'styled-system/css'
const buttonStyles = sva({
slots: ['root'],
base: {
root: {
bg: 'blue',
border: '1px solid black',
},
},
variants: {
size: {
small: {
root: { fontSize: '12px' },
},
},
},
})
const className = cx(
buttonStyles({ size: 'small' }).root,
// add the override styles
css({ bg: 'red', color: 'white' }),
)
// => 'bg_red bd_1px_solid_black fs_12px c_white'
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.