Virtual Color
Panda allows you to create a virtual color or color placeholder in your project.
The colorPalette property is how you create virtual colors.
import { css } from '../styled-system/css'
const className = css({
colorPalette: 'blue',
bg: 'colorPalette.100',
_hover: {
bg: 'colorPalette.500'
}
})This will translate to the blue.100 background color and blue.500 background color on hover.
Virtual colors are useful when creating easily customizable components.
How Palettes Work
Every color group in your theme is a palette. The examples on this page use this theme:
panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
tokens: {
colors: {
blue: {
100: { value: '#dbeafe' },
500: { value: '#3b82f6' },
700: { value: '#1d4ed8' }
},
gray: {
100: { value: '#f3f4f6' },
500: { value: '#6b7280' },
700: { value: '#374151' }
}
}
},
semanticTokens: {
colors: {
blue: {
solid: {
bg: { value: '{colors.blue.500}' },
fg: { value: 'white' }
}
},
gray: {
solid: {
bg: { value: '{colors.gray.500}' },
fg: { value: 'white' }
}
}
}
}
}
})Base and semantic tokens under the same name form one palette, so this theme produces four palettes. Each maps every
token below it to a virtual colorPalette.* token:
blueandgray:colorPalette.100,colorPalette.500,colorPalette.700,colorPalette.solid.bg,colorPalette.solid.fgblue.solidandgray.solid:colorPalette.bg,colorPalette.fg
Use a top-level palette to reach all of its tokens:
css({ colorPalette: 'blue', bg: 'colorPalette.solid.bg', color: 'colorPalette.solid.fg' })Or use a nested group as the palette, so the virtual tokens start from that group:
css({ colorPalette: 'blue.solid', bg: 'colorPalette.bg', color: 'colorPalette.fg' })Default Values
A DEFAULT token makes the bare colorPalette token resolve to it:
panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
semanticTokens: {
colors: {
brand: {
DEFAULT: { value: '{colors.blue.500}' },
hover: { value: '{colors.blue.700}' }
}
}
}
}
})css({ colorPalette: 'brand', bg: 'colorPalette', _hover: { bg: 'colorPalette.hover' } })Usage
With Recipes
You can also use virtual colors with recipes.
import { cva } from '../styled-system/css'
const button = cva({
base: {
padding: 4
// you can also specify a default colorPalette in the `base` recipe key
// colorPalette: 'blue',
},
variants: {
variant: {
primary: { color: 'colorPalette.500' }
}
},
defaultVariants: { variant: 'primary' }
})With Color Modes
You can also use virtual colors with different conditions, such as color modes.
import { css, cva, cx } from '../styled-system/css'
const someButton = cva({
base: { padding: 4 },
variants: {
variant: {
primary: {
bg: { base: 'colorPalette.500', _dark: 'colorPalette.100' },
color: { base: 'white', _dark: 'gray.700' }
}
}
},
defaultVariants: { variant: 'primary' }
})
export const App = () => {
return (
<>
<div className="light">
<button className={cx(css({ colorPalette: 'blue' }), someButton())}>Click me</button>
<button className={cx(css({ colorPalette: 'gray' }), someButton())}>Click me</button>
</div>
<div className="dark">
<button className={cx(css({ colorPalette: 'blue' }), someButton())}>Click me</button>
<button className={cx(css({ colorPalette: 'gray' }), someButton())}>Click me</button>
</div>
</>
)
}With Opacity Modifiers
Add /opacity to a virtual color in any color property:
import { css } from '../styled-system/css'
const className = css({
colorPalette: 'blue',
bg: 'colorPalette.500/50',
color: 'colorPalette.700'
})To use it inside another value, like a shadow or a gradient, store it in a CSS variable with the token reference syntax:
import { css } from '../styled-system/css'
const className = css({
colorPalette: 'blue',
'--ring': '{colors.colorPalette.500/50}',
boxShadow: '0 0 0 2px var(--ring)'
})Pregenerating Palettes
Use the staticCss option in the config to pre-generate values for the colorPalette property. This is useful when you
want to use a color palette that can be changed at runtime (e.g. in Storybook knobs).
Learn more about static css generation.
panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
staticCss: {
css: [
{
properties: { colorPalette: ['blue', 'gray'] }
}
]
}
})Then in your code, you can design components that use the colorPalette property:
import { useState } from 'react'
import { css } from '../styled-system/css'
function ButtonShowcase() {
const [colorPalette, setColorPalette] = useState('blue')
return (
<div>
<select value={colorPalette} onChange={e => setColorPalette(e.currentTarget.value)}>
<option value="blue">Blue</option>
<option value="gray">Gray</option>
</select>
<button
className={css({
bg: 'colorPalette.100',
color: 'colorPalette.700',
colorPalette
})}
>
Click me
</button>
</div>
)
}Configuration
Use the colorPalette property in your theme to choose which palettes are generated.
Include Palettes
Use include to list the palettes you want:
panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
colorPalette: {
include: ['gray']
}
}
})Only gray is a palette now, and it keeps all of its tokens, including the nested ones:
css({ colorPalette: 'gray', bg: 'colorPalette.solid.bg', color: 'colorPalette.100' })Nested groups are palettes too. Include them with a glob pattern:
panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
colorPalette: {
include: ['gray.*'] // gray and gray.solid
}
}
})Exclude Palettes
Use exclude to drop palettes. Excluding a palette also drops its nested groups:
panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
colorPalette: {
exclude: ['blue'] // gray and gray.solid remain
}
}
})exclude wins over include:
panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
colorPalette: {
include: ['gray', 'blue'],
exclude: ['blue'] // only gray
}
}
})Keep Only Top-Level Palettes
Every nested group is a palette, so a theme with many nested groups per color (gray.solid, gray.subtle,
gray.surface, …) can produce hundreds of palette names. With strictTokens, that union can make TypeScript report
"Expression produces a union type that is too complex to represent" on components with spread props.
Exclude every nested name to keep only the top-level palettes:
panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
colorPalette: {
exclude: ['*.*'] // blue and gray, but not blue.solid or gray.solid
}
}
})Every virtual token still works through the top-level palette:
css({ colorPalette: 'gray', bg: 'colorPalette.solid.bg', color: 'colorPalette.100' })Disable Palettes
To turn off color palette generation entirely, set enabled to false:
panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
colorPalette: {
enabled: false
}
}
})