concepts
merging styles

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 text_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.

💡

All .raw(...) signatures are identity functions that return the same value as the input, but serve as a hint to the compiler that the value is a style object.

// 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 text_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 atomic 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 border_1px_solid_black color_white font-size_12px'

Merging sva + css styles

The same technique can be used to merge an atomic 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: {
      root: {
        small: { 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 border_1px_solid_black color_white font-size_12px'

Merging config recipe and style object

Due to the fact that the generated styles of a config recipe are saved in the @layer recipe cascade layer, they can be overridden with any atomic styles. Use the cx function to achieve that.

💡

The utilties layer has more precedence than the recipe layer.

import { css, cx } from 'styled-system/css'
import { button } from 'styled-system/recipes'
 
const className = cx(
  // returns the resolved class name: `button button--size-small`
  button({ size: 'small' }),
  // add the override styles
  css({ bg: 'red' }) // => 'bg_red'
)
 
// => 'button button--size-small bg_red'

Passing styles to custom components

Expose css, a *Css prop, or css.raw() on a custom prop name. Panda extracts styles at the call site.

💡

Note: Set jsxFramework in your config for JSX extraction.

const cardStyles = css.raw({
  bg: 'red',
  color: 'white'
})
 
function Card({ title, description, css: cssProp }) {
  return (
    <div className={css(cardStyles, cssProp)}>
      <h1>{title}</h1>
      <p>{description}</p>
    </div>
  )
}
 
// usage
<Card title="Hello World" description="This is a card component" css={{ bg: 'blue' }} />

Props ending in Css work the same way — useful for named slots:

function Card({ title, description, rootCss, bodyCss }) {
  return (
    <div className={css(cardStyles, rootCss)}>
      <h1>{title}</h1>
      <p className={css(bodyCss)}>{description}</p>
    </div>
  )
}
 
<Card rootCss={{ bg: 'blue' }} bodyCss={{ color: 'white' }} />

For any other prop name, wrap the value in css.raw():

function Card({ title, description, style }) {
  return (
    <div className={css(cardStyles, style)}>
      <h1>{title}</h1>
      <p>{description}</p>
    </div>
  )
}
 
<Card title="Hello World" description="This is a card component" style={css.raw({ bg: 'blue' })} />

See custom component props.