Skip to content

Want to skip the docs? Check out pandamastery.com - the best way to learn Panda CSS

Customization

Utilities

Add your own CSS properties, or remap existing ones to your tokens, with the utility API.

Panda ships a set of built-in utilities (bg, px, rounded, and the rest of what you already use in css()). The utility API is how you add your own on top, or change what a built-in one does. Think of it as building your own typed version of Tailwind's or Chakra's utility layer, except every value comes from your own config.

A utility takes these fields:

  • property: the real CSS property this utility maps to (borderRadius, backgroundColor, and so on).
  • className: the class name Panda generates for it. Defaults to the property name if you skip it.
  • shorthand: one alias, or an array of aliases, for the property (px for paddingInline, say).
  • values: what values the property accepts. A token category name ('radii', 'colors'), an array of literal strings, { type: 'boolean' }, or an object mapping your own keys to raw values.
  • transform: a function that takes the value and returns the CSS object to emit.

Creating a custom utility

Say you want a br shorthand that applies a border radius:

panda.config.ts

import { defineConfig } from '@pandacss/dev'
 
export default defineConfig({
  utilities: {
    extend: {
      br: {
        className: 'rounded', // css({ br: 'sm' }) → .rounded_sm
        values: 'radii', // pull values from the radii tokens
        transform(value) {
          return { borderRadius: value }
        }
      }
    }
  }
})

Run codegen so the new property shows up in your generated types:

pnpm panda codegen

br now works everywhere css() does:

import { css } from '../styled-system/css'
 
function App() {
  return <div className={css({ br: 'sm' })} />
}

And as a JSX style prop:

import { styled } from '../styled-system/jsx'
 
function App() {
  return <styled.div br="sm" />
}

Enum values

Restrict a utility to a fixed set of raw values instead of a token category. Here, borderX only accepts three widths and always sets the border color for you:

panda.config.ts

import { defineConfig } from '@pandacss/dev'
 
export default defineConfig({
  utilities: {
    extend: {
      borderX: {
        values: ['1px', '2px', '4px'],
        shorthand: 'bx', // both `bx` and `borderX` work
        transform(value, { token }) {
          return {
            borderInlineWidth: value,
            borderColor: token('colors.red.200') // resolves to the CSS variable for red.200
          }
        }
      }
    }
  }
})
import { css } from '../styled-system/css'
 
function App() {
  return <div className={css({ borderX: '2px' })} />
}

Mapped values

Map your own keys to raw CSS values instead of a plain array:

panda.config.ts

import { defineConfig } from '@pandacss/dev'
 
export default defineConfig({
  utilities: {
    extend: {
      borderX: {
        values: { small: '2px', medium: '5px' },
        shorthand: 'bx',
        transform(value, { token }) {
          return {
            borderTopWidth: value,
            borderTopColor: token('colors.gray.400')
          }
        }
      }
    }
  }
})

Now borderX: 'small' resolves to 2px, not the literal string 'small'.

Boolean values

Set values: { type: 'boolean' } for a utility that's either on or off:

panda.config.ts

import { defineConfig } from '@pandacss/dev'
 
export default defineConfig({
  utilities: {
    extend: {
      truncate: {
        className: 'truncate',
        values: { type: 'boolean' },
        transform(value) {
          if (!value) return {}
          return {
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            whiteSpace: 'nowrap'
          }
        }
      }
    }
  }
})

css({ truncate: true }) now emits the three declarations above. truncate: false emits nothing.

Starting from zero

Everything here assumes you're extending Panda's defaults with utilities.extend. If you want no built-in utilities at all and full control from the start, see minimal setup.

Edit this page on GitHubView as markdown
Last updated on