Skip to content

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

Consume without Panda

Ship a Panda design system as plain CSS, bundled or split per recipe, for apps that don't run Panda.

If the app doesn't run Panda, designSystem and theme extension are out. Ship it a plain stylesheet instead, in one file or several.

Emit a stylesheet

Bundled CSS file

Everything in one file. One import for the app, and the least to publish.

panda codegen
panda cssgen --outfile dist/styles.css

packages/ds/package.json

{
  "name": "@acme/ds",
  "exports": {
    ".": "./dist/index.js",
    "./styles.css": "./dist/styles.css"
  },
  "files": ["dist"],
  "scripts": {
    "build": "panda codegen && panda cssgen --outfile dist/styles.css && tsup"
  }
}

Split CSS files

--splitting writes one file per layer and one per recipe, so a team can load only the recipes it renders. Pass --outdir to say where they land.

panda codegen
panda cssgen --splitting --outdir dist/panda
dist/panda/styles.css                 # layer order, then imports everything below
dist/panda/styles/reset.css
dist/panda/styles/global.css
dist/panda/styles/tokens.css
dist/panda/styles/utilities.css
dist/panda/styles/recipes.css
dist/panda/styles/recipes/button.css  # one per recipe

Export the tree rather than a single file:

packages/ds/package.json

{
  "exports": {
    "./styles.css": "./dist/panda/styles.css",
    "./styles/*": "./dist/panda/styles/*"
  },
  "files": ["dist"],
  "scripts": {
    "build": "panda codegen && panda cssgen --splitting --outdir dist/panda && tsup"
  }
}

The app imports everything, or only the recipes it renders:

import '@acme/ds/styles.css'
import '@acme/ds/styles/recipes/button.css'

styles.css is where the @layer declarations live, and the individual files don't repeat them. Skip it and the app has to set the cascade order itself.

Emit every variant

The app never scans your source, so the sheet holds only what your own components rendered. A recipe they never call is left out entirely; a variant they never pass ships as a class name with no rule behind it.

packages/ds/panda.config.ts

export default defineConfig({
  staticCss: {
    recipes: '*'
  }
})

Name the recipes instead of '*' if the same package also ships for designSystem consumers. See Static CSS.

Import it in the app

The app needs the CSS and your components, nothing else:

src/main.tsx

import '@acme/ds/styles.css'
import { Button } from '@acme/ds'
 
export function App() {
  return <Button>Save</Button>
}

Two cases need the app to declare the cascade order itself: it has layered CSS of its own, or it cherry-picked files instead of importing styles.css. Copy the @layer lines from the top of styles.css into a stylesheet the app loads first. There are three of them, and the nested recipes.* lines matter as soon as you use more than one recipe.

Managing style conflicts

Two independently built stylesheets can emit the same class names (.button, --colors-brand), and an app without Panda can't deduplicate them. Set prefix in the library before you publish. Changing it later breaks everyone already importing your CSS.

packages/ds/panda.config.ts

export default defineConfig({
  prefix: 'acme' // .button → .acme-button
})

hash and cssVarRoot do the same job differently. See Style isolation.

Example

A Next.js app with no Panda installed, just the published CSS and components:

npx degit chakra-ui/panda-examples/examples/standalone-app my-app

standalone-app (opens in a new tab) in panda-examples.

See also

Edit this page on GitHubView as markdown
Last updated on