migration
stitches

Migrating from Stitches

Migrate your project from Stitches to Bamboo.

This guide helps you migrate from Stitches to Bamboo and understand the design differences between the libraries.

💡

Disclaimer: This isn't about comparing which one is best. Bamboo and Stitches are two different CSS-in-JS solutions with design decisions.

Here are some similarities between the two libraries.

  • Bamboo uses the object literal syntax to define styles. It also supports the shorthand syntax for the margin and padding properties.
  • Bamboo supports the variants, defaultVariants and compoundVariants APIs.
  • Bamboo supports design tokens and themes.
  • Bamboo supports all the variants of nested selectors (attribute, class, pseudo, descendant, child, sibling selectors and more). It also requires the use of the & to chain selectors.

Below are some of the differences between the two libraries.

css function

In Stitches, the css function is used to author both regular style objects and variant style objects.

import { css } from '@stitches/react'
 
// definition
const styles = css({
  border: 'solid 1px red',
  backgroundColor: 'transparent',
 
  variants: {
    variant: {
      // ...
    }
  }
})
 
// usage
<button className={styles({ variant: 'primary' })} />

In Bamboo, the css function is only used to author atomic styles, and the cva function to create variant style objects.

The css function

import { css } from '../styled-system/css'
 
// definition
const styles = css({
  border: 'solid 1px red',
  backgroundColor: 'transparent'
})
 
// usage
<button className={styles} />

The cva function

import { cva } from '../styled-system/css'
 
// definition
const styles = cva({
  base: {
    border: 'solid 1px red',
    backgroundColor: 'transparent'
  },
  variants: {
    variant: {
      // ...
    }
  }
})
 
// usage
<button className={styles({ variant: 'primary' })} />

styled function

In Stitches, the styled function creates components bound to both regular and variant style objects, and generates a unique className for each variant.

import { styled } from '@stitches/react'
 
const Button = styled('button', {
  // base styles
  backgroundColor: 'gainsboro',
  borderRadius: '9999px',
 
  variants: {
    // variant styles
  },
})
// => <button class="c-coNKBW c-coNKBW-dnSdJM-variant-primary">Button</button>

Bamboo has no styled factory. Base styles go under the base key of a recipe. With Vite, inline and configured recipes compile into the same globally shared atom pool; the choice is about organization and generated API, not CSS identity.

  • Colocated with the component, using the cva function. Static selections compile directly; finite runtime axes compile to reduced decision tables containing only complete reachable style sets.
import { cva } from '../styled-system/css'
 
const button = cva({
  className: 'button',
  base: {
    backgroundColor: 'gainsboro',
    borderRadius: '9999px',
  },
})
// => <button class="button">Button</button>

className is optional extraction metadata. It does not enter Vite's declaration identity or the names of the atoms it emits.

  • In the config, by defining the recipe in bamboo.config.ts. It uses the same Vite lowering as an inline recipe, while codegen exposes a named import and its variant types.
import { defineConfig, defineRecipe } from '@bamboocss/dev'
 
const buttonStyle = defineRecipe({
  className: 'button',
  base: {
    backgroundColor: 'gainsboro',
    borderRadius: '9999px',
  },
  variants: {
    // variant styles
  },
})
 
export default defineConfig({
  theme: {
    extend: {
      recipes: {
        buttonStyle,
      },
    },
  },
})
💡

You might need to run bamboo codegen --clean to generate the recipe functions.

import { buttonStyle } from '../styled-system/recipes'
;<button className={buttonStyle({ variant: 'primary' })}>Button</button>
 
// => <button className="button button--variant_primary">Button</button>

Responsive Styles

In Stitches, you configure breakpoints in the media key of the createStitches method, and use it via the @<breakpoint> syntax.

import { createStitches } from '@stitches/react'
 
// configure
const { styled, css } = createStitches({
  media: {
    bp1: '(min-width: 640px)',
    bp2: '(min-width: 768px)',
  },
})
 
// usage
const styles = css({
  backgroundColor: 'gainsboro',
  '@bp1': {
    backgroundColor: 'tomato',
  },
})

In Bamboo, you configure breakpoints in the theme.breakpoints key of the bamboo.config function

import { defineConfig } from '@bamboocss/dev'
 
export default defineConfig({
  theme: {
    extend: {
      breakpoints: {
        bp1: '640px',
        bp2: '768px',
      },
    },
  },
})
 
// usage
import { css } from '../styled-system/css'
 
const styles = css({
  bg: 'gainsboro',
  bp1: { bg: 'tomato' },
  // or
  margin: { base: '10px', bp1: '20px' },
})

In Stitches, you use the @initial keyword to target the base styles.

In Bamboo, you use the base key to target the base styles.

Tokens and Theme

Tokens

In Stitches, tokens are defined in the theme key of the createStitches method.

import { createStitches } from '@stitches/react'
 
const { styled, css } = createStitches({
  theme: {
    colors: {
      gray100: 'hsl(206,22%,99%)',
      gray200: 'hsl(206,12%,97%)',
    },
  },
  space: {},
  fonts: {},
})
 
// usage
const styles = css({
  backgroundColor: '$gray100',
})

In Bamboo, tokens are defined in the theme key of the bamboo.config function.

import { defineConfig } from '@bamboocss/dev'
 
export default defineConfig({
  theme: {
    tokens: {
      colors: {
        gray100: { value: 'hsl(206,22%,99%)' },
        gray200: { value: 'hsl(206,12%,97%)' },
      },
      spacing: {},
      fonts: {},
    },
    semanticTokens: {
      // ...
    },
  },
})
 
// usage
import { css } from '../styled-system/css'
 
const styles = css({
  backgroundColor: 'gray100',
})

Notice that in Bamboo, you don't need to use the $ prefix to access the tokens. If you want to keep it across the board, format the token names in a hook rather than renaming every token:

import { defineConfig } from '@bamboocss/dev'
 
export default defineConfig({
  // ...
  plugins: [
    {
      name: 'stitches-token-names',
      hooks: {
        'tokens:created': ({ configure }) => {
          configure({
            formatTokenName: (path) => '$' + path.join('-'),
          })
        },
      },
    },
  ],
})

Themes

In Stitches, the createTheme function is used to define dark theme values.

import { createStitches } from '@stitches/react'
 
const { createTheme } = createStitches({})
 
// create theme
const darkTheme = createTheme({
  colors: {
    gray100: 'hsl(206,8%,12%)',
    gray200: 'hsl(206,7%,14%)'
  }
})
 
// apply theme
<div className={darkTheme}>
  <div>Content nested in dark theme.</div>
</div>

In Bamboo, themes are designed as semantic tokens. You can define the semantic tokens in the semanticTokens key of the bamboo.config function.

import { defineConfig } from '@bamboocss/dev'
 
export default defineConfig({
  theme: {
    semanticTokens: {
      colors: {
        gray100: {
          value: { base: 'hsl(206,22%,99%)', _dark: 'hsl(206,8%,12%)' },
        },
        gray200: {
          value: { base: 'hsl(206,12%,97%)', _dark: 'hsl(206,7%,14%)' },
        },
      },
    },
  },
})

Token Aliases

In Stitches, you can create locally scoped tokens using the $$ prefix

import { styled } from '@stitches/react'
 
const Button = styled('button', {
  $$shadowColor: '$colors$pink500',
  boxShadow: '0 0 0 15px $$shadowColor',
})

In Bamboo, there's no special syntax, you need to use the css variable syntax. CSS variables are able to query the theme tokens directly using dot notation

import { css } from '../styled-system/css'
 
const button = css({
  '--shadowColor': 'colors.pink500',
  boxShadow: '0 0 0 15px var(--shadowColor)',
})

Animations

In Stitches, you can define keyframes using the keyframes method.

import { keyframes, styled } from '@stitches/react'
 
const scaleUp = keyframes({
  '0%': { transform: 'scale(1)' },
  '100%': { transform: 'scale(1.5)' },
})
 
// usage
const Button = styled('button', {
  '&:hover': {
    animation: `${scaleUp} 200ms`,
  },
})

In Bamboo, you define keyframes in the theme.keyframes key of the bamboo.config function.

import { defineConfig } from '@bamboocss/dev'
 
export default defineConfig({
  theme: {
    extend: {
      keyframes: {
        scaleUp: {
          '0%': { transform: 'scale(1)' },
          '100%': { transform: 'scale(1.5)' },
        },
      },
    },
  },
})
 
// usage
import { css } from '../styled-system/css'
 
const style = css({
  '&:hover': {
    animation: 'scaleUp 200ms',
  },
})

Utils

In Stitches, you can define utilities by using the utils key in the createStitches method.

import { createStitches, type PropertyValue } from '@stitches/react'
 
const { styled, css } = createStitches({
  utils: {
    linearGradient: (value: PropertyValue<'backgroundImage'>) => ({
      backgroundImage: `linear-gradient(${value})`,
    }),
  },
})

In Bamboo, you get a lot of built-in utilities (like mx, marginX, my, py, etc.) that you can use out of the box. You can also create custom utilites using the utilities key in the bamboo.config function.

The utilities API allows you define the connected token scale, generated className, and transform function.

import { defineConfig } from '@bamboocss/dev'
 
export default defineConfig({
  utilities: {
    extend: {
      linearGradient: {
        // (optional): the css property this maps to (to inherit the types from)
        property: 'backgroundImage',
        // (optional): the className to generate
        className: 'bg_gradient',
        // (optional): the shorthand name to use in the css
        shorthand: 'gradient',
        // (required): maps the value to the raw css object
        transform: (value) => ({
          backgroundImage: `linear-gradient(${value})`,
        }),
      },
    },
  },
})
💡

Running bamboo codegen will update the typings for the utilities, allowing for a type-safe developer experience.

Then you can use the utility in your styles.

import { css } from '../styled-system/css'
 
const buttonClass = css({
  linearGradient: '19deg, #21D4FD 0%, #B721FF 100%',
})

Global Styles

In Stitches, you define the global styles using the global.css function, and then call it in your app.

import { globalCss } from '@stitches/react'
 
const globalStyles = globalCss({
  '*': { margin: 0, padding: 0 },
})
 
// then in your app
globalStyles()

In Bamboo, you define the global styles in the bamboo.config.ts under global.css, using the defineGlobalStyles helper.

💡

The styles are emitted automatically under the base cascade layer by the Vite plugin

import { defineConfig, defineGlobalStyles } from '@bamboocss/dev'
 
const globalCss = defineGlobalStyles({
  '*': { margin: 0, padding: 0 },
})
 
export default defineConfig({
  // ...
  global: { css: globalCss },
})

Targeting Components

In Stitches, you can directly target React or styled components via the toString() method.

import { css } from '@stitches/react'
 
const Icon = () => (
  <svg className="right-arrow" ... />
);
 
// add a `toString` method
Icon.toString = () => '.right-arrow';
 
const buttonClass = css({
  [`& ${Icon}`]: {
    marginLeft: '5px'
  }
})

In Bamboo, you need to use the native selector directly. This is largely due to the static nature of Bamboo

import { css } from '../styled-system/css'
 
const Icon = () => (
  <svg className="right-arrow" ... />
);
 
 
const buttonClass = css({
  "& .right-arrow": {
    marginLeft: '5px'
  }
})

Server Side Rendering

Stitches needs per-framework SSR wiring — getCssText() from createStitches, and its output placed into the document head by hand.

Bamboo needs none of it. Styles are extracted and emitted at build time by the Vite plugin, so there is nothing to collect at request time.

Conclusion

Before choosing your preferred CSS-in-JS library, be sure to consider your engineering and design goals. Both Stitches and Bamboo are capable of achieving many of the same styling goals, but they have different approaches.