Virtual Color
Bamboo 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.200',
},
})
This will translate to the blue.100 background color and blue.200 background color on hover.
Virtual colors are useful when creating easily customizable components.
Using with recipes
You can also use virtual colors with recipes.
import { css, cva, cx } 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' },
})
Using with different 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.200' },
color: { base: 'white', _dark: 'gray.900' },
},
},
},
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: 'green' }), someButton())}>Click me</button>
<button className={cx(css({ colorPalette: 'red' }), someButton())}>Click me</button>
</div>
<div className="dark">
<button className={cx(css({ colorPalette: 'blue' }), someButton())}>Click me</button>
<button className={cx(css({ colorPalette: 'green' }), someButton())}>Click me</button>
<button className={cx(css({ colorPalette: 'red' }), someButton())}>Click me</button>
</div>
</>
)
}
Semantic Virtual Colors
Semantic virtual colors gives you an ability to create a virtual color organized by category, variant and state. Hierarchically organized virtual colors are useful when creating easily customizable components.
const theme = {
extend: {
semanticTokens: {
colors: {
button: {
dark: {
value: 'navy',
},
light: {
DEFAULT: {
value: 'skyblue',
},
accent: {
DEFAULT: {
value: 'cyan',
},
secondary: {
value: 'blue',
},
},
},
},
},
},
},
}
You can now use the root button color palette and its values directly:
import { css } from '../styled-system/css'
export const App = () => {
return (
<button
className={css({
colorPalette: 'button',
color: 'colorPalette.light',
backgroundColor: 'colorPalette.dark',
_hover: {
color: 'colorPalette.light.accent',
background: 'colorPalette.light.accent.secondary',
},
})}
>
Root color palette
</button>
)
}
Or you can use any deeply nested property (e.g. button.light.accent) as a root color palette:
import { css } from '../styled-system/css'
export const App = () => {
return (
<button
className={css({
colorPalette: 'button.light.accent',
color: 'colorPalette.secondary',
})}
>
Nested color palette leaf
</button>
)
}
Note: Nested tokens require glob patterns in the colorPalette config (e.g., 'button.*') to generate proper CSS
variables.
Pregenerated Virtual Colors
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).
Extraction-only. staticCss puts the rules in the stylesheet; it does not make a call with a runtime value compile,
so the component below is rejected by the Vite compiler as dynamic. Under the compiler, declare the choice as a
recipe variant — its finite states are enumerated at build time — or set
--colors-color-palette-* from the style attribute, which is what a palette chosen from a <select> really is. See
dynamic styling.
Learn more about static css generation.
export default defineConfig({
staticCss: {
css: [
{
properties: { colorPalette: ['red', 'blue'] },
},
],
},
})
Then in your code, you can design components that use the colorPalette property:
import { css } from '../styled-system/css'
function ButtonShowcase() {
const [colorPalette, setColorPalette] = useState('red')
return (
<div>
<select value={colorPalette} onChange={(e) => setColorPalette(e.currentTarget.value)}>
<option value="red">Red</option>
<option value="blue">Blue</option>
</select>
<button
className={css({
bg: 'colorPalette.50',
color: 'colorPalette.500',
colorPalette,
})}
>
Click me
</button>
</div>
)
}
Configuration
Color palette generation is on by default and covers every color in your theme. Narrow it with theme.colorPalette:
bamboo.config.ts
import { defineConfig } from '@bamboocss/dev'
export default defineConfig({
theme: {
colorPalette: {
enabled: true,
include: ['gray', 'blue', 'red', 'green', 'button.*'],
exclude: ['red'],
},
},
})| Option | Type | Effect |
|---|---|---|
enabled | boolean | Skips color palette generation entirely when false. Defaults to true. |
include | string[] | Restricts generation to these colors. Everything else in the theme is skipped. |
exclude | string[] | Drops these colors. Applied after include, so red above is excluded despite being listed. |
| glob patterns | — | include and exclude entries accept *, e.g. 'button.*'. Nested tokens need this, or they resolve to raw paths rather than a var. |