Migrating from Theme UI
Migrate your project from Theme UI to Bamboo.
This guide outlines the steps needed to migrate your project from Theme UI to Bamboo and highlights key design differences between the two libraries.
Here are some similarities between the two libraries.
- Both support design tokens and themes.
- Both ship layout primitives — Theme UI as components (
Box,Flex,Grid), Bamboo as pattern functions that return a class name.
Below are some of the differences between the two libraries. The one that shapes the whole migration: Bamboo has no JSX
style props and no sx prop. Every style is a css(), cva() or pattern call whose class name you put on an element.
Performance
Theme UI relies on @emotion/styled to style components. This means that every time you use the sx prop, runtime
CSS-in-JS is required to compute the styles in the browser. This can lead to performance issues in larger applications.
Bamboo's Vite compiler converts CSS-in-JS styles to static CSS at build time, avoiding runtime style computation.
Theming
In Theme UI, you need to wrap your application in a ThemeProvider component which is a wrapper around @emotion/react
theme context.
import { ThemeProvider } from 'theme-ui'
const theme = {
fonts: {
body: 'system-ui, sans-serif',
heading: '"Avenir Next", sans-serif',
},
colors: {
text: '#000',
background: '#fff',
},
}
export default function App({ Component, pageProps }) {
return (
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
)
}
In Bamboo, you don't need to wrap your application in a ThemeProvider component. Instead, you can pass the theme
object to the bamboo.config.js file.
The theme object in Bamboo is broken down into multiple parts, tokens and semanticTokens. The theme specification
also required passing the tokens as { value: XX }
bamboo.config.js
import { defineConfig } from '@bamboocss/dev'
export default defineConfig({
theme: {
extend: {
tokens: {
fonts: {
body: { value: 'system-ui, sans-serif' },
heading: { value: '"Avenir Next", sans-serif' },
},
colors: {
text: { value: '#000' },
background: { value: '#fff' },
},
},
},
},
})The sx prop
In Theme UI, you can use the sx prop to style any component when you add the jsxImportSource pragma to the top of
your file.
/** @jsxImportSource theme-ui */
export const Demo = (props) => (
<div
{...props}
sx={{
color: 'white',
bg: 'primary',
fontSize: 4,
}}
/>
)
Bamboo has no sx prop. Write the same object with the css function and pass the class name it returns. Numeric scale
steps become named tokens — the default preset's fontSizes run 2xs through 9xl.
import { css } from '../styled-system/css'
export const Demo = (props) => (
<div
{...props}
className={css({
color: 'white',
bg: 'primary',
fontSize: 'xl',
})}
/>
)
Reach for cva instead when the styles need a name and variants.
Variants
In Theme UI, variants are used to create groups of styles based on the theme. It offers variant groups in the theme for several components.
Gridmaps totheme.gridsButton,IconButtonmaps totheme.buttonsNavLink,Linkmaps totheme.linksInput,Select,Textareamaps totheme.formsHeading,Textmaps totheme.text
theme.js
export default {
colors: {
primary: '#07c',
secondary: '#30c',
accent: '#609'
},
buttons: {
primary: {
color: 'white',
bg: 'primary'
},
secondary: {
color: 'white',
bg: 'secondary'
},
accent: {
color: 'white',
bg: 'accent'
}
}
}
// Button.js
<button sx={{ variant: 'buttons.primary' }} />In Bamboo, multi-variant styles are called recipes. Under Vite, inline and configured recipes resolve into the same
globally shared declaration atoms as css(). What you choose is where the recipe lives and how its typed API is
generated; recipe identity does not affect emitted CSS identity.
Colocated with the component, using the cva function:
import { cva } from '../styled-system/css'
const buttonStyles = cva({
className: 'button',
base: {
display: 'inline-flex',
},
variants: {
variant: {
primary: { color: 'white', bg: 'primary' },
secondary: { color: 'white', bg: 'secondary' },
accent: { color: 'white', bg: 'accent' },
},
},
})
const Demo = () => <button className={buttonStyles({ variant: 'accent' })} />
Or in the theme.recipes property of the bamboo config — 'config recipes' — which shares the recipe across components
and projects and generates only the variants the project uses. You then import it from ../styled-system/recipes. See
Recipes.
Color Modes
In Theme UI, colors modes can be used to create a user-configurable light and dark mode values that are automatically applied to components depending on color mode.
theme.js
const theme = {
colors: {
primary: '#07c',
modes: {
dark: {
primary: '#0cf',
},
},
},
}
// Button.js
const Demo = () => <button sx={{ color: 'primary' }} />In Bamboo, color mode values are defined as semanticTokens in the theme. Semantic tokens change depending on the color
mode, so the call site stays an ordinary css({ color: 'primary' }).
bamboo.config.js
import { defineConfig } from '@bamboocss/dev'
export default defineConfig({
theme: {
extend: {
semanticTokens: {
colors: {
primary: { value: { base: '#07c', _dark: '#0cf' } },
},
},
},
},
})Global Styles
Theme UI offers a Global component (that wraps Emotion’s) for adding global CSS with theme-based values.
import { Global } from 'theme-ui'
export default (props) => (
<Global
styles={{
button: {
m: 0,
bg: 'primary',
color: 'background',
border: 0,
},
}}
/>
)
In Bamboo, global styles are defined in the top-level global.css property of the bamboo config — not under theme.
bamboo.config.js
import { defineConfig } from '@bamboocss/dev'
export default defineConfig({
global: {
css: {
button: {
m: 0,
bg: 'primary',
color: 'background',
border: 0,
},
},
},
})Component Styles
Theme UI offers pre-defined layout components like Box, Stack, Grid, Flex
import { Box, Grid } from 'theme-ui'
const Demo = () => (
<Grid width={[128, null, 192]}>
<Box bg="primary">Box</Box>
<Box bg="muted">Box</Box>
<Box bg="primary">Box</Box>
<Box bg="muted">Box</Box>
</Grid>
)
In Bamboo, these are called "layout patterns", or "patterns" for short. Each one is a function that returns a class name, so it composes with any element.
import { css } from '../styled-system/css'
import { grid } from '../styled-system/patterns'
const Demo = () => (
<div className={grid({ width: [128, null, 192] })}>
<div className={css({ bg: 'primary' })}>Box</div>
<div className={css({ bg: 'muted' })}>Box</div>
</div>
)