# Panda CSS Complete Documentation
> Panda CSS is a CSS-in-JS framework with build-time optimizations for styling web applications
This document contains the complete Panda CSS documentation, organized by category for easy navigation.
## Table of Contents
### Styling
- [Agent Skills](#agent-skills)
- [Using Angular](#using-angular)
- [Using Astro](#using-astro)
- [Browser Support](#browser-support)
- [Cascade Layers](#cascade-layers)
- [Migrating from Chakra UI](#migrating-from-chakra-ui)
- [Panda CLI](#panda-cli)
- [Color opacity modifier](#color-opacity-modifier)
- [The compiler engine](#the-compiler-engine)
- [Conditional Styles](#conditional-styles)
- [Dynamic styling](#dynamic-styling)
- [Using Ember](#using-ember)
- [Migrating from Emotion](#migrating-from-emotion)
- [The extend keyword](#the-extend-keyword)
- [Frequently Asked Questions](#frequently-asked-questions)
- [Using Gatsby](#using-gatsby)
- [Welcome to Panda](#welcome-to-panda)
- [Global Styles](#global-styles)
- [How Panda works](#how-panda-works)
- [Installation](#installation)
- [JSX Style Context](#jsx-style-context)
- [LLMs.txt](#llms.txt)
- [MCP Server](#mcp-server)
- [Merging Styles](#merging-styles)
- [Migration strategy](#migration-strategy)
- [Using Next.js](#using-next.js)
- [Using Nuxt](#using-nuxt)
- [Patterns](#patterns)
- [Performance & Optimization](#performance-&-optimization)
- [Using PostCSS](#using-postcss)
- [Using Preact](#using-preact)
- [Using Qwik](#using-qwik)
- [Using React Router](#using-react-router)
- [Recipes](#recipes)
- [Using Redwood](#using-redwood)
- [Using Remix](#using-remix)
- [Responsive Design](#responsive-design)
- [Using Rsbuild](#using-rsbuild)
- [Slot Recipes](#slot-recipes)
- [Using SolidJS](#using-solidjs)
- [Migrating from Stitches](#migrating-from-stitches)
- [Using Storybook](#using-storybook)
- [Style props](#style-props)
- [Migrating from Styled Components](#migrating-from-styled-components)
- [Styled System](#styled-system)
- [Migrating from StyleX](#migrating-from-stylex)
- [Using Svelte](#using-svelte)
- [Migrating from Tailwind CSS](#migrating-from-tailwind-css)
- [Template Literals](#template-literals)
- [Migrating from Theme UI](#migrating-from-theme-ui)
- [Thinking in Panda](#thinking-in-panda)
- [Upgrading to v2](#upgrading-to-v2)
- [viewTransition()](#viewtransition())
- [Virtual Color](#virtual-color)
- [Using Vite](#using-vite)
- [Using Vue](#using-vue)
- [Writing Styles](#writing-styles)
### Theming
- [Animation Styles](#animation-styles)
- [Custom Font](#custom-font)
- [Layer Styles](#layer-styles)
- [Multi-Theme Tokens](#multi-theme-tokens)
- [Spec](#spec)
- [Using Panda Studio](#using-panda-studio)
- [Panda Studio in v2](#panda-studio-in-v2)
- [Text Styles](#text-styles)
- [Theme](#theme)
- [Tokens](#tokens)
- [Using Tokens](#using-tokens)
### Design Systems
- [Avoiding CSS collisions](#avoiding-css-collisions)
- [Build a design system](#build-a-design-system)
- [Conditions](#conditions)
- [Config Functions](#config-functions)
- [Consume a design system](#consume-a-design-system)
- [Ecosystem plugins](#ecosystem-plugins)
- [Environment-specific config](#environment-specific-config)
- [Federated Micro-Frontends](#federated-micro-frontends)
- [forwardProps](#forwardprops)
- [Panda Integration Hooks](#panda-integration-hooks)
- [Isolated declarations](#isolated-declarations)
- [Minimal Setup](#minimal-setup)
- [Monorepo dev workflow](#monorepo-dev-workflow)
- [Using Panda in a Component Library](#using-panda-in-a-component-library)
- [Customizing Patterns](#customizing-patterns)
- [Building a design system with Panda](#building-a-design-system-with-panda)
- [Presets](#presets)
- [Publishing to npm](#publishing-to-npm)
- [Set up a library package](#set-up-a-library-package)
- [Shared styled-system in a monorepo](#shared-styled-system-in-a-monorepo)
- [Ship the styled-system vs the CSS](#ship-the-styled-system-vs-the-css)
- [Static CSS Generator](#static-css-generator)
- [Track usage in wrapped components](#track-usage-in-wrapped-components)
- [Troubleshooting](#troubleshooting)
- [Utilities](#utilities)
- [Wrap headless UI](#wrap-headless-ui)
### Reference
- [Background](#background)
- [Border](#border)
- [CLI Reference](#cli-reference)
- [Configuring Panda](#configuring-panda)
- [Debugging](#debugging)
- [Deprecations](#deprecations)
- [Diagnostics Reference](#diagnostics-reference)
- [Display](#display)
- [Divide](#divide)
- [Editor & IDE tooling](#editor-&-ide-tooling)
- [Effects](#effects)
- [ESLint & OXLint Plugin](#eslint-&-oxlint-plugin)
- [Flex and Grid](#flex-and-grid)
- [Focus Ring](#focus-ring)
- [Gradients](#gradients)
- [Helpers](#helpers)
- [Interactivity](#interactivity)
- [Layout](#layout)
- [List](#list)
- [Masks](#masks)
- [Outline](#outline)
- [Sizing](#sizing)
- [Spacing](#spacing)
- [SVG](#svg)
- [Tables](#tables)
- [Transforms](#transforms)
- [Transitions](#transitions)
- [Typography](#typography)
---
# Styling
## Agent Skills
Portable, job-shaped skill packs that teach AI coding agents how to use Panda correctly.
Agent Skills are coming soon and aren't published yet, so treat the shape below as a preview. For AI agent support
that works today, see [LLMs.txt](/docs/styling/llms-txt) and [MCP Server](/docs/styling/mcp-server).
## The problem it's meant to solve
AI coding agents are a normal part of how Panda projects get written today, but without something grounding them,
agents commonly reach for the wrong instincts: emitting Tailwind class strings or Emotion-style patterns in a Panda
project, inventing raw hex or spacing values instead of using tokens, or wiring a design system by hand
(`include: ['β¦/panda.buildinfo.json']`) instead of the newer dedicated mechanism. Community skill packs for Panda
exist today but are thin and unofficial, while some other libraries already ship official ones.
## How this differs from LLMs.txt and MCP
Panda already has two other AI-facing mechanisms, and Agent Skills is meant to be a third, distinct one, not a
replacement for either:
- **[LLMs.txt](/docs/styling/llms-txt)** hands an agent the documentation itself, a static content dump for context.
- **[MCP Server](/docs/styling/mcp-server)** answers "what exists right now" in your specific project, live token,
recipe, and pattern lookups.
- **Agent Skills** would answer "how do I do this correctly," procedural, decision-oriented guidance (which API to
reach for, what not to do), read once per task rather than queried live.
## The planned skills
Rather than one large reference document, the plan is six small, job-shaped skills, so an agent loads only the one
relevant to the task in front of it:
| Skill | Job |
| --- | --- |
| `panda-styling` | Write or edit styles in application or component code |
| `panda-recipes` | Build or change recipes, slot recipes, or `cva`/`sva` |
| `panda-tokens` | Author or extend theme tokens and conditions |
| `panda-setup` | Install, initialize, and wire up a build tool |
| `panda-design-system` | Publish or consume a design system |
| `panda-migrate-v2` | Upgrade from v1, or navigate a v2 beta |
Each skill is planned to stay short (roughly 200 to 300 lines), lead with when an agent should reach for it, and
include short do/don't examples rather than long prose, along the lines of what
[skills.sh](https://skills.sh/) already hosts for other libraries.
## Installing skills (once published)
The install experience is expected to follow the same shape as any other package on the open skills ecosystem:
```bash
npx skills add /@panda-styling
```
Where exactly Panda's skills will be published from (the main repo, or a dedicated skills-only repo) isn't finalized,
so treat the exact install path above as illustrative rather than a real command to run today.
## See also
- [LLMs.txt](/docs/styling/llms-txt) and [MCP Server](/docs/styling/mcp-server) for the AI-facing tooling that
exists today.
- [Upgrading to v2](/docs/styling/upgrading-to-v2) for the broader context this feature is part of.
---
## Using Angular
Easily use Panda with Angular with our dedicated integration.
This guide shows you how to set up Panda CSS in an Angular project using PostCSS.
## Start a new project
### Create Vite project
To get started, we will need to create a new Angular project using the official
[scaffolding tool](https://angular.dev/tools/cli).
If you don't enter any parameter, the CLI will guide you through the process of creating a new Angular app.
```bash
ng new test-app
```
You will be asked a few questions, answer them as follows:
```bash
? Which stylesheet format would you like to use? CSS
? Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? No
```
### Install Panda
Install panda and create your `panda.config.ts` file.
{/* */}
```bash
pnpm install -D @pandacss/dev
pnpm panda init
```
```bash
npm install -D @pandacss/dev
npx panda init
```
```bash
yarn add -D @pandacss/dev
yarn panda init
```
```bash
bun add -D @pandacss/dev
bun panda init
```
{/* */}
### Configure PostCSS
Create a `postcss.config.json` file in the root of your project and add the following code:
```json filename="postcss.config.json"
{
"plugins": {
"@pandacss/dev/postcss": {}
}
}
```
> You must use a JSON file for the PostCSS configuration, as the Angular CLI does not support JavaScript PostCSS
> configuration files.
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3} filename="package.json"
{
"scripts": {
+ "prepare": "panda codegen",
"ng": "ng",
"start": "ng serve",
"build": "ng build",
}
}
```
- `"prepare"` - script that will run Panda CSS CLI codegen before each build. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the content
Make sure that all of the paths of your Angular components are included in the `include` section of the
`panda.config.ts` file.
```js {8,17} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ['./src/**/*.{js,jsx,ts,tsx}'],
// Files to exclude
exclude: [],
// The output directory for your css system
outdir: 'styled-system'
})
```
### Configure the entry CSS with layers
Add this code to an `src/index.css` file and import it in the root component of your project.
```css filename="src/index.css"
@layer reset, base, tokens, recipes, utilities;
```
## Start your build process
Run the following command to start your development server.
{/* */}
```bash
pnpm dev
```
```bash
npm run dev
```
```bash
yarn dev
```
```bash
bun dev
```
{/* */}
### Start using Panda
Now you can start using Panda CSS in your project. Here is the snippet of code that you can use in your
`src/app.component.ts` file.
```typescript filename="src/app.component.ts"
import { Component } from '@angular/core'
import { css } from '../styled-system/css'
@Component({
selector: 'app-root',
standalone: true,
template: ` `
})
export class App {
redBg = css({ bg: 'red.400' })
}
```
---
## Using Astro
Easily use Panda with Astro with our dedicated integration.
This guide shows you how to set up Panda CSS in an Astro project using our dedicated integration.
## Setup
### Install Panda
Install panda and create your `panda.config.ts` file.
{/* */}
```bash
pnpm install -D @pandacss/dev
pnpm panda init --postcss
```
```bash
npm install -D @pandacss/dev
npx panda init --postcss
```
```bash
yarn add -D @pandacss/dev
yarn panda init --postcss
```
```bash
bun add -D @pandacss/dev
bun panda init --postcss
```
{/* */}
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3}
{
"scripts": {
+ "prepare": "panda codegen",
"dev": "astro dev",
"start": "astro start",
"build": "astro build",
"preview": "astro preview"
}
}
```
The `prepare` script that will run codegen after dependency installation. Read more about [codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the content
Add your panda config to your `panda.config.js` file, or wherever panda is configured in your project.
```js {6}
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
preflight: true,
// define the content to scan ππ»
include: ['./src/**/*.{ts,tsx,js,jsx,astro}', './pages/**/*.{ts,tsx,js,jsx,astro}'],
exclude: [],
outdir: 'styled-system'
})
```
### Configure the entry CSS with layers
Add the layer css code to the `src/index.css` file
```css filename="src/index.css"
@layer reset, base, tokens, recipes, utilities;
```
Then, import the `src/index.css` file in your page or layout file
```md filename="src/pages/index.astro"
---
import '../index.css';
---
```
### Update the postcss config
Astro requires a little change for the `postcss.config.cjs` that `panda init --postcss` generated:
```diff {3} filename="postcss.config.cjs"
module.exports = {
- plugins: {
- '@pandacss/dev/postcss': {}
- }
+ plugins: [require('@pandacss/dev/postcss')()]
}
```
### Start your build process
Run your build process with `npm run dev` or whatever command is configured in your package.json file.
{/* */}
```bash
pnpm dev
```
```bash
npm run dev
```
```bash
yarn dev
```
```bash
bun dev
```
{/* */}
### Start using Panda
Use the generated style utilities in your code, and panda will extract them to the generated CSS file.
```jsx
---
import { css } from '../../styled-system/css';
---
Hello !
```
## Troubleshooting
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
`tsconfig.json` file:
```json filename="tsconfig.json"
{
// ...
"include": ["src", "styled-system"]
}
```
---
## Browser Support
Learn about the browser support for Panda
Panda CSS is built with modern CSS features and uses [PostCSS](https://postcss.org/) to add support for older browsers.
Panda supports the latest, stable releases of major browsers that support the following features:
- [CSS Variables](https://caniuse.com/css-variables)
- [CSS Cascade Layers](https://caniuse.com/css-cascade-layers)
- Modern selectors, such as [`:where()`](https://caniuse.com/mdn-css_selectors_where) and
[`:is()`](https://caniuse.com/css-matches-pseudo)
## Browserlist
Based on the above criteria, the following browsers are supported:
```txt
>= 1%
last 1 major version
not dead
Chrome >= 99
Edge >= 99
Firefox >= 97
iOS >= 15.4
Safari >= 15.4
Android >= 115
Opera >= 73
```
## Polyfills
For older browsers:
- Set `polyfill: true` (or `--polyfill`) so Panda emits cascade-layer order without `@layer` (see
[Cascade Layers](/docs/concepts/cascade-layers#polyfills)).
- Add [autoprefixer](https://github.com/postcss/autoprefixer) in PostCSS when you need vendor prefixes.
```js
// postcss.config.cjs
module.exports = {
plugins: ['@pandacss/dev/postcss', 'autoprefixer']
}
```
---
## Cascade Layers
CSS cascade layers refer to the order in which CSS rules are applied to an HTML element.
When multiple CSS rules apply
to the same element, the browser uses the cascade to determine which rule should take precedence. See the
[MDN article](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer) to learn more.
Panda takes advantage of the cascade to provide a more efficient and flexible way to organize styles. This allows you to
define styles in a modular way, using CSS rules that are scoped to specific components or elements.
## Layer Types
Panda supports five types of cascade layers out of the box:
- `@layer reset` - The reset layer is used to reset the default styles of HTML elements. This is used when
`preflight: true` is set in the config. You can also use this layer to add your own reset styles.
The generated CSS for the reset layer looks like this:
```css
@layer reset {
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* ... */
}
```
- `@layer base` - The base layer contains global styles defined in the `globalStyles` key in the config. You can also
use this layer to add your own global styles.
The generated CSS for the base layer looks like this:
```css
@layer base {
a {
color: #000;
text-decoration: none;
}
/* ... */
}
```
- `@layer recipes` - The recipes layer contains styles for recipes created within the config (aka config recipes). You
can also use this layer to add your own component styles.
The generated CSS for the recipes layer looks like this:
```css
@layer recipes {
.button {
/* ... */
}
.button--variant-primary {
/* ... */
}
/* ... */
}
```
- `@layer tokens` - The tokens layer contains css variables for tokens and semantic tokens. You can also use this layer
to add your own design tokens.
The generated CSS for the tokens layer looks like this:
```css
@layer tokens {
:root {
--color-primary: #000;
--color-secondary: #fff;
--color-tertiary: #ccc;
--shadow-sm: 0 0 0 1px rgba(0, 0, 0, 0.05);
}
/* ... */
}
```
- `@layer utilities` - Styles that are scoped to a specific utility class. These styles are only applied to elements
that have the utility class applied.
## Layer Order
The cascade layers are applied in the following order:
- `@layer utilities` (Highest priority)
- `@layer recipes`
- `@layer tokens`
- `@layer base`
- `@layer reset` (Lowest priority)
This means that styles defined in the `@layer utilities` will take precedence over styles defined in the
`@layer recipes`. This is useful when you want to override the default styles of a component.
## Layer CSS
The generated CSS in Panda is organized into layers. This allows you to define styles in a modular way, using CSS rules
that are scoped to specific components or elements.
Here's what the first line of the generated CSS looks like:
```css
@layer reset, base, tokens, recipes, utilities;
```
Adding this line to the top of your CSS file will determine the order in which the layers are applied. This is the most
exciting feature of CSS cascade layers.
## Customize layers
Panda lets you customize the cascade layers, so your project can coexist with other solutions. Learn more about customizing layers [here](/docs/reference/config#layers).
## Polyfills
Need older browsers, or want unlayered CSS unable to override Panda? Turn on the built-in polyfill:
```js
export default defineConfig({
// ...
polyfill: true
})
```
Or pass `--polyfill` to `panda` / `panda cssgen`. Emit replaces `@layer` with `:not(#\#)` specificity boosts (same idea
as `@csstools/postcss-cascade-layers`). Keep `@layer reset, base, β¦;` in your entry CSS so PostCSS / Vite / webpack can
find the stylesheet root β hosts strip that Panda order line when polyfill is on.
---
## Migrating from Chakra UI
Migrate your project from Chakra UI to Panda and see how style props, theme, and variants map across.
This guide outlines the steps needed to migrate your project from Chakra UI to Panda and highlights key design
differences between the two.
> **Note:** Chakra's own team has said its theming system was deliberately designed to align with Panda's
> conventions, `globalCss`, `{ value }`-wrapped tokens, and `recipes`/`slotRecipes` terminology all match Panda
> directly, while Chakra still runs on Emotion at runtime rather than Panda's static extraction. Several of the
> mappings below will already look familiar because of that.
Here are some similarities between the two:
- Both support style props directly on components, and both support design tokens defined in a central theme.
- Both support responsive values and a similar breakpoint-object shorthand.
- Both have a first-class idea of a component "variant" that's more than just a class name toggle.
Here's where they differ.
## Performance
Chakra styles components with `@emotion/styled` at runtime: every style prop and `sx` value is computed by Emotion in
the browser (or on the server during SSR), and the CSS is injected on the fly.
Panda extracts your style objects at build time and ships plain static CSS, there's no runtime style computation
cost at all. This matters most in large component trees or lists, where Chakra's runtime style computation happens
per render.
## Theming
Chakra builds a system with `createSystem` and a config from `defineConfig`, then passes it to `ChakraProvider` as
`value`:
```jsx
import { ChakraProvider, createSystem, defaultConfig, defineConfig } from '@chakra-ui/react'
const config = defineConfig({
theme: {
tokens: {
colors: {
brand: { value: '#0ea5e9' }
}
}
}
})
const system = createSystem(defaultConfig, config)
export default function App({ children }) {
return {children}
}
```
Notice the token shape, `{ value: '#0ea5e9' }`, is the same wrapper Panda uses. Panda doesn't need a provider, the
theme lives in `panda.config.ts` and is resolved at build time, not read from React context at render time:
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
tokens: {
colors: {
brand: { value: '#0ea5e9' }
}
}
}
}
})
```
See [Tokens](/docs/theming/tokens) for the full token model.
## The `sx` prop
Chakra's `sx` prop (and the lower-priority `__css` prop) layers ad hoc styles onto a component that already has
style props:
```jsx
```
Panda's closest equivalent is the `css` prop on the `styled` factory, or just building the class name directly with
`css()`:
```jsx
import { styled } from '../styled-system/jsx'
```
```jsx
import { css } from '../styled-system/css'
```
See [JSX Style Props](/docs/styling/style-props) for the full set of ways to style a component inline.
## Variants
Chakra defines a component's variants with `defineRecipe` (single-part) or `defineSlotRecipe` (multi-part, like
`Menu` or `Tabs`), registered on the system's `theme.recipes`/`theme.slotRecipes`:
```ts
import { defineRecipe } from '@chakra-ui/react'
export const buttonRecipe = defineRecipe({
base: { fontWeight: 'bold' },
variants: {
variant: {
solid: { bg: 'brand', color: 'white' }
}
}
})
```
```ts
const config = defineConfig({
theme: {
recipes: { button: buttonRecipe }
}
})
```
Panda's equivalent is a [recipe](/docs/styling/recipes), either colocated with `cva`/`sva` or shared globally via
`theme.recipes`/`theme.slotRecipes` in `panda.config.ts`, the same `base`/`variants` shape Chakra uses:
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
recipes: {
button: {
className: 'button',
base: { fontWeight: 'bold' },
variants: {
variant: {
solid: { bg: 'brand.500', color: 'white' }
}
}
}
}
}
}
})
```
```jsx
import { button } from '../styled-system/recipes'
```
See [Recipes](/docs/styling/recipes) and [Slot Recipes](/docs/styling/slot-recipes).
## Color Modes
Chakra's own docs recommend semantic tokens (`bg="bg.subtle"`, values that resolve automatically per color mode) as
the preferred pattern, the same model Panda uses. A `useColorModeValue` hook is also available, opt-in via a
CLI-generated snippet (`npx @chakra-ui/cli snippet add color-mode`) built on `next-themes`, for cases where a value
genuinely needs to branch per color mode at the call site rather than through a token:
```jsx
import { useColorModeValue } from '@/components/ui/color-mode'
const bg = useColorModeValue('white', 'gray.800')
```
Panda defines the light/dark pair once, as a semantic token, and every user of that token gets the right value
automatically, no hook call needed at the usage site:
```ts filename="panda.config.ts"
theme: {
extend: {
semanticTokens: {
colors: {
bg: { value: { base: '{colors.white}', _dark: '{colors.gray.800}' } }
}
}
}
}
```
```jsx
```
See [Theme](/docs/theming/theme) and [Multiple Themes](/docs/theming/multiple-themes) for semantic tokens and
color-mode setup in depth.
## Global Styles
Chakra applies global styles through the `globalCss` key in `defineConfig`, the same key name Panda uses:
```ts
const config = defineConfig({
globalCss: {
body: { bg: 'gray.50', color: 'gray.800' }
}
})
```
Panda's version is the same shape, `globalCss` in `panda.config.ts`, no provider needed since it's emitted straight
into the generated CSS:
```ts filename="panda.config.ts"
export default defineConfig({
globalCss: {
body: { bg: 'gray.50', color: 'gray.800' }
}
})
```
See [Global Styles](/docs/styling/global-styles).
## Component Styles
Chakra ships pre-styled components (`Box`, `Flex`, `Grid`, `Stack`) that accept style props directly:
```jsx
import { Box, Grid } from '@chakra-ui/react'
Box
```
Panda's [patterns](/docs/styling/patterns) cover the same layout primitives, as a JSX component or a plain function:
```jsx
import { Box, Grid } from '../styled-system/jsx'
Box
```
Panda doesn't ship interactive components (`Menu`, `Modal`, `Tabs`) the way Chakra does, since Panda is a styling
engine, not a component library. If you need Chakra's interactive components without Chakra's runtime styling, see
[Building a design system with Panda](/docs/design-systems/preset-overview) and consider a headless library like Ark
UI on top of Panda instead, the same pattern covered in [Wrap headless UI](/docs/design-systems/wrap-headless-ui).
## Conclusion
Chakra and Panda agree on a lot of the underlying concepts, matching config shapes (`globalCss`, `{ value }`
tokens), matching recipe terminology, and semantic tokens as the preferred color-mode pattern on both sides, since
Chakra's theming system was deliberately designed toward Panda's conventions. The real migration cost is mechanical:
moving each of those concepts off Chakra's runtime (`ChakraProvider`, `createSystem`, the recipe hooks) onto Panda's
build-time equivalent, and separately deciding what to do about Chakra's interactive components if you were relying
on them.
## See also
- [Migration strategy](/docs/styling/migration-strategy) for running both libraries side by side during the
migration.
- [Wrap headless UI](/docs/design-systems/wrap-headless-ui) if you need Chakra-like interactive components without
Chakra's runtime.
---
## Panda CLI
An alternative way to use Panda is by running the Panda CLI tool.
This guide shows you how to use Panda as an alternative approach by running the Panda CLI tool.
### Install Panda
{/* */}
```bash
pnpm install -D @pandacss/dev
pnpm panda init
```
```bash
npm install -D @pandacss/dev
npx panda init
```
```bash
yarn add -D @pandacss/dev
yarn panda init
```
```bash
bun add -D @pandacss/dev
bun panda init
```
{/* */}
### Configure the content
Add the paths to all of your JavaScript or TypeScript code where you intend to use panda.
```js {5}
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
preflight: true,
include: ['./src/**/*.{ts,tsx,js,jsx}', './pages/**/*.{ts,tsx,js,jsx}'],
exclude: [],
outdir: 'styled-system'
})
```
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3}
{
"scripts": {
+ "prepare": "panda codegen",
}
}
```
The `prepare` script that will run codegen after dependency installation. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Import the generated CSS
For each Panda run, it emits the generated CSS at the `styled-system/styles.css` file path. Import this file at the root
component of your project.
```jsx {1}
import './styled-system/styles.css'
export function App() {
return
Page
}
```
### Start the Panda build process
Run the CLI tool to scan your JavaScript and TypeScript files for style properties and call expressions.
{/* */}
```bash
# Run it once
pnpm panda
# Run it in watch mode
pnpm panda --watch
```
```bash
# Run it once
npx panda
# Run it in watch mode
npx panda --watch
```
```bash
# Run it once
yarn panda
# Run it in watch mode
yarn panda --watch
```
```bash
# Run it once
bun panda
# Run it in watch mode
bun panda --watch
```
{/* */}
### Start using Panda
Use the generated style utilities in your code and panda will extract them to the generated CSS file. Then run your
build process.
```jsx
import { css } from './styled-system/css'
export function App() {
return
}
```
## Troubleshooting
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
`tsconfig.json` file:
```json filename="tsconfig.json"
{
// ...
"include": ["src", "styled-system"]
}
```
---
## Color opacity modifier
Append /{opacity} to color tokens so Panda emits color-mix; omit it for plain token references.
Append `/{opacity}` to a color token, for example `red.300/40`, `{colors.black/50}`, or `red/half`. Panda emits
[`color-mix`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-mix) only when the suffix is present;
plain values like `white` stay as token references. Works on color-connected utilities from `@pandacss/preset-base`:
`background`, `color`, `borderColor`, and others. Browsers without `color-mix` use the solid token.
## Examples
### Basic example
```ts
css({
bg: 'red.300/40',
color: 'white'
})
```
```css
@layer utilities {
.bg_red\.300\/40 {
--mix-background: color-mix(in srgb, var(--colors-red-300) 40%, transparent);
background: var(--mix-background, var(--colors-red-300));
}
.text_white {
color: var(--colors-white);
}
}
```
### Using a custom property
For `--*` variables in `css()`, wrap the token path in curly braces and reference it with `var()`:
```ts
css({
'--overlay': '{colors.black/50}',
bg: 'var(--overlay)'
})
```
```ts
// β Not resolved
'--overlay': 'colors.black/50'
// β Resolved to color-mix
'--overlay': '{colors.black/50}'
```
### Usage with virtual colors
See [virtual colors](/docs/styling/virtual-color) for the full `colorPalette` API:
```ts
css({
colorPalette: 'blue',
'--tint': '{colors.colorPalette.500/50}',
bg: 'var(--tint)'
})
```
## Usage in custom utilities
Support `{color}/{opacity}` on your own utilities through the `colorMix` helper on `utils`:
```ts
export default defineConfig({
utilities: {
background: {
shorthand: 'bg',
className: 'bg',
values: 'colors',
transform(value, args) {
const mix = args.utils.colorMix(value)
if (mix.invalid) return { background: value }
return {
background: mix.value
}
}
}
}
})
```
### Using createColorMixTransform
```ts
import type { PropertyTransform } from '@pandacss/types'
export const createColorMixTransform =
(prop: string): PropertyTransform =>
(value, args) => {
const mix = args.utils.colorMix(value)
if (mix.invalid) return { [prop]: value }
const cssVar = '--mix-' + prop
return {
[cssVar]: mix.value,
[prop]: `var(${cssVar}, ${mix.color})`
}
}
```
```ts
export default defineConfig({
utilities: {
background: {
shorthand: 'bg',
className: 'bg',
values: 'colors',
transform: createColorMixTransform('background')
}
}
})
```
---
## The compiler engine
How Panda compiles your styles β a Rust engine built on Oxc, exposed to both Node and the browser.
Panda's compiler is a Rust engine built on [Oxc](https://oxc.rs). It reads your source, pulls out the styles, and emits
atomic CSS in cascade layers β the same model [How Panda works](/docs/styling/how-panda-works) describes, running on
native code.
If you're coming from an earlier version, the pipeline is what changed:
```
before ts-morph walks a TypeScript AST β PostCSS generates the CSS
after Oxc parses each file once β native Rust emits the CSS
```
Your styles and your output don't change (see [Upgrading to v2](/docs/styling/upgrading-to-v2) for the handful of
deliberate CSS differences). The machine underneath does.
## Two bindings, one engine
The engine is a set of Rust crates. Two bindings wrap it, so the same code runs in both places:
- **`@pandacss/compiler`** β a native binding (NAPI). The CLI and the Vite, webpack, and Rollup plugins use it.
- **`@pandacss/compiler-wasm`** β the same engine compiled to WASM, for the browser. The playground and editor tooling
run on it.
Both talk to a filesystem trait rather than `node:fs` directly, which is what lets the engine compile to WASM at all.
Node and browser builds produce the same CSS.
## One long-lived compiler
The entry point is `createCompiler(config)`. It returns a stateful compiler you hold onto, not a one-shot function.
The expensive work β turning your config into utilities, conditions, recipes, and a resolved token dictionary β happens
once, when you create the compiler. The same instance is reused for every file you parse and every rebuild. You feed it
files as they change (`parseFile`, `parseFiles`, `parseFileSource`) and ask it for CSS; the config isn't recompiled each
time.
That's what makes `panda dev` cheap: the watcher re-parses only the files that changed and re-emits, against config
state that's already in memory.
## The pipeline: extract, encode, emit
Data flows one way. Each stage is a separate crate, and dependencies point in the direction of the arrows β nothing
downstream reaches back up.
```
source β extract β encode β emit β CSS
```
- **Extract.** Oxc parses each source file once and matches your style calls β `css()`, `cva()`, patterns, JSX style
props, and the rest. One parse per file, no TypeScript program in the hot path. Static values (including local
`const`s and imports from other files) are folded here.
- **Encode.** Matched usage becomes atomic style records β one property/value/condition per atom, deduplicated.
- **Emit.** Atoms, recipes, and your static CSS become real CSS rules, grouped into `@layer`s and written natively. No
PostCSS pass.
Codegen runs alongside this to write the `styled-system` output β the types and helpers you import. A faΓ§ade crate owns
the multi-file build and watch state and wires the stages together; it's the single entry point both bindings talk to.
## What you gain
You write the same Panda β static analysis of your source, atomic CSS, cascade layers, tokens, recipes, conditions. The
compiled engine changes what that costs:
- One parse per file, and no `ts-morph` AST to build and hold.
- A smaller install, since the `ts-morph` / `ts-evaluator` dependency tree is gone.
- One shared compiler core behind the tooling. Diagnostics and lint read the same extraction the build does, rather than
reimplementing it β see [Diagnostics](/docs/reference/diagnostics), the
[ESLint & oxlint plugin](/docs/reference/eslint-oxlint-plugin), and
[Editor & IDE tooling](/docs/reference/editor-tooling).
## Still open: CSS optimization
The emitter writes CSS natively and minifies when you set `minify: true`, but full parity with LightningCSS
minification is still open. If you rely on the last few percent of that minified output, check the result before you
ship it.
## See also
- [How Panda works](/docs/styling/how-panda-works) for the extraction model this runs.
- [Upgrading to v2](/docs/styling/upgrading-to-v2) for what else changes.
---
## Conditional Styles
Learn how to use conditional and responsive styles in Panda.
When writing styles, you might need to apply specific changes depending on a specific condition, whether it's based on
breakpoint, css pseudo state, media query or custom data attributes.
Panda allows you to write conditional styles, and provides common condition shortcuts to make your life easier. Let's
say you want to change the background color of a button when it's hovered. You can do it like this:
```jsx
```
## Overview
### Property based condition
This works great, but might be a bit verbose. You can apply the condition `_hover` directly to the `bg` property,
leading to a more concise syntax:
```diff
```
> Note: The `base` key is used to define the default value of the property, without any condition.
### Nested condition
Conditions in Panda can be nested, which means you can apply multiple conditions to a single property or another
condition.
Let's say you want to change the background color of a button when it's focused and hovered. You can do it like this:
```jsx
```
### Built-in conditions
Panda includes a set of common pseudo states that you can use to style your components:
- Pseudo Class: `_hover`, `_active`, `_focus`, `_focusVisible`, `_focusWithin`, `_disabled`
- Pseudo Element: `_before`, `_after`
- Media Query: `sm`, `md`, `lg`, `xl`, `2xl`
- Data Attribute Selector: `_horizontal`, `_vertical`, `_portrait`, `_landscape`
## Arbitrary selectors
What if you need a one-off selector that is not defined in your config's conditions? You can use the `css` function to
generate classes for arbitrary selectors:
```tsx
import { css } from '../styled-system/css'
const App = () => {
return (
*': { margin: '2' }
})}
/>
)
}
```
This also works with the supported at-rules (`@media`, `@layer`, `@container`, `@supports`, and `@page`):
```tsx
import { css } from '../styled-system/css'
const App = () => {
return (
)
}
```
## Pseudo Classes
### Hover, Active, Focus, and Disabled
You can style the hover, active, focus, and disabled states of an element using their `_` modifier:
```jsx
```
### First, Last, Odd, Even
You can style the first, last, odd, and even elements of a group using their `_` modifier:
```jsx
{items.map(item => (
{item}
))}
```
You can also style even and odd elements using the `_even` and `_odd` modifier:
```jsx
{items.map(item => (
{item}
))}
```
## Pseudo Elements
### Before and After
You can style the `::before` and `::after` pseudo elements of an element using their `_before` and `_after` modifier:
```jsx
Hello
```
#### Notes
- **Before and After**: Ensure you wrap the content value in double quotes.
- **Mixing with Conditions**: When using condition and pseudo elements, prefer to place the condition **before** the
pseudo element.
```jsx
css({
// This works β
_dark: { _backdrop: { color: 'red' } }
// This doesn't work β
_backdrop: { _dark: { color: 'red' } }
})
```
The reason `_backdrop: { _dark: { color: 'red' } }` doesn't work is because it generated an invalid CSS structure that
looks like:
```css
&::backdrop {
&.dark,
.dark & {
color: red;
}
}
```
### Placeholder
Style the placeholder text of any input or textarea using the `_placeholder` modifier:
```jsx
```
### File Inputs
Style the file input button using the `_file` modifier:
```jsx
```
## Media Queries
### Reduced Motion
Use the `_motionReduce` and `_motionSafe` modifiers to style an element based on the user's motion preference:
```jsx
Hello
```
### Color Scheme
The `prefers-color-scheme` media feature is used to detect if the user has requested the system use a light or dark
color theme.
Use the `_osLight` and `_osDark` modifiers to style an element based on the user's color scheme preference:
```jsx
Hello
```
Let's say your app is dark by default, but you want to allow users to switch to a light theme. You can do it like this:
```jsx
Hello
```
### Color Contrast
The `prefers-contrast` media feature is used to detect if the user has requested the system use a high or low contrast
theme.
Use the `_highContrast` and `_lessContrast` modifiers to style an element based on the user's color contrast preference:
```jsx
Hello
```
### Orientation
The `orientation` media feature is used to detect if the user has a device in portrait or landscape mode.
Use the `_portrait` and `_landscape` modifiers to style an element based on the user's device orientation:
```jsx
Hello
```
## Group Selectors
When you need to style an element based on its parent element's state or attribute, you can add the `group` class to the
parent element, and use any of the `_group*` modifiers on the child element.
```jsx
Hover me
```
This modifer for every pseudo class modifiers like `_groupHover`, `_groupActive`, `_groupFocus`, and `_groupDisabled`,
etc.
## Sibling Selectors
When you need to style an element based on its sibling element's state or attribute, you can add the `peer` class to the
sibling element, and use any of the `_peer*` modifiers on the target element.
```jsx
Hover me
I'll change by bg
```
> Note: This only works for when the element marked with `peer` is a previous siblings, that is, it comes before the
> element you want to start.
## Data Attribute
### LTR and RTL
You can style an element based on the direction of the text using the `_ltr` and `_rtl` modifiers:
```jsx
Hello
```
For this to work, you need to set the `dir` attribute on the parent element. In most cases,you can set this on the
`html` element.
> **Note:** Consider using logical css properties like `marginInlineStart` and `marginInlineEnd` instead their physical
> counterparts like `marginLeft` and `marginRight`. This will reduce the need to use the `_ltr` and `_rtl` modifiers.
### State
You can style an element based on its `data-{state}` attribute using the corresponding `_{state}` modifier:
```jsx
Hello
```
This also works for common states like `data-active`, `data-disabled`, `data-focus`, `data-hover`, `data-invalid`,
`data-required`, and `data-valid`.
```jsx
Hello
```
> Most of the `data-{state}` attributes typically mirror the corresponding browser pseudo class. For example,
> `data-hover` is equivalent to `:hover`, `data-focus` is equivalent to `:focus`, and `data-active` is equivalent to
> `:active`.
### Orientation
You can style an element based on its `data-orientation` attribute using the `_horizontal` and `_vertical` modifiers:
```jsx
Hello
```
## ARIA Attribute
You can style an element based on its `aria-{state}=true` attribute using the corresponding `_{state}` modifier:
```jsx
Hello
```
> Most of the `aria-{state}` attributes typically mirror the support ARIA states in the browser pseudo class. For
> example, `aria-checked=true` is styled with `_checked`, `aria-disabled=true` is styled with `_disabled`.
## Container queries
You can define container names and sizes in your theme configuration and use them in your styles.
```ts
export default defineConfig({
// ...
theme: {
extend: {
containerNames: ['sidebar', 'content'],
containers: {
xs: '40em',
sm: '60em',
md: '80em'
}
}
}
})
```
The default container sizes in the `@pandacss/preset-panda` preset are shown below:
```ts
export const containers = {
xs: '320px',
sm: '384px',
md: '448px',
lg: '512px',
xl: '576px',
'2xl': '672px',
'3xl': '768px',
'4xl': '896px',
'5xl': '1024px',
'6xl': '1152px',
'7xl': '1280px',
'8xl': '1440px'
}
```
Then use them in your styles by referencing using `@/` syntax:
> The default container syntax is `@/`.
```ts
import { css } from '/styled-system/css'
function Demo() {
return (
)
}
```
This will generate the following CSS:
```css
.cq-type_inline-size {
container-type: inline-size;
}
@container (min-width: 60em) {
.\@\/sm:fs_md {
container-type: inline-size;
}
}
```
You can also named container queries:
```ts
import { cq } from 'styled-system/patterns'
function Demo() {
return (
)
}
```
## Reference
Here's a list of all the condition shortcuts you can use in Panda:
| Condition name | Selector |
| ---------------------- | -------------------------------------------------------------------------------------------------|
| \_hover | `&:is(:hover, [data-hover])` |
| \_focus | `&:is(:focus, [data-focus])` |
| \_focusWithin | `&:focus-within` |
| \_focusVisible | `&:is(:focus-visible, [data-focus-visible])` |
| \_disabled | `&:is(:disabled, [disabled], [data-disabled], [aria-disabled=true])` |
| \_active | `&:is(:active, [data-active])` |
| \_visited | `&:visited` |
| \_target | `&:target` |
| \_readOnly | `&:is(:read-only, [data-read-only], [aria-readonly=true])` |
| \_readWrite | `&:read-write` |
| \_empty | `&:is(:empty, [data-empty])` |
| \_checked | `&:is(:checked, [data-checked], [aria-checked=true], [data-state="checked"])` |
| \_enabled | `&:enabled` |
| \_expanded | `&:is([aria-expanded=true], [data-expanded], [data-state="expanded"])` |
| \_highlighted | `&[data-highlighted]` |
| \_complete | `&[data-complete]` |
| \_incomplete | `&[data-incomplete]` |
| \_dragging | `&[data-dragging]` |
| \_before | `&::before` |
| \_after | `&::after` |
| \_firstLetter | `&::first-letter` |
| \_firstLine | `&::first-line` |
| \_marker | `&::marker, &::-webkit-details-marker` |
| \_selection | `&::selection` |
| \_file | `&::file-selector-button` |
| \_backdrop | `&::backdrop` |
| \_first | `&:first-child` |
| \_last | `&:last-child` |
| \_only | `&:only-child` |
| \_even | `&:nth-child(even)` |
| \_odd | `&:nth-child(odd)` |
| \_firstOfType | `&:first-of-type` |
| \_lastOfType | `&:last-of-type` |
| \_onlyOfType | `&:only-of-type` |
| \_peerFocus | `.peer:is(:focus, [data-focus]) ~ &` |
| \_peerHover | `.peer:is(:hover, [data-hover]) ~ &` |
| \_peerActive | `.peer:is(:active, [data-active]) ~ &` |
| \_peerFocusWithin | `.peer:focus-within ~ &` |
| \_peerFocusVisible | `.peer:is(:focus-visible, [data-focus-visible]) ~ &` |
| \_peerDisabled | `.peer:is(:disabled, [disabled], [data-disabled], [aria-disabled=true]) ~ &` |
| \_peerChecked | `.peer:is(:checked, [data-checked], [aria-checked=true], [data-state="checked"]) ~ &` |
| \_peerInvalid | `.peer:is(:invalid, [data-invalid], [aria-invalid=true]) ~ &` |
| \_peerExpanded | `.peer:is([aria-expanded=true], [data-expanded], [data-state="expanded"]) ~ &` |
| \_peerPlaceholderShown | `.peer:placeholder-shown ~ &` |
| \_groupFocus | `.group:is(:focus, [data-focus]) &` |
| \_groupHover | `.group:is(:hover, [data-hover]) &` |
| \_groupActive | `.group:is(:active, [data-active]) &` |
| \_groupFocusWithin | `.group:focus-within &` |
| \_groupFocusVisible | `.group:is(:focus-visible, [data-focus-visible]) &` |
| \_groupDisabled | `.group:is(:disabled, [disabled], [data-disabled], [aria-disabled=true]) &` |
| \_groupChecked | `.group:is(:checked, [data-checked], [aria-checked=true], [data-state="checked"]) &` |
| \_groupExpanded | `.group:is([aria-expanded=true], [data-expanded], [data-state="expanded"]) &` |
| \_groupInvalid | `.group:is(:invalid, [data-invalid], [aria-invalid=true]) &` |
| \_indeterminate | `&:is(:indeterminate, [data-indeterminate], [aria-checked=mixed], [data-state="indeterminate"])` |
| \_required | `&:is(:required, [data-required], [aria-required=true])` |
| \_valid | `&:is(:valid, [data-valid])` |
| \_invalid | `&:is(:invalid, [data-invalid], [aria-invalid=true])` |
| \_userValid | `&:is(:user-valid, [data-user-valid])` |
| \_userInvalid | `&:is(:user-invalid, [data-user-invalid])` |
| \_autofill | `&:autofill` |
| \_inRange | `&:is(:in-range, [data-in-range])` |
| \_outOfRange | `&:is(:out-of-range, [data-outside-range])` |
| \_placeholder | `&::placeholder, &[data-placeholder]` |
| \_placeholderShown | `&:is(:placeholder-shown, [data-placeholder-shown])` |
| \_pressed | `&:is([aria-pressed=true], [data-pressed])` |
| \_selected | `&:is([aria-selected=true], [data-selected])` |
| \_grabbed | `&:is([aria-grabbed=true], [data-grabbed])` |
| \_underValue | `&[data-state=under-value]` |
| \_overValue | `&[data-state=over-value]` |
| \_atValue | `&[data-state=at-value]` |
| \_default | `&:default` |
| \_optional | `&:optional` |
| \_open | `&:is([open], [data-open], [data-state="open"], :popover-open)` |
| \_closed | `&:is([closed], [data-closed], [data-state="closed"])` |
| \_fullscreen | `&:is(:fullscreen, [data-fullscreen])` |
| \_loading | `&:is([data-loading], [aria-busy=true])` |
| \_hidden | `&:is([hidden], [data-hidden])` |
| \_inert | `&:is([inert], [data-inert])` |
| \_current | `&:is([aria-current=true], [data-current])` |
| \_currentPage | `&[aria-current=page]` |
| \_currentStep | `&[aria-current=step]` |
| \_today | `&[data-today]` |
| \_unavailable | `&[data-unavailable]` |
| \_rangeStart | `&[data-range-start]` |
| \_rangeEnd | `&[data-range-end]` |
| \_now | `&[data-now]` |
| \_topmost | `&[data-topmost]` |
| \_motionReduce | `@media (prefers-reduced-motion: reduce)` |
| \_motionSafe | `@media (prefers-reduced-motion: no-preference)` |
| \_print | `@media print` |
| \_landscape | `@media (orientation: landscape)` |
| \_portrait | `@media (orientation: portrait)` |
| \_dark | `.dark &` |
| \_light | `.light &` |
| \_osDark | `@media (prefers-color-scheme: dark)` |
| \_osLight | `@media (prefers-color-scheme: light)` |
| \_highContrast | `@media (forced-colors: active)` |
| \_lessContrast | `@media (prefers-contrast: less)` |
| \_moreContrast | `@media (prefers-contrast: more)` |
| \_ltr | `:where([dir=ltr], :dir(ltr)) &` |
| \_rtl | `:where([dir=rtl], :dir(rtl)) &` |
| \_scrollbar | `&::-webkit-scrollbar` |
| \_scrollbarThumb | `&::-webkit-scrollbar-thumb` |
| \_scrollbarTrack | `&::-webkit-scrollbar-track` |
| \_horizontal | `&[data-orientation=horizontal]` |
| \_vertical | `&[data-orientation=vertical]` |
| \_icon | `& :where(svg)` |
| \_starting | `@starting-style` |
| \_noscript | `@media (scripting: none)` |
| \_invertedColors | `@media (inverted-colors: inverted)` |
| \_pointerFine | `@media (pointer: fine)` |
| \_pointerCoarse | `@media (pointer: coarse)` |
| \_pointerNone | `@media (pointer: none)` |
| \_anyPointerFine | `@media (any-pointer: fine)` |
| \_anyPointerCoarse | `@media (any-pointer: coarse)` |
| \_anyPointerNone | `@media (any-pointer: none)` |
## Custom conditions
Panda lets you create your own conditions, so you're not limited to the ones in the default preset. Learn more about
customizing conditions [here](/docs/design-systems/conditions).
---
## Dynamic styling
How to manage dynamic styling in Panda
While Panda is mainly focused on the statically analyzable styles, you might need to handle dynamic styles in your
project.
> We recommend that you **avoid relying on runtime values for your styles** . Consider using recipes, css variables or
> `data-*` attributes instead.
Here are some ways you can handle dynamic styles in Panda:
## Runtime values
Using a value that is not statically analyzable at build-time will not work in Panda due to the inability to determine
the style values.
```tsx filename="App.tsx"
import { useState } from 'react'
import { css } from '../styled-system/css'
const App = () => {
const [color, setColor] = useState('red.300')
return (
)
}
```
The example above will not work because Panda can't determine the value of `color` at build-time. Here are some ways to
fix this:
### Using Static CSS
Panda supports a [`staticCss`](/docs/design-systems/static) option in the config you can use to pre-generate some styles ahead
of time.
```tsx filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
staticCss: {
css: [
{
properties: {
// β Good: Pre-generate the styles for the color
color: ['red.300']
}
}
]
}
})
```
```tsx filename="Button.tsx"
import { useState } from 'react'
import { styled } from '../styled-system/jsx'
export const Button = () => {
const [color, setColor] = useState('red.300')
// β Good: This will work because `red.300` is pre-generated using `staticCss` config
return
}
```
### Using `token()`
The `token()` function is generated by Panda and contains an object of all tokens by dot-path, allowing you to query for
token's raw value at runtime.
```tsx filename="App.tsx"
import { useState } from 'react'
import { css } from '../styled-system/css'
import { token } from '../styled-system/tokens'
const Component = props => {
return (
Dynamic color with runtime value
)
}
// App.tsx
const App = () => {
const [runtimeColor, setRuntimeColor] = useState('pink.300')
return
}
```
### Using `token.var()`
You could also directly use the `token.var()` function to get a reference to the underlying CSS custom property for a
given token:
```tsx filename="App.tsx"
import { useState } from 'react'
import { token } from '../styled-system/tokens'
const Component = props => {
return (
Dynamic color with runtime value
)
}
const App = () => {
const [runtimeColor, setRuntimeColor] = useState('yellow.300')
return
}
```
## JSX Style Props
Panda supports forwarding JSX style properties to any element in your codebase.
For example, let's say we create a Card component that accepts a `color` prop:
```tsx filename="Card.tsx"
import { styled } from '../styled-system/jsx'
const Card = props => {
return
}
```
Then you add more style props to the Card component in a different file:
```tsx filename="App.tsx"
const App = () => {
return (
Some content
)
}
```
As long as all prop-value pairs are statically extractable, Panda will automatically generate the CSS, so avoid using
runtime values:
```tsx filename="App.tsx"
import { useState } from 'react'
const App = () => {
const [color, setColor] = useState('blue.300')
// β Avoid: Panda can't determine the value of color at build-time
return (
Some content
)
}
```
## Property Renaming
Due to the static nature of Panda, you can't rename properties at runtime.
```tsx filename="App.tsx"
import { Circle, CircleProps } from '../styled-system/jsx'
type Props = {
circleSize?: CircleProps['size']
}
const CustomCircle = (props: Props) => {
const { circleSize = '3' } = props
return (
)
}
```
In this case, use `size` at the call site.
### Alternative
As of v0.8, we added a new `{fn}.raw()` method to css, patterns and recipes. This function is an identity function and
only serves as a hint for the compiler to extract the css.
It can be useful, for example, in Storybook args or custom react props.
```tsx filename="App.tsx"
// mark the object as valid css for the extractor
```
```tsx
export const Funky: Story = {
// mark this as a button recipe usage
args: button.raw({
visual: 'funky',
shape: 'circle',
size: 'sm'
})
}
```
### Custom prop names
`elementWidth="300px"` won't compile to `width`. Use a utility name, `css`, a `*Css` prop, or `css.raw()`:
```tsx
```
See [custom component props](/docs/styling/style-props#custom-component-props).
### Enhanced `css.raw` spreading
> **Added in v1.6.1**
You can also spread `css.raw` objects within nested selectors and conditions for better style composition:
```tsx filename="App.tsx"
import { css } from '../styled-system/css'
const baseStyles = css.raw({ margin: 0, padding: 0 })
const interactive = css.raw({ cursor: 'pointer', transition: 'all 0.2s' })
const component = css({
// Spreading in child selectors
'& p': { ...baseStyles, fontSize: '1rem' },
// Spreading in nested conditions
_hover: {
...interactive,
_dark: { ...interactive, color: 'white' }
}
})
```
## Static expressions
Panda supports static expressions in your styles, as long as they are statically analyzable.
### Static Composition
You can compose different style objects together using the `css.raw()` function.
```tsx filename="App.tsx"
import { css } from 'styled-system/css'
const paragraphSpacingStyle = css.raw({
'& p': { marginBlockEnd: '1em' }
})
export const proseCss = css.raw({
'& h1': paragraphSpacingStyle
})
```
This will result in the following CSS:
```css
/* ... */
@layer utilities {
.\[\&_p\]\:mb_1em p,
.\[\&_h1\]\:\[\&_p\]\:mb_1em h1 p {
margin-block-end: 1em;
}
}
```
### Static Expressions
Panda supports the use of functions to generate the style objects as long they are statically analyzable.
You can only use functions that are defined in the ECMAScript spec such as `Math`, `Object`, `Array`, etc, to support
the evaluation of basic expressions like this:
```ts
import { cva } from '.panda/css'
const getVariants = () => {
const spacingTokens = Object.entries({
sm: 'token(spacing.1)',
md: 'token(spacing.2)'
})
// Generate variants programmatically
const variants = spacingTokens.map(([variant, token]) => [variant, { paddingX: token }])
return Object.fromEntries(variants)
}
const baseStyle = cva({
variants: {
variant: getVariants()
}
})
```
This will generate the following variants object:
```json
{
"sm": { "paddingX": "token(spacing.1)" },
"md": { "paddingX": "token(spacing.2)" }
}
```
And the following CSS
```css
@layer utilities {
.px_token\(spacing\.1\) {
padding-inline: var(--spacing-1);
}
.px_token\(spacing\.2\) {
padding-inline: var(--spacing-2);
}
}
```
## Runtime conditions
Even though we recommend that you first look for better alternatives (such as using
[recipe variants](/docs/styling/recipes)), you may still occasionally need runtime conditions.
When encountering a runtime condition, Panda will first try to resolve it statically. If it can't, it will fallback to
the generating the corresponding CSS for each possible branches.
```tsx
import { useState } from 'react'
import { css } from '../styled-system/css'
import { Stack } from '../styled-system/jsx'
const App = () => {
const [isHovered, setIsHovered] = useState(false)
return (
)
}
```
Since none of the conditions above are statically extractable, Panda will generate css for all possible code path,
resulting in a css that looks like this:
```css
/* ... */
@layer utilities {
.hover\:text_red\.100:where(:hover, [data-hover]) {
color: var(--colors-red-100);
}
.text_red\.200 {
color: var(--colors-red-200);
}
.hover\:text_red\.300:where(:hover, [data-hover]) {
color: var(--colors-red-300);
}
@media screen and (min-width: 768px) {
.hover\:md\:text_red\.400:where(:hover, [data-hover]) {
color: var(--colors-red-400);
}
}
.text_red\.500 {
color: var(--colors-red-500);
}
.text_red\.600 {
color: var(--colors-red-600);
}
}
/* ... */
```
## Referenced values
Although you should have your styles inlined most of the time, maybe you want to store a value in a variable and re-use
in multiple places. This should be fine as long as you keep it statically analyzable.
Here's a short list of things to avoid:
- Variables that are not defined in the same file
- Variables resulting from a function call (e.g. `const color = getColor()`)
> If you don't know what value a variable holds with a quick glance, Panda won't be able to either.
```tsx
import { css } from '../styled-system/css'
// β Good: All values are statically extractable
const mainColor = 'red.300'
const sizes = { sm: '12px', md: '16px', '2xl': '42px' }
const App = () => {
return (
)
}
```
### Runtime reference on known objects
Using a more complex but still common example :
```tsx
import { useState } from 'react'
import { css } from '../styled-system/css'
const colorByType = {
primary: 'red.300',
secondary: 'blue.300',
tertiary: 'green.300'
}
const Section = () => {
const [type, setType] = useState('primary')
// β Avoid: since only "gray.100" is statically extractable here
// This will not work as expected, the color CSS won't be generated
return β Will not be extracted
}
```
Even though `colorByType` is statically analyzable, Panda does not _yet_ support this kind of automatic extraction
fallback. This is the perfect opportunity to use the [recipes](/docs/styling/recipes).
```tsx
import { useState } from 'react'
import { cva } from '../styled-system/cva'
const sectionRecipe = cva({
base: { color: 'gray.100' },
variants: {
type: {
primary: { color: 'red.300' },
secondary: { color: 'blue.300' },
tertiary: { color: 'green.300' }
}
}
})
const Section = () => {
const [type, setType] = useState('primary')
// β Good: This will work as expected
return β With a recipe
}
```
Not only did you get the same end result, but you also got a more readable and maintainable code !
You can now :
- add more variants to your recipe
- add more properties
- use a shorthand or a condition
All of this with complete type-safety and without having to make drastic changes to the component.
> Note that you can also [integrate this recipe directly into your theme](/docs/styling/recipes) if you want to only
> generate the CSS that you use, among other things
## Config recipes
Config recipes only emit CSS for variants Panda can statically analyze. `button({ size })` works at runtime, but you'll
only get `defaultVariants` in the stylesheet unless you add [`staticCss`](/docs/design-systems/static#generating-recipes) or
use string literals in the call.
See [Dynamic variant props](/docs/styling/recipes#dynamic-variant-props).
## Summary
### What you can do
```tsx
// β Good: Conditional styles
// β Good: Arbitrary value
// β Good: Arbitrary selector
span": { color: "red.100" } }} />
// β Good: Runtime value (with config.`staticCss`)
const Button = () => {
const [color, setColor] = useState('red.300')
return
}
// β Good: Runtime condition
// β Good: Referenced value
```
### What you can't do
```tsx
// β Avoid: Runtime value (without config.`staticCss`)
const Button = () => {
const [color, setColor] = useState('red.300')
return
}
// β Avoid: Referenced value (not statically analyzable or from another file)
const CustomCircle = (props) => {
const { circleSize = '3' } = props
return (
)
}
```
---
## Using Ember
Easily use Panda with Ember with our dedicated integration.
This guide shows you how to set up Panda CSS in an Ember project using PostCSS.
## Start a new project
### Create Ember project
To get started, we will need to create a new Ember project using the `embroider` build system. We will name our project
`test-app` but you can name it whatever you want.
{/* */}
```bash
pnpm dlx ember-cli@latest new test-app --embroider --no-welcome --typescript --pnpm
```
```bash
npx ember-cli@latest new test-app --embroider --no-welcome --typescript
```
```bash
yarn dlx ember-cli@latest new test-app --embroider --no-welcome --typescript --yarn
```
```bash
bunx ember-cli@latest new test-app --embroider --no-welcome --typescript --skip-install
```
{/* */}
Enter the newly created directory:
```bash
cd test-app
```
### Install Panda
Install panda and its peer dependencies, as well as `postcss-loader`. Run the init command to generate the
`panda.config.ts` and `postcss.config.cjs` file.
{/* */}
```bash
pnpm install -D @pandacss/dev postcss postcss-loader
pnpm panda init --postcss
```
```bash
npm install -D @pandacss/dev postcss postcss-loader
npx panda init --postcss
```
```bash
yarn add -D @pandacss/dev postcss postcss-loader
yarn panda init --postcss
```
```bash
bun add -D @pandacss/dev postcss postcss-loader
bun panda init --postcss
```
{/* */}
### Enable PostCSS support
In your `ember-cli-build.js` file, configure PostCSS to process your CSS files.
```js {12-23} filename="ember-cli-build.js"
'use strict'
const EmberApp = require('ember-cli/lib/broccoli/ember-app')
module.exports = function (defaults) {
const app = new EmberApp(defaults, {
// Add options here
})
const { Webpack } = require('@embroider/webpack')
return require('@embroider/compat').compatBuild(app, Webpack, {
packagerOptions: {
webpackConfig: {
module: {
rules: [
{
test: /\.css$/i,
use: ['postcss-loader']
}
]
}
}
}
// other options...
})
}
```
### Configure the PostCSS plugin
Add the `.embroider` folder to the allow list so the Panda PostCSS plugin picks up your app CSS files.
```js {4} filename="postcss.config.cjs"
module.exports = {
plugins: {
'@pandacss/dev/postcss': {
allow: [/node_modules\/.embroider/]
}
}
}
```
### Update package.json scripts
Open the `package.json` file and update the `scripts` section as follows:
```diff {3} filename="package.json"
{
"scripts": {
+ "prepare": "panda codegen",
// ...
}
}
```
- `"prepare"` - script that will run Panda CSS CLI codegen before each build. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the content
Make sure that all of the paths of your Ember components are included in the `include` section of the `panda.config.ts`
file. Set the `outdir` to the app folder so the code can be imported in your Ember app. Adjust the `importMap`
accordingly to reflect your app name.
```js {8,19-22} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ['./app/**/*.{js,ts,gjs,gts}'],
// Files to exclude
exclude: [],
// Useful for theme customization
theme: {
extend: {}
},
// The output directory for your css system
outdir: 'app/styled-system',
// Configure the import map to use your project name
importMap: 'test-app/styled-system'
})
```
### Configure the entry CSS with layers
Add this code to an `app/index.css` file.
```css filename="app/index.css"
@layer reset, base, tokens, recipes, utilities;
```
Next, import the file in your `app/app.ts` file.
```ts {5} filename="app/app.ts"
import Application from '@ember/application'
import Resolver from 'ember-resolver'
import loadInitializers from 'ember-load-initializers'
import config from 'test-app/config/environment'
import 'test-app/index.css'
export default class App extends Application {
modulePrefix = config.modulePrefix
podModulePrefix = config.podModulePrefix
Resolver = Resolver
}
loadInitializers(App, config.modulePrefix)
```
### Start your build process
Run the following command to start your development server.
{/* */}
```bash
pnpm start
```
```bash
npm run start
```
```bash
yarn start
```
```bash
bun start
```
{/* */}
### Start using Panda
Now you can start using Panda CSS in your project.
```ts filename="app/components/hello-panda.ts"
import Component from '@glimmer/component'
import { css } from 'test-app/styled-system/css'
export default class HelloPanda extends Component {
style = css({ fontSize: '5xl', fontWeight: 'bold' })
}
```
```hbs filename="app/components/hello-panda.hbs"
{{outlet}}
```
> For the best developer experience, set up
> [template tag component authoring format](https://guides.emberjs.com/release/components/template-tag-format/) in
> Ember.
## Troubleshooting
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
`tsconfig.json` file:
```json filename="tsconfig.json"
{
// ...
"include": ["app/styled-system"]
}
```
---
## Migrating from Emotion
Migrate your project from Emotion to Panda and see how css, styled, and theming map across.
This guide outlines the steps needed to migrate your project from Emotion to Panda and highlights key design
differences between the two.
> **Note:** Emotion is a lower-level library than the others in this section, it doesn't ship a `sx` prop, a variant
> system, color modes, or layout components itself. Teams typically build those on top of Emotion by hand, or pull
> in Theme UI or Chakra to get them. Where that's the case below, this guide describes the common hand-rolled
> pattern, not something Emotion itself provides.
Here are some similarities between the two:
- Both support tagged template literals and object syntax for styles.
- Both support a `styled` factory for creating styled components.
- Both let you define theme tokens and read them back inside style definitions.
Here's where they differ.
## Performance
Emotion computes and injects styles at runtime, in the browser (and during SSR), every `css()` call and every
`styled` component render re-evaluates its styles. Panda extracts style objects at build time and ships static CSS,
so there's no runtime style computation cost, this matters most in large lists or deeply nested trees where
Emotion's per-render style computation adds up.
## Theming
Emotion's `ThemeProvider` passes a plain object through context, there's no required shape or token wrapper:
```jsx
import { ThemeProvider } from '@emotion/react'
const theme = { colors: { brand: '#0ea5e9' } }
export default function App({ children }) {
return {children}
}
```
```jsx
import { useTheme } from '@emotion/react'
function Button() {
const theme = useTheme()
return
}
```
Panda doesn't need a provider or a hook to read the theme, tokens are resolved at build time into the generated
`css()`/`cva()` functions directly:
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
tokens: {
colors: {
brand: { value: '#0ea5e9' }
}
}
}
}
})
```
```jsx
import { css } from '../styled-system/css'
```
See [Tokens](/docs/theming/tokens).
## The `sx` prop, and the `css` prop
Emotion's own inline mechanism is the `css` prop, enabled with the `@jsxImportSource @emotion/react` pragma (or the
older `/** @jsx jsx */` plus an explicit `jsx` import):
```jsx
/** @jsxImportSource @emotion/react */
```
Panda's `styled` factory accepts a `css` prop the same way, no pragma or special JSX runtime required:
```jsx
import { styled } from '../styled-system/jsx'
```
See [JSX Style Props](/docs/styling/style-props) for the rest of Panda's inline styling options.
## Variants
Plain Emotion has no variant system, a common hand-rolled pattern is a function that takes props and returns a style
object or class list:
```jsx
import styled from '@emotion/styled'
const Button = styled.button`
${props => (props.variant === 'primary' ? `background: blue;` : `background: gray;`)}
`
```
Panda's [recipes](/docs/styling/recipes) replace this with a typed, declarative variant map, no manual prop-to-style
branching:
```ts filename="button.ts"
import { cva } from '../styled-system/css'
export const button = cva({
variants: {
variant: {
primary: { bg: 'blue.500' },
secondary: { bg: 'gray.500' }
}
}
})
```
```jsx
```
`button({ variant: 'accent' })` is a type error if `accent` was never defined, the hand-rolled version above has no
equivalent safety net. See [Recipes](/docs/styling/recipes) and [Slot Recipes](/docs/styling/slot-recipes).
## Color Modes
Emotion has no built-in color-mode concept, teams typically swap the whole theme object passed to `ThemeProvider`
based on some app state, and every user of the theme reads the current mode's value through `useTheme`.
Panda's semantic tokens define the light/dark pair once, no theme-swapping or context read at the usage site:
```ts filename="panda.config.ts"
theme: {
extend: {
semanticTokens: {
colors: {
bg: { value: { base: 'white', _dark: 'gray.900' } }
}
}
}
}
```
```jsx
```
See [Theme](/docs/theming/theme) and [Multiple Themes](/docs/theming/multiple-themes).
## Global Styles
Emotion's `Global` component injects styles into the global scope from within your component tree:
```jsx
import { Global, css } from '@emotion/react'
```
Panda's equivalent is the `globalCss` key in `panda.config.ts`, declared once in config rather than rendered as a
component:
```ts filename="panda.config.ts"
export default defineConfig({
globalCss: {
body: { margin: 0 }
}
})
```
See [Global Styles](/docs/styling/global-styles).
## Component Styles
Emotion has no built-in layout primitives, `styled.div`/`styled.section` give you a styled element, but nothing like
a pre-built `Box` or `Stack`. Panda ships [patterns](/docs/styling/patterns) for common layout shapes as both a
function and a JSX component:
```jsx
import { Box, Stack } from '../styled-system/jsx'
Item
```
Emotion's `keyframes` helper for animations does carry over directly in spirit, Panda defines keyframes in
[`theme.keyframes`](/docs/theming/theme#keyframes) instead of an inline `keyframes()` call, and composes them into
named, reusable presets with [Animation Styles](/docs/theming/animation-styles).
## Conclusion
Because Emotion is a lower-level library, this migration is less about translating equivalent built-in features
(most of what Panda has built in, Emotion doesn't have at all) and more about replacing hand-rolled patterns, a
variant function, a manually-swapped theme object, with Panda's typed, declarative equivalents.
## See also
- [Migration strategy](/docs/styling/migration-strategy) for running both libraries side by side during the
migration.
- [Theme UI](/docs/styling/theme-ui) if your app also uses Theme UI's layer on top of Emotion, that guide covers the
`sx` prop, variants, and color modes Theme UI adds.
---
## The extend keyword
What is and how to to use the extend keyword
The `extend` keyword allows you to extend the default Panda configuration. It is useful when you want to add your own
customizations to Panda, without erasing the default `presets` values (`conditions`, `tokens`, `utilities`, etc).
It will (deeply) merge your customizations with the default ones, instead of replacing them.
The `extend` keyword allows you to extend the following parts of Panda:
- [conditions](/docs/design-systems/conditions)
- [theme](/docs/theming/theme)
- [recipes](/docs/styling/recipes) (included in theme)
- [patterns](/docs/design-systems/patterns)
- [utilities](/docs/design-systems/utilities)
- [globalCss](/docs/styling/writing-styles#global-styles)
- [staticCss](/docs/design-systems/static)
> These keys are all allowed in [presets](/docs/design-systems/presets).
## Example
After running the `panda init` command you should see something similar to this:
```ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// ...
// Useful for theme customization
theme: {
extend: {} // π it's already there! perfect, now you just need to add your customizations in this object
}
// ...
})
```
Let's say you want to add a new color to the default theme. You can do it like this:
```ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
colors: {
primary: { value: '#ff0000' }
}
}
}
})
```
This will add a new color to the default theme, without erasing the other ones.
Now, let's say we want to create new property `br` that applies a border radius to an element.
```ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
utilities: {
extend: {
br: {
className: 'rounded', // css({ br: "sm" }) => rounded-sm
values: 'radii', // connect values to the radii tokens
transform(value) {
return { borderRadius: value }
}
}
}
}
})
```
What if this utility was coming from a preset (`@acme/my-preset`) ? You can extend any specific part, as it will be
deeply merged with the existing one:
```ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
presets: ['@acme/my-preset']
utilities: {
extend: {
br: {
className: 'br' // css({ br: "sm" }) => br-sm
}
}
}
})
```
## Removing something from a preset
Let's say you want to remove the `br` utility from the `@acme/my-preset` preset. You can do it like this:
```ts
import { defineConfig } from '@pandacss/dev'
import myPreset from '@acme/my-preset'
const { br, ...utilities } = myPreset.utilities
export default defineConfig({
presets: ['@acme/my-preset']
utilities: {
extend: {
...utilities, // π we still want the other utilities from this preset
// your customizations here
}
}
})
```
## Removing something from the base presets
Let's say you want to remove the `stack` pattern from the `@pandacss/preset-base` preset (included by default).
You can pick only the parts that you need with and spread the rest, like this:
```ts
import pandaBasePreset from '@pandacss/preset-base'
// omitting stack here
const { stack, ...pandaBasePresetPatterns } = pandaBasePreset.patterns
export default defineConfig({
// π omit `@pandacss/preset-base` from `presets` so it isn't resolved β Panda doesn't add it for you
presets: ['@pandacss/preset-panda'], // π we still want the tokens, breakpoints and textStyles from this preset
patterns: {
extend: {
...pandaBasePresetPatterns
// your customizations here
}
}
})
```
## Minimal setup
If you want to use Panda with the bare minimum, without any of the defaults, you can read more about it
[here](/docs/design-systems/minimal-setup)
## FAQ
### Why is my preset overriding the base one, even after adding it to the array?
You might have forgotten to include the `extend` keyword in your config. Without `extend`, your preset will completely
replace the base one, instead of merging with it.
---
## Frequently Asked Questions
Frequently asked questions and how to resolve common issues
## How does Panda manage style conflicts ?
When you combine shorthand and longhand properties, Panda will resolve the styles in a predictable way. The shorthand
property will take precedence over the longhand property.
```jsx
import { css } from '../styled-system/css'
const styles = css({
paddingTop: '20px',
padding: '10px'
})
```
The styles generated at build time will look like this:
```css
@layer utilities {
.p_10px {
padding: 10px;
}
.pt_20px {
padding-top: 20px;
}
}
```
---
## Imported Image is not working in Vite App
This is a known limitation of Panda due to our static extraction approach.
> Think of it this way: there's no way for the compiler to know what the final asset URL will be since Vite controls it.
We recommend moving the imported `backgroundImage` to the `style` attribute.
```jsx
import myImageBackground from './my-image.png'
const Demo = () => {
return (
Hello World
)
}
```
---
## How to get Panda to work with Jest?
If you run into error messages like `SyntaxError: Unexpected token 'export'` when running Jest tests. Here's what you
can:
In your tsconfig, add
```json
{
"compilerOptions": {
"allowJs": true
}
}
```
In your Jest configuration, add the `ts-jest` transformer:
```ts
export default {
// ...
transform: {
'^.+\\.tsx?$': 'ts-jest',
'^.+\\.(ts|tsx|js|jsx)?$': 'ts-jest'
}
}
```
In your Panda config, set the `outExtension` to `js`:
```ts
export default defineConfig({
// ...
outExtension: 'js'
})
```
---
## HMR does not work when I use `tsconfig` paths?
Panda tries to automatically infer and read the custom paths defined in `tsconfig.json` file. However, there might be
scenarios where the hot module replacement doesn't work.
To fix this add the `importMap` option to your `panda.config.js` file, setting it's value to the specified `paths` in
your `tsconfig.json` file.
```json
// tsconfig.json
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@my-path/*": ["./styled-system/*"]
}
}
}
```
```js
// panda.config.js
module.exports = {
importMap: '@my-path'
}
```
This will ensure that the paths are resolved correctly, and HMR works as expected.
---
## HMR not triggered
If you are having issues with HMR not being triggered after a `panda.config.ts` change (or one of its
[dependencies](/docs/reference/config#dependencies)), you can manually specify the files that should trigger a rebuild
by adding the following to your `panda.config.ts`:
```js filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// ...
dependencies: ['path/to/files/**.ts']
})
```
---
## Why are my styles not applied?
Check that the [`@layer` rules](/docs/styling/cascade-layers#layer-css) are set and the corresponding `.css` file is
included. [If you're not using `postcss`](/docs/styling/cli), ensure that `styled-system/styles.css` is imported
and that the `panda` command has been run (or is running with `--watch`).
---
## How can I debug the styles?
You can use the `panda debug` to debug design token extraction & css generated from files.
If the issue persists, you can try looking for it in the [issues](https://github.com/chakra-ui/panda/issues) or in the
[discord](https://discord.gg/VQrkpsgSx7). If you can't find it, please create a minimal reproduction and submit
[a new github issue](https://github.com/chakra-ui/panda/issues/new/choose) so we can help you.
---
## Why is my IDE not showing `styled-system` imports?
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
tsconfig.json file.
---
## How do I get a type with each recipe properties?
You can get a [`config recipe`](/docs/styling/recipes#config-recipe) properties types by using `XXXVariantProps`. Let's
say you have a `config recipe` named `button`, you can import its type like this:
```ts
import { button, type ButtonVariantProps } from '../styled-system/recipes'
```
---
You can get an [`atomic recipe`](/docs/styling/recipes#atomic-recipe-or-cva) properties types by using `RecipeVariantProps`.
Let's say you have a `atomic recipe` named `button`, you can get its type like this:
```ts
import { cva, type RecipeVariantProps } from '../styled-system/css'
export type ButtonVariantProps = RecipeVariantProps
```
---
## How do I split recipe props from the rest?
You can split recipe props by using `xxx.splitVariantProps`. Let's say you have a `recipe` named `button`, you can split
its props like this:
```tsx Button.tsx {8}
import { css, cx } from '../styled-system/css'
import { ButtonVariantProps, button } from '../styled-system/recipes'
interface ButtonProps extends ButtonVariantProps {
children: React.ReactNode
}
export function Button(props: ButtonProps) {
const { children, ...rest } = props
const [buttonProps, cssProps] = button.splitVariantProps(rest)
return
}
```
The same `xxx.splitVariantProps` method is available for both `config recipes` and `atomic recipes`.
---
## How do I reference a token value or css var?
You can reference a token value or it's associated css variable using the
[`token` function](/docs/theming/usage#vanilla-js). This function allows you to access and use the values stored in your
theme tokens at runtime.
```tsx
import { token } from '../styled-system/tokens'
function App() {
return (
)
}
```
---
## Should I commit the styled-system folder?
Just like the `node_modules` folder, you most likely don't want to commit the `styled-system` folder. It contains code
that is auto-generated and can be re-generated at any time.
---
## How does Panda work?
When running `pnpm panda`, here's what's happening under the hood:
- **Load Panda context**:
- Find and evaluate app config, merge result with presets.
- Create panda context: prepare code generator from config, parse user's file as AST.
- **Generating artifacts**:
- Write lightweight JS runtime and types to output directory
- **Extracting used styles in app code**:
- Run parser on each user's file: identify and extract styles, compute CSS, write to styles.css.
---
## I'm seeing a "Could not resolve xxx" error with esbuild/tsup. What should I do?
In such a case, check the [`outExtension`](/docs/reference/config#outextension) in your `panda.config` and set it to
"js". This will ensure your modules are resolved correctly.
---
## Why does importing `styled` not exist?
You should use [`config.jsxFramework`](/docs/styling/style-props#configure-jsx) when you need to import styled
components. You can then use the [`jsxFactory`](/references/config#jsxfactory) option to set the name of the factory
component.
---
## Why is my preset overriding the base one, even after adding it to the array?
You might have forgotten to include the `extend` keyword in your config. Without `extend`, your preset will completely
replace the base one, instead of merging with it.
---
## Why is my base condition not working in this example?
```ts
css({ color: { _base: 'red.600', _dark: 'white' } })
```
You used `_base` instead of `base`, there is no underscore `_`.
---
## What's the difference between using `defineConfig()` vs `definePreset()`
`defineConfig` is intended to be used in your app config, and will show you all the config keys that are available.
`definePreset` will only show you the config keys that will be merged into an app's config, the rest will be ignored.
---
## How can I completely override the default tokens?.
If you want to **completely override all** of the default presets theme tokens, you can omit the `extends` keyword from
your `theme` config object.
If you want to **keep some of the defaults**, you can install the `@pandacss/preset-panda` package, import it, then
specifically pick what you need in there (or use the JS spread operator `...` and override the other keys).
---
## How do I make a design system / component library with Panda?
There is a detailed guide on how to do this [here](/docs/design-systems/overview).
---
## Can I use dynamic styles with Panda?
Yes, you can use dynamic styles with Panda. More on that [here](/docs/styling/dynamic-styling#runtime-conditions).
---
## Should I use atomic or config recipes ?
[Config recipes](/docs/styling/recipes#config-recipe) are generated just in time, meaning that only the recipes and
variants you use will exist in the generated CSS, regardless of the number of recipes in the config.
This contrasts with [Atomic recipes](/docs/styling/recipes#atomic-recipe-or-cva) (cva), which generates all of the variants
regardless of what was used in your code. The reason for this difference is that all `config.recipes` are known at the
start of the panda process when we evaluate your config. In contrast, the CVA recipes are scattered throughout your
code. To get all of them and find their usage across your code, we would need to scan your app code multiple times,
which would not be ideal performance-wise.
When dealing with simple use cases, or if you need code colocation, or even avoiding dynamic styling, atomic recipes
shine by providing all style variants. Config recipes are preferred for design system components, delivering leaner CSS
with only the styles used. Choose according to your component needs.
---
## Why does the panda codegen command fail ?
If you run into any error related to "Transforming const to the configured target environment ("es5") is not supported
yet", update your tsconfig to use es6 or higher:
```json filename="tsconfig.json"
{
"compilerOptions": {
"target": "es6"
}
}
```
---
## How can I generate all possible CSS variants at build time?
While it's possible to generate all variants, even unused ones, by using
[`config.staticCss`](https://panda-css.com/docs/styling/dynamic-styling#using-static-css), it's generally **not
recommended** to use it for more than a few values. However, keep in mind this approach compromises one of Panda's
strengths: lean, usage-based CSS generation.
---
## Can I use one-off media query and other at rules?
Yes, you can! You can apply one-off media queries and other at rules (such as `@container`, `@supports`) in your CSS as
shown below:
```javascript
css({
containerType: 'size',
'@media (min-width: 10px)': {
fontSize: 'xl',
color: 'blue.300'
},
'@container (min-width: 10px)': {
fontSize: '2xl',
color: 'green.300'
},
'@supports (display: flex)': {
fontSize: '3xl',
color: 'red.300'
}
})
```
---
## How can I prevent other libraries from overriding my styles?
You can use
[Layer Imports]()
to prevent other libraries from overriding your styles.
First of all you cast the css from the other library(s) to a css layer:
```css
@import url('bootstrap.css') layer(bootstrap);
@import url('ionic.css') layer(ionic);
```
Then update the default layer list to deprioritize the styles from the other library(s):
```css
@layer bootstrap, reset, base, token, recipes, utilities;
@layer ionic, reset, base, token, recipes, utilities;
```
---
## Using Gatsby
Easily use Panda with Gatsby with our dedicated integration.
This guide shows you how to set up Panda CSS in a Gatsby project using PostCSS.
### Create Gatsby project
To get started, we will need to create a new Gatsby project. We will name our project `test-app` but you can name it
whatever you want.
If you don't enter any parameter, the CLI will guide you through the process of creating a new Gatsby app.
```bash
npm init gatsby
```
You will be asked a few questions, answer them as follows:
```
β What would you like to call your site? ... My Gatsby Site
β What would you like to name the folder where your site will be created? ... projects/ test-app
β Will you be using JavaScript or TypeScript? ... TypeScript
β Will you be using a CMS? ... No (or I'll add it later)
β Would you like to install a styling system? ... No (or I'll add it later)
β Would you like to install additional features with other plugins? ... No items were selected
```
Enter the newly created directory:
```bash
cd test-app
```
### Install Panda CSS
Install Panda CSS and `gatsby-plugin-postcss` to your project. After that run the `panda init` command to setup Panda
CSS in your project.
```bash
npm install -D @pandacss/dev postcss gatsby-plugin-postcss
npx panda init --postcss
```
### Setup the Gatsby PostCSS plugin
Include the plugin in your `gatsby-config.ts` file. Check out the
[official documentation](https://www.gatsbyjs.com/plugins/gatsby-plugin-postcss/) for more information.
```ts {9} filename="gatsby-config.ts"
import type { GatsbyConfig } from 'gatsby'
const config: GatsbyConfig = {
siteMetadata: {
title: `My Gatsby Site`,
siteUrl: `https://www.yourdomain.tld`
},
graphqlTypegen: true,
plugins: [`gatsby-plugin-postcss`]
}
export default config
```
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3} filename="package.json"
{
"scripts": {
+ "prepare": "panda codegen",
"develop": "gatsby develop",
"start": "gatsby develop",
"build": "gatsby build",
"serve": "gatsby serve",
"clean": "gatsby clean",
"typecheck": "tsc --noEmit"
}
}
```
- `"prepare"` - script that will run Panda CSS CLI codegen before each build. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the content
Make sure that all of the paths of your React components are included in the `include` section of the `panda.config.ts`
file.
> If you use [GraphQL Typegen](/docs/how-to/local-development/graphql-typegen/), you'll need to update the `include` to
> avoid infinite loop due to generated `src/gatsby-types.d.ts`.
```js {6} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
preflight: true,
// Where to look for your css declarations
include: ['./src/pages/*.{js,jsx,ts,tsx}', './src/components/**/*.{js,jsx,ts,tsx}'],
exclude: [],
outdir: 'styled-system'
})
```
### Configure the entry CSS with layers
Create `src/styles/index.css` file and add the following content:
```css filename="src/styles/index.css"
@layer reset, base, tokens, recipes, utilities;
```
### Import the entry CSS
Create a `gatsby-browser.ts` file in the root of your project and add the following content:
```ts filename="gatsby-browser.ts"
import './src/styles/index.css'
```
### Start your build process
Run the following command to start your development server.
```bash
npm run develop
```
### Start using Panda
Now you can start using Panda CSS in your project. Here is the snippet of code that you can use in your
`src/pages/index.tsx` file.
```tsx {3,7} filename="src/pages/index.tsx"
import * as React from 'react'
import type { HeadFC, PageProps } from 'gatsby'
import { css } from '../../styled-system/css'
const IndexPage: React.FC = () => {
return
Hello πΌ!
}
export default IndexPage
export const Head: HeadFC = () => Home Page
```
## Troubleshooting
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
`tsconfig.json` file:
```json filename="tsconfig.json"
{
// ...
"include": ["src", "styled-system"]
}
```
---
## Welcome to Panda
The universal design system solution for the web
Panda is a styling engine that generates styling primitives to author atomic CSS and recipes in a type-safe and readable
manner.
Panda combines the developer experience of CSS-in-JS and the performance of atomic CSS. It leverages static analysis to
scan your JavaScript and TypeScript files for JSX style props and function calls, generating styles on-demand (aka
Just-in-Time)
> TLDR; Panda is a CSS-in-JS engine that generates atomic CSS at build time (via CLI or PostCSS)
## Why Panda
- **Static analysis:** Panda parses your styles at build time and generates plain CSS files that work in any
JavaScript framework.
- **Type safety:** Panda combines `csstype` with auto-generated typings, so CSS properties and design tokens are
type-checked as you write them.
- **Performance:** styles compile down to atomic CSS ahead of time, so there's no runtime style engine shipped to the
browser.
- **Developer experience:** recipes, patterns, design tokens, and JSX style props give you a consistent way to author
styles without hand-rolling your own system.
- **Modern CSS:** generated styles use cascade layers, CSS variables, and modern selectors like `:where` and `:is`.
Here's what using Panda looks like in practice:
```jsx
import { css } from '../styled-system/css'
import { circle, stack } from '../styled-system/patterns'
function App() {
return (
John Doe
john@doe.com
)
}
```
## Installation
Panda can be installed as a CLI tool, a PostCSS plugin, through a framework-specific guide, or inside Storybook.
## Next Steps
Get familiar with the core features and concepts in Panda.
## Playground
You can use the [online playground](https://play.panda-css.com) to get a taste of what Panda can do.
- See the live results of your JSX code
- Inspect what panda can extract using static analysis from your code
- Preview the statically generated `.css` files
## Acknowledgement
The development of Panda was only possible due to the inspiration and ideas from these amazing projects.
- [Chakra UI](https://chakra-ui.com/) - where it all started
- [Vanilla Extract](https://vanilla-extract.style/) - for inspiring the utilities API
- [Stitches](https://stitches.dev/) - for inspiring the recipes and variants API
- [Tailwind CSS](https://tailwindcss.com/) - for inspiring the JIT compiler and strategy
- [Class Variance Authority](https://cva.style/) - for inspiring the `cva` name
- [Styled System](https://styled-system.com/) - for the initial idea of Styled Props
- [Linaria](https://linaria.dev/) - for inspiring the initial atomic css strategy
- [Uno CSS](https://unocss.dev) - for inspiring the studio and astro integration
---
## Global Styles
How to work with resets, global styles, and global CSS variables in Panda.
Panda groups global styles into reset and base layers so you can control defaults predictably and override them safely.
## Layers overview
- **@layer reset**: Preflight/reset styles, enabled with `preflight`.
- **@layer base**: Your additional global styles via `globalCss`.
> See also: [Cascade layers](/docs/styling/cascade-layers)
## Reset (preflight)
Enable or scope the reset styles.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
preflight: true
})
```
Scope and level:
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
preflight: { scope: '.extension', level: 'element' }
})
```
## Exposed global CSS variables
These variables are used by the reset and defaults. Set them in `globalCss`:
- `--global-font-body`
- `--global-font-mono`
- `--global-color-border`
- `--global-color-placeholder`
- `--global-color-selection`
- `--global-color-focus-ring`
## Setting global styles (base)
Use `globalCss` to define additional global styles and set variables.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// ...
globalCss: {
html: {
'--global-font-body': 'Inter, sans-serif',
'--global-font-mono': 'Mononoki Nerd Font, monospace',
'--global-color-border': 'colors.gray.400',
'--global-color-placeholder': 'rgba(0,0,0,0.5)',
'--global-color-selection': 'rgba(0,115,255,0.3)',
'--global-color-focus-ring': 'colors.blue.400'
}
}
})
```
### Theming patterns
You can set variables on `:root`, a `.dark` class, or via media queries.
```css
:root {
--global-color-border: oklch(0.8 0 0);
}
.dark {
--global-color-border: oklch(0.72 0 0);
}
@media (prefers-color-scheme: dark) {
:root {
--global-color-border: oklch(0.72 0 0);
}
}
```
## Custom global variables (`globalVars`)
Define additional global CSS variables or `@property` entries.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
globalVars: {
'--button-color': {
syntax: '',
inherits: false,
initialValue: 'blue'
}
}
})
```
> Keys from `globalVars` are suggestable in style objects and generated near your tokens at `cssVarRoot`.
### Variables owned by a utility
A utility that writes a CSS variable declares the registration itself, so the two can't drift apart:
```ts filename="panda.config.ts"
export default defineConfig({
utilities: {
blur: {
className: 'blur',
globalVars: {
'--blur': { syntax: '*', inherits: false }
},
transform: (value) => ({ '--blur': `blur(${value})` })
}
}
})
```
Only the utility that *writes* the variable needs to declare it. A second utility that reads it β `filter: 'auto'`
composing `var(--blur, )` β declares nothing.
These merge into `globalVars`, and Panda drops any registration your stylesheet never references. Declaring one
costs nothing until it's used, which is how `preset-base` registers dozens of them without charging projects that
don't mask, transform, or filter.
Two rules keep collisions honest:
- Two utilities registering the same name with different definitions is a config error. A CSS variable has one
registration for the whole document, so share one definition between them.
- Putting a plain value on a name a utility registered warns, because the value drops the `@property` registration
and starts the variable inheriting. Pass a full `@property` object if you meant to retune it. The warning only
appears when your stylesheet actually reads that variable, so reusing a name the preset happens to reserve is
fine as long as the two never meet.
### Older browsers
`@property` needs Chrome 85+, Safari 16.4+, or Firefox 128+. Engines without it skip the registration entirely, so a
declaration that reads an unregistered variable is dropped β the utility silently does nothing.
Set `optimize.propertyFallback` to also seed the defaults as plain declarations:
```ts filename="panda.config.ts"
export default defineConfig({
optimize: { propertyFallback: true }
})
```
Panda derives the seeds from the registrations it actually emitted, so you only pay for the variables your project
uses. Modern engines ignore them.
## Troubleshooting
- **Global styles aren't applied:** Confirm `preflight` is enabled (if you expect reset), and ensure your selector
(`html`, `:root`, `.dark`, etc.) matches the element where variables are set.
- **Global styles are overridden by utilities or component styles:** Verify layer order and specificity. Ensure
`@layer reset` and `@layer base` are emitted before utilities. If you customize insertion or injection order (SSR,
framework plugins), preserve `@layer` order so globals are not overridden.
---
## How Panda works
The mental model behind Panda's static extraction, why css() calls don't cost anything at runtime.
Panda looks like a normal CSS-in-JS library at the call site, `css({ bg: 'red.500' })` returns a class name. What
makes it different is that nothing runs in the browser to produce that class name. This page is the mental model for
why, useful before you hit [Dynamic Styles](/docs/styling/dynamic-styling) and wonder why some patterns don't work.
## Two separate steps, not one
It's easy to assume "generating my styled-system" and "generating my CSS" are the same step. They're not, and Panda
actually runs them in this order every time you build:
1. **Codegen.** Panda reads `panda.config.ts`, resolves your tokens, utilities, conditions, and recipes, and writes
the `styled-system` package, the actual `css()`, `cva()`, pattern, and token files you import from. This step
doesn't look at your application code at all, it only depends on your config.
2. **Extraction.** Panda then scans your source files (everything matched by [`include`](/docs/reference/config#include))
for calls to the functions codegen just produced, `css()`, `cva()`, JSX style props, and so on. Only the styles
your code actually calls get turned into CSS rules. Everything else in the generated `styled-system` package stays
unused code that never reaches a stylesheet.
The output of step 2, one CSS file, is the only thing that ships to the browser. There's no client-side style engine
reading your `css()` calls at runtime, by the time your app runs, the class names are already plain strings and the
CSS already exists.
## What makes a value extractable
Because step 2 works by reading your source code as text (not by running it), Panda needs your style calls to be
statically analyzable, values it can determine by looking at the code, not by executing it. `css({ bg: 'red.500' })`
is extractable, `css({ bg: someVariableComputedAtRuntime })` generally isn't, because Panda can't know what that
variable will be without running your program. This is the root cause behind most "why isn't my style showing up"
questions, see [Dynamic Styles](/docs/styling/dynamic-styling) for the specific patterns that work around it.
## The tradeoff
The payoff is that Panda adds zero runtime, no style computation, no context reads, no re-render cost from styling
itself, since everything is already resolved into a static CSS file before the browser ever sees your app. The
tradeoff is the one above: styles have to be discoverable by static analysis, which is a real constraint compared to
a runtime library that can compute anything a JavaScript expression can compute.
## See also
- [Writing Styles](/docs/styling/writing-styles) for the `css()` API this pipeline is built around.
- [Dynamic Styles](/docs/styling/dynamic-styling) for the specific escape hatches when a value truly can't be known
at build time.
- [Cascade Layers](/docs/styling/cascade-layers) for how the generated CSS file is organized once extraction
produces it.
- [Debugging](/docs/reference/debugging) to inspect what extraction actually saw for a given file.
---
## Installation
Install Panda CSS via the CLI, PostCSS, your framework, or Storybook.
Panda can be installed a few different ways: as a standalone CLI tool, as a PostCSS plugin, through one of our
framework-specific guides, or inside Storybook. Pick the tab below that matches your setup.
This guide shows you how to use Panda as an alternative approach by running the Panda CLI tool.
### Install Panda
```bash
pnpm install -D @pandacss/dev
pnpm panda init
```
```bash
npm install -D @pandacss/dev
npx panda init
```
```bash
yarn add -D @pandacss/dev
yarn panda init
```
```bash
bun add -D @pandacss/dev
bun panda init
```
### Configure the content
Add the paths to all of your JavaScript or TypeScript code where you intend to use panda.
```js {5}
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
preflight: true,
include: ['./src/**/*.{ts,tsx,js,jsx}', './pages/**/*.{ts,tsx,js,jsx}'],
exclude: [],
outdir: 'styled-system'
})
```
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3}
{
"scripts": {
+ "prepare": "panda codegen",
}
}
```
The `prepare` script that will run codegen after dependency installation. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Import the generated CSS
For each Panda run, it emits the generated CSS at the `styled-system/styles.css` file path. Import this file at the root
component of your project.
```jsx {1}
import './styled-system/styles.css'
export function App() {
return
Page
}
```
### Start the Panda build process
Run the CLI tool to scan your JavaScript and TypeScript files for style properties and call expressions.
```bash
# Run it once
pnpm panda
# Run it in watch mode
pnpm panda --watch
```
```bash
# Run it once
npx panda
# Run it in watch mode
npx panda --watch
```
```bash
# Run it once
yarn panda
# Run it in watch mode
yarn panda --watch
```
```bash
# Run it once
bun panda
# Run it in watch mode
bun panda --watch
```
### Start using Panda
Use the generated style utilities in your code and panda will extract them to the generated CSS file. Then run your
build process.
```jsx
import { css } from './styled-system/css'
export function App() {
return
}
```
#### Troubleshooting
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
`tsconfig.json` file:
```json filename="tsconfig.json"
{
// ...
"include": ["src", "styled-system"]
}
```
This guide shows you how to install Panda as a PostCSS plugin, which is the recommended way to integrate it with your
project.
### Install Panda
Install panda and create your `panda.config.ts` file.
```bash
pnpm install -D @pandacss/dev postcss
pnpm panda init -p
```
```bash
npm install -D @pandacss/dev postcss
npx panda init -p
```
```bash
yarn add -D @pandacss/dev postcss
yarn panda init -p
```
```bash
bun add -D @pandacss/dev postcss
bun panda init -p
```
### Add Panda to your PostCSS config
Add panda to your `postcss.config.cjs` file, or wherever PostCSS is configured in your project.
```js
module.exports = {
plugins: {
'@pandacss/dev/postcss': {}
}
}
```
### Configure the content
Add your panda config to your `panda.config.js` file, or wherever panda is configured in your project.
```js {5}
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
preflight: true,
include: ['./src/**/*.{ts,tsx,js,jsx}', './pages/**/*.{ts,tsx,js,jsx}'],
exclude: [],
outdir: 'styled-system'
})
```
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3}
{
"scripts": {
+ "prepare": "panda codegen",
}
}
```
The `prepare` script will run codegen after dependency installation. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the entry CSS with layers
Add this code to an `index.css` file which is going to be the root css of your project.
```css
@layer reset, base, tokens, recipes, utilities;
```
### Start your build process
Run your build process by feeding the [root css](#configure-the-entry-css-with-layers) to PostCSS in your preferred way.
```bash
postcss -o output.css index.css
```
```js
const postcss = require("postcss");
const fs = require("fs");
fs.readFile("index.css", (err, css) => {
postcss()
.process(css, { from: "index.css", to: "output.css" })
.then((result) => {
console.log(result.css);
});
});
```
> If you're using a framework, PostCSS is probably already integrated with your build process. Check our other guides or
> the documentation of your framework to see how to configure PostCSS.
### Start using Panda
Use the generated style utilities in your code and panda will extract them to the generated CSS file.
```jsx
import { css } from './styled-system/css'
export function App() {
return
}
```
#### Troubleshooting
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
`tsconfig.json` file:
```json filename="tsconfig.json"
{
// ...
"include": ["src", "styled-system"]
}
```
Start using Panda CSS in your JavaScript framework using our framework-specific guides that cover our recommended
approach.
We are assuming that you already have a project set up with a framework like React, Vue or Svelte.
### Install Storybook
Storybook needs to be installed into a project that is already set up with a framework. It will not work on an empty
project.
```bash
pnpm dlx storybook@latest init
```
```bash
npx storybook@latest init
```
```bash
yarn dlx storybook@latest init
```
```bash
bunx storybook@latest init
```
### Install Panda
Install panda and create your `panda.config.ts` file.
```bash
pnpm install -D @pandacss/dev
pnpm panda init --postcss
```
```bash
npm install -D @pandacss/dev
npx panda init --postcss
```
```bash
yarn add -D @pandacss/dev
yarn panda init --postcss
```
```bash
bun add -D @pandacss/dev
bun panda init --postcss
```
If you are using Storybook with the Vite builder, you will have to update your PostCSS config file to use the array
syntax for the plugins instead of the object syntax. Simply change `postcss.config.cjs`:
```diff filename="postcss.config.cjs"
module.exports = {
- plugins: {
- '@pandacss/dev/postcss': {}
- }
+ plugins: [require('@pandacss/dev/postcss')()]
}
```
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3} filename="web/package.json"
{
"scripts": {
+ "prepare": "panda codegen"
}
}
```
- `"prepare"` - script that will run Panda CSS CLI codegen before each build. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the content
Make sure that all of the paths of your Storybook components are included in the `include` section of the
`panda.config.ts` file.
```ts {7} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ['./src/**/*.{js,jsx,ts,tsx}', './pages/**/*.{js,jsx,ts,tsx}', './stories/**/*.{js,jsx,ts,tsx}'],
// Files to exclude
exclude: [],
// The output directory for your css system
outdir: 'styled-system'
})
```
### Configure the entry CSS with layers
Locate your main CSS file and add the following layers:
```css filename="src/index.css"
@layer reset, base, tokens, recipes, utilities;
```
### Import the CSS in your Storybook config
Locate your `.storybook/preview.ts` file and import the CSS file.
In this example CSS file is located in the `src` folder.
```ts {1} filename=".storybook/preview.ts"
import '../src/index.css'
import type { Preview } from '@storybook/react'
const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/
}
}
}
}
export default preview
```
### Start the Storybook server
Run the following command to start your Storybook server.
```bash
pnpm storybook
```
```bash
npm run storybook
```
```bash
yarn storybook
```
```bash
bun storybook
```
### Start using Panda
Now you can start using Panda CSS in Storybook.
Here is the example of a Button component and its corresponding Storybook story:
```tsx filename="src/stories/Button.tsx"
import { ReactNode } from 'react'
import { css } from '../../styled-system/css'
export interface IButtonProps {
children: ReactNode
}
export const Button = ({ children }: IButtonProps) => {
return (
)
}
```
```tsx filename="src/stories/Button.stories.tsx"
import type { Meta, StoryObj } from '@storybook/react'
import { css } from '../../styled-system/css'
import { Button } from './Button'
const meta = {
title: 'Example/Button',
component: Button,
tags: ['autodocs'],
decorators: [
Story => (
)
]
} satisfies Meta
export default meta
type Story = StoryObj
export const Default: Story = {
args: {
children: 'Hello πΌ!'
}
}
```
#### Configuring Dark Mode
To enable dark mode in Storybook, you can use the `@storybook/addon-themes` package.
```bash
pnpm add -D @storybook/addon-themes
```
Then, update your `.storybook/preview.ts` file to include the following:
```ts filename=".storybook/preview.ts"
import { withThemeByClassName } from '@storybook/addon-themes'
import type { Preview, ReactRenderer } from '@storybook/react'
const preview: Preview = {
// ...
decorators: [
withThemeByClassName({
themes: {
light: '',
dark: 'dark'
},
defaultTheme: 'light'
})
]
}
export default preview
```
With that in place, you should see the light/dark switcher in Storybook's toolbar.
#### Troubleshooting
##### Cannot find postcss plugin
If you are having issues with the PostCSS plugin similar to `Cannot find module '@pandacss/dev/postcss'`, update the
PostCSS config as follows:
```js filename="postcss.config.cjs"
module.exports = {
plugins: [require('@pandacss/dev/postcss')]
}
```
##### HMR not triggered
If you are having issues with HMR not being triggered after a `panda.config.ts` change (or one of its
[dependencies](/docs/reference/config#dependencies), you can manually specify the files that should trigger a rebuild by
adding the following to your `panda.config.ts`:
```js filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// ...
dependencies: ['path/to/files/**.ts']
})
```
##### Styles in `args` is not generated
If you are having issues with your `args` not generating the expected CSS, it's probably because of:
- you didn't add a file glob for the Storybook files in your [`config.include`](/docs/reference/config#include) like
`"./stories/**/*.{js,jsx,ts,tsx}"`
- you didn't wrap your `args` object (or some part of it) with the
[`.raw()` marker that helps Panda find style usage](https://panda-css.com/docs/styling/dynamic-styling#alternative)
```tsx filename="stories/Button.stories.tsx"
import type { Meta, StoryObj } from '@storybook/react'
import { button } from '../../styled-system/recipes'
export const Funky: Story = {
// mark this as a button recipe usage
args: button.raw({
visual: 'funky',
shape: 'circle',
size: 'sm'
})
}
```
##### Some recipes styles are missing
If you are having issues with your config `recipes` or `slotRecipes` not generating the expected CSS, it's probably
because of:
- you didn't add a file glob for the Storybook files in your [`config.include`](/docs/reference/config#include) like
`"./stories/**/*.{js,jsx,ts,tsx}"`
- you haven't used every recipes variants in your app, so you might want to pre-generate it (only for storybook usage)
with [`staticCss`](/docs/design-systems/static)
```tsx filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// ...
staticCss: {
recipes: '*'
}
})
```
---
## JSX Style Context
JSX Style Context provides an ergonomic way to style compound components with slot recipes.
It uses a context-based approach to distribute recipe styles across multiple child components, making it easier to style
headless UI libraries like Ark UI, and Radix UI.
## Atomic Slot Recipe
- Create a slot recipe using the `sva` function
- Pass the slot recipe to the `createSlotRecipeContext` function
- Use the `withProvider` and `withContext` functions to create compound components
```tsx
// components/ui/card.tsx
import { sva } from 'styled-system/css'
import { createSlotRecipeContext } from 'styled-system/jsx'
const card = sva({
slots: ['root', 'label'],
base: {
root: {},
label: {}
},
variants: {
size: {
sm: { root: {} },
md: { root: {} }
}
},
defaultVariants: {
size: 'sm'
}
})
const { withProvider, withContext } = createSlotRecipeContext(card)
const Root = withProvider('div', 'root')
const Label = withContext('label', 'label')
export const Card = {
Root,
Label
}
```
Then you can use the `Root` and `Label` components to create a card.
```tsx
// app/page.tsx
import { Card } from './components/ui/card'
export default function App() {
return (
Hello
)
}
```
## Config Slot Recipe
The `createSlotRecipeContext` function can also be used with slot recipes defined in the `panda.config.ts` file.
- Pass the config recipe to the `createSlotRecipeContext` function
- Use the `withProvider` and `withContext` functions to create compound components
```tsx
// components/ui/card.tsx
import { card } from '../styled-system/recipes'
import { createSlotRecipeContext } from 'styled-system/jsx'
const { withProvider, withContext } = createSlotRecipeContext(card)
const Root = withProvider('div', 'root')
const Label = withContext('label', 'label')
export const Card = {
Root,
Label
}
```
Then you can use the `Root` and `Label` components to create a card.
```tsx
// app/page.tsx
import { Card } from './components/ui/card'
export default function App() {
return (
Hello
)
}
```
## createSlotRecipeContext
Use this for a slot recipe (`sva` or `defineSlotRecipe`) with multiple parts. It's a factory function that returns three
functions: `withRootProvider`, `withProvider`, and `withContext`.
### withRootProvider
Creates the root component that provides the style context. Use this when the root component **does not render an
underlying DOM element**.
```tsx
import { Dialog } from '@ark-ui/react'
//...
const DialogRoot = withRootProvider(Dialog.Root)
```
### withProvider
Creates a component that both provides context and applies the root slot styles. Use this when the root component
**renders an underlying DOM element**.
> **Note:** It requires the root `slot` parameter to be passed.
```tsx
import { Avatar } from '@ark-ui/react'
//...
const AvatarRoot = withProvider(Avatar.Root, 'root')
```
### withContext
Creates a component that consumes the style context and applies slot styles. It does not accept variant props directly,
but gets them from context.
```tsx
import { Avatar } from '@ark-ui/react'
//...
const AvatarImage = withContext(Avatar.Image, 'image')
const AvatarFallback = withContext(Avatar.Fallback, 'fallback')
```
### unstyled prop
Every component created with `createSlotRecipeContext` supports the `unstyled` prop to disable styling. It is useful when
you want to opt-out of the recipe styles.
- When applied the root component, will disable all styles
- When applied to a child component, will disable the styles for that specific slot
```tsx
// Removes all styles
// Removes only the styles for the image slot
```
## createRecipeContext
Use this for a single-element config recipe (`cva` or `defineRecipe`) that has no slots. It returns a single
`withContext` function, and `withContext` takes just the element or component, with no slot name.
```tsx
import { button } from '../styled-system/recipes'
import { createRecipeContext } from 'styled-system/jsx'
const { withContext } = createRecipeContext(button)
export const Button = withContext('button')
```
The component accepts the recipe's variant props and forwards them to drive the styling.
## Guides
### Config Recipes
The rules of config recipes still applies when using `createSlotRecipeContext`. Ensure the name of the final component
matches the name of the recipe.
> If you want to use a custom name, you can configure the recipe's `jsx` property in the `panda.config.ts` file.
```tsx
// recipe name is "card"
import { card } from '../styled-system/recipes'
const { withRootProvider, withContext } = createSlotRecipeContext(card)
const Root = withRootProvider('div')
const Header = withContext('header', 'header')
const Body = withContext('body', 'body')
// The final component name must be "Card"
export const Card = {
Root,
Header,
Body
}
```
### Default Props
Use `defaultProps` option to provide default props to the component.
```tsx
const { withContext } = createSlotRecipeContext(card)
export const CardHeader = withContext('header', 'header', {
defaultProps: {
role: 'banner'
}
})
```
### Forwarding props
`withProvider` uses variant props to style the slots, but doesn't pass them to your component. When you need a variant
value inside the component, list it in `forwardProps`. It still styles the slot, and now your component receives it too.
```tsx
const tabs = sva({
slots: ['root', 'tab'],
base: { root: { display: 'flex' } },
variants: {
orientation: {
horizontal: { root: { flexDirection: 'row' } },
vertical: { root: { flexDirection: 'column' } }
}
}
})
const { withProvider } = createSlotRecipeContext(tabs)
function TabsRoot({ orientation, ...rest }: TabsRootProps) {
// `orientation` is available here, so we can mirror it as an aria attribute
return
}
export const Tabs = withProvider(TabsRoot, 'root', {
forwardProps: ['orientation']
})
```
`forwardProps` works on `withContext` too. Since context consumers don't take variant props, it's mainly useful for
forwarding props that share a name with a CSS property, since otherwise they'd be turned into styles instead of reaching your
component.
```tsx
// `width` would normally become a style. Forward it to the component instead
const TabIndicator = withContext(Indicator, 'indicator', {
forwardProps: ['width']
})
```
---
## LLMs.txt
Help AI tools understand Panda CSS with LLMs.txt support.
## What is LLMs.txt?
[LLMs.txt](https://llmstxt.org/) files are used to provide the Panda CSS documentation to large language models (LLMs).
This helps AI tools better understand our styling engine, its APIs, and usage patterns.
## Available Routes
These routes are available to help AI tools access our documentation:
- [/llms.txt](/llms.txt) - Contains a structured overview of all concepts and their documentation links
- [/llms-full.txt](/llms-full.txt) - Provides comprehensive documentation including implementation details and examples
## Access Individual Pages
You can also access the raw markdown content for any documentation page by adding `.mdx` to the end of the URL. For
example:
- [`/docs/styling/getting-started.mdx`](/docs/styling/getting-started.mdx) - Raw markdown for the welcome page
- [`/docs/styling/recipes.mdx`](/docs/styling/recipes.mdx) - Raw markdown for the recipes documentation
- [`/docs/theming/tokens.mdx`](/docs/theming/tokens.mdx) - Raw markdown for the tokens documentation
This is useful for AI tools that need to access specific documentation sections directly.
## Usage with AI Tools
### Cursor
Use the `@Docs` feature in Cursor to include the LLMs.txt files in your project. This helps Cursor provide more accurate
code suggestions and documentation for Panda CSS.
[Read more about @Docs in Cursor](https://docs.cursor.com/context/@-symbols/@-docs)
### Windsurf
Reference the LLMs.txt files using `@` or in your `.windsurfrules` files to enhance Windsurf's understanding of Panda
CSS.
[Read more about Windsurf Memories](https://docs.codeium.com/windsurf/memories#memories-and-rules)
### Other AI Tools
Any AI tool that supports LLMs.txt can use these routes to better understand Panda CSS. Simply point your tool to any of
the routes above to get comprehensive documentation about our styling engine.
---
## MCP Server
Expose your Panda CSS design system to AI assistants using the Model Context Protocol (MCP).
The Panda MCP Server allows AI assistants like Claude, Cursor, VS Code Copilot, Windsurf, and Codex to understand and
work with your project's design system. It provides tools for querying tokens, recipes, patterns, conditions, and more.
## What is MCP?
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open standard for connecting AI assistants to
external tools and data sources. Panda's MCP server exposes your design system through a set of specialized tools that
AI assistants can use to:
- Look up design tokens and their values
- Understand available component recipes and variants
- Query layout patterns and their properties
- Analyze token and recipe usage across your codebase
## Quick Start
### 1. Add the MCP Server
Add the Panda MCP server to your AI client's MCP configuration:
```json
{
"mcpServers": {
"panda": {
"command": "npx",
"args": ["-y", "@pandacss/mcp"]
}
}
}
```
For VS Code, use the `servers` key instead of `mcpServers`.
### 2. Use with Your AI Assistant
Once configured, your AI assistant will automatically have access to Panda CSS tools. You can ask questions like:
- "What color tokens are available in my design system?"
- "Show me the button recipe variants"
- "Which tokens are unused in my codebase?"
- "What breakpoints are defined?"
## CLI Commands
The MCP server is distributed as `@pandacss/mcp`. It is usually started by your AI client, but you can also run it
directly with `npx`, `pnpm dlx`, or your package manager's equivalent one-off package runner.
Start the MCP server manually (usually not needed - clients start it automatically).
```bash
pnpm dlx @pandacss/mcp
# With custom config path
pnpm dlx @pandacss/mcp --config ./panda.config.ts
# Specify working directory
pnpm dlx @pandacss/mcp --cwd ./my-project
```
| Flag | Description |
| --------------------- | ------------------------- |
| `--config, -c ` | Path to Panda config file |
| `--cwd ` | Current working directory |
| `--silent` | Suppress startup logs |
## Supported AI Clients
The MCP server can be used with AI assistants that support local stdio MCP servers:
| Client | Typical Config Path | Config Key |
| -------- | -------------------- | ------------ |
| Claude | `.mcp.json` | `mcpServers` |
| Cursor | `.cursor/mcp.json` | `mcpServers` |
| VS Code | `.vscode/mcp.json` | `servers` |
| Windsurf | `.windsurf/mcp.json` | `mcpServers` |
| Codex | `.codex/mcp.json` | `mcpServers` |
## Available Tools
The MCP server exposes these tools to AI assistants:
| Tool | Description | Input |
| ---------------------- | ------------------------------------------------------------------- | ----------------------------------------- |
| `get_tokens` | Get design tokens with values, CSS variables, and usage examples | `category?` - filter by token category |
| `get_semantic_tokens` | Get semantic tokens with conditional values (dark mode, responsive) | `category?` - filter by token category |
| `get_color_palette` | Get the complete color palette | - |
| `get_recipes` | Get component recipes with variants and default values | `name?` - filter by recipe name |
| `get_patterns` | Get layout patterns with properties and usage examples | `name?` - filter by pattern name |
| `get_conditions` | Get all conditions (breakpoints, pseudo-classes, color modes) | - |
| `get_keyframes` | Get keyframe animations defined in the theme | - |
| `get_text_styles` | Get text style compositions for typography | - |
| `get_layer_styles` | Get layer style compositions for visual styling | - |
| `get_animation_styles` | Get animation style compositions | - |
| `get_config` | Get the resolved Panda CSS configuration | - |
| `get_usage_report` | Analyze token/recipe usage across the codebase | `scope?` - `'all'`, `'token'`, `'recipe'` |
The `get_usage_report` tool is particularly useful for auditing your design system, identifying unused tokens/recipes,
and finding missing definitions.
## Configuration
For most MCP clients, use this configuration:
```json
{
"mcpServers": {
"panda": {
"command": "npx",
"args": ["-y", "@pandacss/mcp"]
}
}
}
```
The server automatically loads your `panda.config.ts` from the current working directory when started.
### Claude
Create `.mcp.json` in your project root:
```json
{
"mcpServers": {
"panda": {
"command": "npx",
"args": ["-y", "@pandacss/mcp"]
}
}
}
```
### Cursor
Create `.cursor/mcp.json`:
```json
{
"mcpServers": {
"panda": {
"command": "npx",
"args": ["-y", "@pandacss/mcp"]
}
}
}
```
### VS Code
Create `.vscode/mcp.json`:
```json
{
"servers": {
"panda": {
"command": "npx",
"args": ["-y", "@pandacss/mcp"]
}
}
}
```
### Custom Config Path
If your Panda config is not in the default location, specify it explicitly:
```json
{
"mcpServers": {
"panda": {
"command": "npx",
"args": ["-y", "@pandacss/mcp", "--config", "./path/to/panda.config.ts"]
}
}
}
```
## Example Interactions
Here are some example prompts you can use with AI assistants once MCP is configured:
### Exploring Tokens
> "What spacing tokens are available?"
The AI will use `get_tokens` with `category: "spacing"` to show you all spacing values.
### Understanding Recipes
> "How do I use the button recipe with a destructive variant?"
The AI will use `get_recipes` with `name: "button"` to show variants and usage.
### Finding Unused Tokens
> "Which design tokens are not being used in my codebase?"
The AI will use `get_usage_report` with `scope: "token"` to identify unused tokens.
### Checking Conditions
> "What responsive breakpoints are defined?"
The AI will use `get_conditions` to show all available breakpoints.
## Troubleshooting
### Server Not Starting
If the MCP server fails to start:
1. Ensure Panda CSS is installed: `pnpm add -D @pandacss/dev`
2. Verify you have a valid `panda.config.ts` in your project
3. Check that `npx -y @pandacss/mcp` runs without errors
### Tools Not Available
If tools aren't showing up in your AI assistant:
1. Restart the AI assistant after adding the MCP configuration
2. Verify the config file is in the correct location
3. Check the AI assistant's MCP documentation for any additional setup steps
### Usage Report Empty
If `get_usage_report` returns empty results:
1. Ensure your `include` paths in `panda.config.ts` cover your source files
2. Run `pnpm panda codegen` to ensure the project is properly set up
3. Verify files contain Panda CSS usage (css(), styled(), etc.)
---
## Merging Styles
Learn how to merge multiple styles without conflicts.
## Merging `css` objects
You can merge multiple style objects together using the `css` function.
```js
import { css } from 'styled-system/css'
const style1 = {
bg: 'red',
color: 'white'
}
const style2 = {
bg: 'blue'
}
const className = css(style1, style2) // => 'bg_blue text_white'
```
In some cases though, the style object might not be colocated in the same file as the component. In this case, you can
use the `css.raw` function to preserve the original style object.
> All `.raw(...)` signatures are identity functions that return the same value as the input, but serve as a hint to the
> compiler that the value is a style object.
```js
// style.js
import { css } from 'styled-system/css'
export const style1 = css.raw({
bg: 'red',
color: 'white'
})
// component.js
import { css } from 'styled-system/css'
import { style1 } from './style.js'
const style2 = css.raw({
bg: 'blue'
})
const className = css(style1, style2) // => 'bg_blue text_white'
```
## Spreading `css.raw` objects
> **Added in v1.6.1**
You can also spread `css.raw` objects within style declarations. This is particularly useful for reusing styles in
nested selectors, conditions, and complex compositions:
### Child selectors
```js
import { css } from 'styled-system/css'
const baseStyles = css.raw({ margin: 0, padding: 0 })
const component = css({
'& p': { ...baseStyles, fontSize: '1rem' },
'& h1': { ...baseStyles, fontSize: '2rem' }
})
```
### Nested conditions
```js
import { css } from 'styled-system/css'
const interactive = css.raw({ cursor: 'pointer', transition: 'all 0.2s' })
const card = css({
_hover: {
...interactive,
_dark: { ...interactive, color: 'white' }
}
})
```
## Merging `cva` + `css` styles
The same technique can be used to merge an atomic `cva` recipe and a style object.
```js
import { css, cx, cva } from 'styled-system/css'
const overrideStyles = css.raw({
bg: 'red',
color: 'white'
})
const buttonStyles = cva({
base: {
bg: 'blue',
border: '1px solid black'
},
variants: {
size: {
small: { fontSize: '12px' }
}
}
})
const className = css(
// returns the resolved style object
buttonStyles.raw({ size: 'small' }),
// add the override styles
overrideStyles
)
// => 'bg_red border_1px_solid_black color_white font-size_12px'
```
## Merging `sva` + `css` styles
The same technique can be used to merge an atomic `sva` recipe and a style object.
```js
import { css, sva } from 'styled-system/css'
const overrideStyles = css.raw({
bg: 'red',
color: 'white'
})
const buttonStyles = sva({
slots: ['root']
base: {
root: {
bg: 'blue',
border: '1px solid black'
}
},
variants: {
size: {
root: {
small: { fontSize: '12px' }
}
}
}
})
// returns the resolved style object for all slots
const { root } = buttonStyles.raw({ size: 'small' })
const className = css(
root,
// add the override styles
overrideStyles
)
// => 'bg_red border_1px_solid_black color_white font-size_12px'
```
## Merging config recipe and style object
Due to the fact that the generated styles of a config recipe are saved in the `@layer recipe` cascade layer, they can be
overridden with any atomic styles. Use the `cx` function to achieve that.
> The `utilties` layer has more precedence than the `recipe` layer.
```js
import { css, cx } from 'styled-system/css'
import { button } from 'styled-system/recipes'
const className = cx(
// returns the resolved class name: `button button--size-small`
button({ size: 'small' }),
// add the override styles
css({ bg: 'red' }) // => 'bg_red'
)
// => 'button button--size-small bg_red'
```
## Passing styles to custom components
Expose `css`, a `*Css` prop, or `css.raw()` on a custom prop name. Panda extracts styles at the call site.
> **Note:** Set `jsxFramework` in your config for JSX extraction.
```jsx
const cardStyles = css.raw({
bg: 'red',
color: 'white'
})
function Card({ title, description, css: cssProp }) {
return (
{title}
{description}
)
}
// usage
```
Props ending in `Css` work the same way, which is useful for named slots:
```jsx
function Card({ title, description, rootCss, bodyCss }) {
return (
{title}
{description}
)
}
```
For any other prop name, wrap the value in `css.raw()`:
```jsx
function Card({ title, description, style }) {
return (
{title}
{description}
)
}
```
See [custom component props](/docs/styling/style-props#custom-component-props).
---
## Migration strategy
Run Panda alongside your current styling solution during a gradual migration, and what to know about how the two systems interact on the page.
Most real migrations aren't a single rewrite, they're Panda and your current solution running on the same page for
weeks or months while you convert one component or route at a time. This page covers what to know before you start
that: how the two systems interact in the cascade, and a prompt you can hand to an AI coding agent to speed up the
conversion itself. For the specifics of translating one particular tool's API to Panda's, jump straight to its
guide: [Stitches](/docs/styling/stitches), [Styled Components](/docs/styling/styled-components), [Theme
UI](/docs/styling/theme-ui), [Tailwind](/docs/styling/tailwind), [Chakra UI](/docs/styling/chakra-ui),
[Emotion](/docs/styling/emotion), [StyleX](/docs/styling/stylex).
## The one thing that surprises people: layered vs. unlayered CSS
Panda's generated CSS ships inside [cascade layers](/docs/styling/cascade-layers) (`@layer reset, base, tokens,
recipes, utilities`). Per the CSS spec, **any unlayered rule beats every layered rule**, regardless of selector
specificity or which stylesheet loaded last. Most existing CSS, plain stylesheets, most CSS-in-JS libraries, is
unlayered.
That means during a migration, your existing styles win over Panda's by default, even for a component you've already
converted. If you see a converted component still showing its old styles, this is almost always why, not a
specificity bug in your new Panda styles.
Two ways to fix it, depending on which side you want to win:
- **Let Panda's new styles win.** Strip the `@layer` wrapper from Panda's own output with
[`postcss-cascade-layers`](/docs/styling/cascade-layers#polyfills). It emulates each layer's priority using
specificity instead of the `@layer` rule, so Panda's CSS competes as unlayered CSS against your legacy styles
instead of automatically losing to them.
- **Keep your legacy styles as the fallback while you migrate.** Leave both as they are. Converted components need
a specific enough legacy override removed (or the legacy selector deleted) once they're fully on Panda, since
layering alone won't make Panda's version win.
If your previous tool also uses cascade layers (rare, but check if you're coming from a tool that ships its own
`@layer` rules), the fix is different: use [`layers`](/docs/reference/config#layers) to rename Panda's layers so they
don't collide with the other tool's layer names, then declare both tools' `@layer` statements in one explicit order
at the top of your CSS. Order in that declaration, not import order, decides priority between two layered sources.
## Migrating gradually, not all at once
Pick one boundary and hold it consistently for the length of the migration, either by route, by component
directory, or by team. Trying to convert overlapping pieces of the same component tree from two teams at once is
where most of the cascade confusion above actually bites. A clean boundary also makes `git blame` and rollback
straightforward if a converted piece needs to revert temporarily.
## AGENT PROMPT: converting a component to Panda
Paste this into an AI coding agent along with the component file you want converted, filling in the tool you're
migrating from:
```text
You are converting a component from [STITCHES / STYLED-COMPONENTS / THEME UI / TAILWIND / CHAKRA UI / EMOTION /
STYLEX] to Panda CSS. Follow these rules:
1. Read the migration guide at https://panda-css.com/docs/styling/[stitches|styled-components|theme-ui|tailwind|
chakra-ui|emotion|stylex] first, and translate the component using the equivalents shown there, not general CSS
knowledge.
2. Prefer the `css()` function for one-off styles, and `cva()`/`sva()` (defined with `styled-system/recipes` and
`styled-system/patterns` imports) only when the component already has real variants, don't introduce variants
that didn't exist in the original.
3. Map every raw color/spacing/font value in the original component to a design token from `styled-system/tokens` if
an equivalent token exists. If none exists, leave the raw value and flag it in a comment rather than guessing at
a token name.
4. Do not invent Panda config options, hooks, or CLI flags. If you're unsure whether something exists, say so instead
of guessing.
5. Preserve the original component's props and behavior exactly, this is a styling migration, not a refactor. Don't
rename props, change default variants, or restructure the component's JSX unless the styling approach requires it.
6. After converting, list anything you weren't able to translate directly (a CSS feature the old tool supported that
Panda handles differently, a dynamic style that needs `css.raw()` or a data attribute instead) so a human can
review it.
```
## See also
- [Cascade Layers](/docs/styling/cascade-layers) for the full layer model this page's coexistence advice is based
on.
- [Federated Micro-Frontends](/docs/design-systems/federated-microfrontends) if your migration also involves
multiple independently-built bundles on the same page, a related but different collision problem.
---
## Using Next.js
Easily use Panda with Next.js with our dedicated integration.
Setting up Panda CSS with Next.js is straightforward. Follow the steps below to get started.
If you don't have Next.js app installed, you can follow the [Create a Next.js app](#create-a-nextjs-app) section to
create a new Next.js app, otherwise you can skip to the [Install Panda CSS](#install-panda-css) section.
## Start a new project
You can chose between these two options to start a new project:
### Create a Next.js app
First, create a Next.js app using the official [Create Next App](https://nextjs.org/docs/api-reference/create-next-app)
CLI. We will name our project `test-app` but you can name it whatever you want.
If you don't enter any parameter, the CLI will guide you through the process of creating a new Next.js app.
{/* */}
```bash
pnpm dlx create-next-app@latest --use-pnpm
```
```bash
npx create-next-app@latest --use-npm
```
```bash
yarn dlx create-next-app@latest --use-yarn
```
```bash
bunx create next-app@latest --use-bun
```
{/* */}
You will be asked a few questions, answer them as follows:
{/* */}
```bash
β What is your project named? β¦ test-app
β Would you like to use TypeScript with this project? β¦ Yes
β Would you like to use ESLint with this project? β¦ Yes
β Would you like to use Tailwind CSS with this project? β¦ No
β Would you like to use `src/` directory with this project? β¦ Yes
β Use App Router (recommended)? β¦ Yes
β Would you like to customize the default import alias? β¦ No
```
{/* */}
{/* */}
```bash
β What is your project named? β¦ test-app
β Would you like to use TypeScript with this project? β¦ Yes
β Would you like to use ESLint with this project? β¦ Yes
β Would you like to use Tailwind CSS with this project? β¦ No
β Would you like to use `src/` directory with this project? β¦ Yes
β Use App Router (recommended)? β¦ No
β Would you like to customize the default import alias? β¦ No
```
{/* */}
Enter the newly created directory:
```bash
cd test-app
```
### Install Panda CSS
Install Panda CSS dependency using your favorite package manager.
{/* */}
```bash
pnpm install -D @pandacss/dev
pnpm panda init --postcss
```
```bash
npm install -D @pandacss/dev
npx panda init --postcss
```
```bash
yarn add -D @pandacss/dev
yarn panda init --postcss
```
```bash
bun add -D @pandacss/dev
bun panda init --postcss
```
{/* */}
`panda init --postcss` command will automatically create a `postcss.config.cjs` file at the root of your project
with the following code:
```js {3}
module.exports = {
plugins: {
'@pandacss/dev/postcss': {}
}
}
```
For advanced configuration follow the Next.js PostCSS guide to set up a custom PostCSS configuration by referring to
this [link](https://nextjs.org/docs/pages/building-your-application/configuring/post-css#customizing-plugins).
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3}
{
"scripts": {
+ "prepare": "panda codegen",
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
}
}
```
The `prepare` script that will run codegen after dependency installation. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the content
Make sure that all of the paths of your React components are included in the `include` section of the `panda.config.ts`
file.
```ts {7} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ['./src/components/**/*.{ts,tsx,js,jsx}', './src/app/**/*.{ts,tsx,js,jsx}'],
// Files to exclude
exclude: [],
// The output directory for your css system
outdir: 'styled-system'
})
```
```ts {7} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ['./src/components/**/*.{ts,tsx,js,jsx}', './src/pages/**/*.{ts,tsx,js,jsx}'],
// Files to exclude
exclude: [],
// The output directory for your css system
outdir: 'styled-system'
})
```
### Configure the entry CSS with layers
In your Next.js project, navigate to the `src/app` folder and open `globals.css` file. Replace all the content with
the following code:
In your Next.js project, navigate to the `src/styles` folder and open `globals.css` file. Replace all the content with
the following code:
```css
@layer reset, base, tokens, recipes, utilities;
```
> **Note:** Feel free to remove the `page.module.css` file as we don't need it
anymore.
> **Note:** Feel free to remove the `Home.module.css` file as we don't need it anymore.
### Import the entry CSS in your app
Make sure that you import the `globals.css` file in your `src/app/layout.tsx` file as follows:
```tsx {1} filename="./src/app/layout.tsx"
import './globals.css'
import type { Metadata } from 'next'
import localFont from 'next/font/local'
const geistSans = localFont({
src: './fonts/GeistVF.woff',
variable: '--font-geist-sans',
weight: '100 900'
})
const geistMono = localFont({
src: './fonts/GeistMonoVF.woff',
variable: '--font-geist-mono',
weight: '100 900'
})
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app'
}
export default function RootLayout({
children
}: Readonly<{
children: React.ReactNode
}>) {
return (
{children}
)
}
```
Make sure that you import the `globals.css` file in your `src/pages/_app.tsx` file as follows:
```tsx {1} filename="./src/pages/_app.tsx"
import '../styles/globals.css'
import type { AppProps } from 'next/app'
export default function App({ Component, pageProps }: AppProps) {
return
}
```
### Start using Panda
We will update the contents of `src/app/page.tsx` with the following snippet that uses Panda CSS:
We will update the contents of `src/pages/index.tsx` with the following snippet that uses Panda CSS:
```tsx
import { css } from '../../styled-system/css'
export default function Home() {
return
Hello πΌ!
}
```
### Start the development server
Run the following command to start the development server:
{/* */}
```bash
pnpm dev
```
```bash
npm run dev
```
```bash
yarn dev
```
```bash
bun dev
```
{/* */}
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
## Troubleshooting
### I don't see any styles
Sometimes Next.js caches PostCSS generated styles and when that happens you need to clear the cache. To do that, delete
the `.next` folder and restart your development server.
You can also update you `package.json` scripts to delete the `.next` folder before each build:
```diff {3,4}
{
"scripts": {
- "dev": "next dev",
+ "dev": "rm -rf .next && next dev",
},
}
```
This is a known issue with Next.js and you can track the progress here:
- [vercel/next.js#39410](https://github.com/vercel/next.js/issues/39410)
- [vercel/next.js#48748](https://github.com/vercel/next.js/issues/48748)
- [vercel/next.js#47533](https://github.com/vercel/next.js/issues/47533)
### I don't see any import autocomplete in my IDE
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
`tsconfig.json` file:
```json filename="tsconfig.json"
{
// ...
"include": ["src", "styled-system"]
}
```
### Codegen fails using es5
If you run into any error related to "Transforming const to the configured target environment ("es5") is not supported
yet", update your tsconfig to use es6 or higher:
```json filename="tsconfig.json"
{
"compilerOptions": {
"target": "es6"
}
}
```
---
## Using Nuxt
Easily use Panda with Nuxt with the vue integration.
Learn how to set up Panda CSS in a Nuxt project using PostCSS.
## Start a new project
### Create Nuxt project
To get started, we will need to create a new Nuxt project using npx.
{/* */}
```bash
npx nuxi@latest init test-app
```
```bash
pnpm dlx nuxi@latest init test-app
```
```bash
bunx nuxi@latest init test-app
```
{/* */}
Enter the newly created directory and install the dependencies.
{/* */}
```bash
cd test-app
pnpm install
```
```bash
cd test-app
npm install
```
```bash
cd test-app
yarn install
```
```bash
cd test-app
bun install
```
{/* */}
### Install Panda
Install panda and create your `panda.config.ts` file.
{/* */}
```bash
pnpm install -D @pandacss/dev
pnpm panda init
```
```bash
npm install -D @pandacss/dev
npx panda init
```
```bash
yarn add -D @pandacss/dev
yarn panda init
```
```bash
bun add -D @pandacss/dev
bun panda init
```
{/* */}
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3} filename="package.json"
{
"scripts": {
+ "prepare": "panda codegen",
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview"
}
}
```
- `"prepare"` - script that will run Panda CSS CLI codegen before each build. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the content
Make sure that all of the paths of your Vue components are included in the `include` section of the `panda.config.ts`
file.
```js {8,17} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ['./app.vue', './components/**/*.{js,jsx,ts,tsx,vue}', './pages/**/*.{js,jsx,ts,tsx,vue}'],
// Files to exclude
exclude: [],
// The output directory for your css system
outdir: 'styled-system'
})
```
### Configure the entry CSS with layers
Add this code to an `assets/css/global.css` file.
```css filename="assets/css/global.css"
@layer reset, base, tokens, recipes, utilities;
```
### Configure Nuxt
Add the following configuration to the `nuxt.config.ts` file
```js {1-2,5-17} filename="nuxt.config.ts"
import { createResolver } from '@nuxt/kit'
const { resolve } = createResolver(import.meta.url)
export default defineNuxtConfig({
alias: {
'styled-system': resolve('./styled-system')
},
css: ['@/assets/css/global.css'],
postcss: {
plugins: {
'@pandacss/dev/postcss': {}
}
}
})
```
With the above we've performed the following:
- imported the global css file '@/assets/css/global.css' at the root of the system.
- created an alias that points to the `styled-system` directory.
- added panda into the postcss plugins section.
## Start your build process
Run the following command to start your development server.
{/* */}
```bash
pnpm dev
```
```bash
npm run dev
```
```bash
yarn dev
```
```bash
bun dev
```
{/* */}
### Start using Panda
Now you can start using Panda CSS in your project.
As an example here is a snippet of code for a `components/Demo.vue` file.
```xml filename="components/Demo.vue"
Hello πΌ!
```
---
## Patterns
Patterns are layout primitives that can be used to create robust and responsive layouts with ease. Panda comes with predefined patterns like stack, hstack, vstack, wrap, etc. These patterns can be used as functions or JSX elements.
Think of patterns as a set of predefined styles to reduce repetition and improve readability. You can override the
properties as needed, just like in the `css` function.
## Creating Patterns
To learn how to create patterns, check out the [customization](/docs/design-systems/patterns) section.
## Predefined Patterns
### Box
The Box pattern does not contain any additional styles. With its function form it's the equivalent of the `css`
function. It can be useful with its JSX form and is the equivalent of a `styled.div` component, serving mostly to get
style props available in JSX.
```tsx
import { Box } from '../styled-system/jsx'
function App() {
return (
Cool !
)
}
```
### Container
The Container pattern is used to create a container with a max-width and center the content.
By default, the container sets the following properties:
- `maxWidth: 8xl`
- `marginX: auto`
- `position: relative`
- `paddingX: { base: 4, md: 6, lg: 8 }`
```tsx
import { container } from '../styled-system/patterns'
function App() {
return (
First
Second
Third
)
}
```
```tsx
import { Container } from '../styled-system/jsx'
function App() {
return (
First
Second
Third
)
}
```
### Stack
The Stack pattern is a layout primitive that can be used to create a vertical or horizontal stack of elements.
The `stack` function accepts the following properties:
- `direction`: An alias for the css `flex-direction` property. Default is `column`.
- `gap`: The gap between the elements in the stack. Default is `8px`.
- `align`: An alias for the css `align-items` property.
- `justify`: An alias for the css `justify-content` property.
Change defaults in [config](/docs/design-systems/patterns#changing-pattern-defaults). `HStack` and `VStack` are separate
patterns with their own defaults.
```tsx
import { stack } from '../styled-system/patterns'
function App() {
return (
First
Second
Third
)
}
```
```tsx
import { Stack } from '../styled-system/jsx'
function App() {
return (
First
Second
Third
)
}
```
#### HStack
The HStack pattern is a wrapper around the `stack` pattern that sets the `direction` property to `horizontal`, and
centers the elements vertically.
```tsx
import { hstack } from '../styled-system/patterns'
function App() {
return (
First
Second
Third
)
}
```
```tsx
import { HStack } from '../styled-system/jsx'
function App() {
return (
First
Second
Third
)
}
```
#### VStack
The VStack pattern is a wrapper around the `stack` pattern that sets the `direction` property to `vertical`, and centers
the elements horizontally.
```tsx
import { vstack } from '../styled-system/patterns'
function App() {
return (
First
Second
Third
)
}
```
```tsx
import { VStack } from '../styled-system/jsx'
function App() {
return (
First
Second
Third
)
}
```
### Wrap
The Wrap pattern is used to add space between elements and wraps automatically if there isn't enough space.
The `wrap` function accepts the following properties:
- `gap`: The gap between the elements in the stack.
- `columnGap`: The gap between the elements in the stack horizontally.
- `rowGap`: The gap between the elements in the stack vertically.
- `align`: An alias for the css `align-items` property.
- `justify`: An alias for the css `justify-content` property.
```tsx
import { wrap } from '../styled-system/patterns'
function App() {
return (
First
Second
Third
)
}
```
```tsx
import { Wrap } from '../styled-system/jsx'
function App() {
return (
First
Second
Third
)
}
```
### Aspect Ratio
The Aspect Ratio pattern is used to create a container with a fixed aspect ratio. It is used when displaying images,
maps, videos and other media.
> **Note:** In most cases, we recommend using the `aspectRatio` property instead of the pattern.
The `aspectRatio` function accepts the following properties:
- `ratio`: The aspect ratio of the container. Can be a number or a string.
```tsx
import { aspectRatio } from '../styled-system/patterns'
function App() {
return (
)
}
```
```tsx
import { AspectRatio } from '../styled-system/jsx'
function App() {
return (
)
}
```
### Flex
The Flex pattern is used to create a flex container and provides some shortcuts for the `flex` property.
The `flex` function accepts the following properties:
- `direction`: The flex direction of the container. Can be `row`, `column`, `row-reverse` or `column-reverse`.
- `wrap`: Whether to wrap the flex items. The value is a boolean.
- `align`: An alias for the css `align-items` property.
- `justify`: An alias for the css `justify-content` property.
- `basis`: An alias for the css `flex-basis` property.
- `grow`: An alias for the css `flex-grow` property.
- `shrink`: An alias for the css `flex-shrink` property.
```tsx
import { flex } from '../styled-system/patterns'
function App() {
return (
First
Second
Third
)
}
```
```tsx
import { Flex } from '../styled-system/jsx'
function App() {
return (
First
Second
Third
)
}
```
### Center
The Center pattern is used to center the content of a container.
The `center` function accepts the following properties:
- `inline`: Whether to use `inline-flex` or `flex` for the container. The value is a boolean.
```tsx
import { center } from '../styled-system/patterns'
function App() {
return (
)
}
```
```tsx
import { Center } from '../styled-system/jsx'
function App() {
return (
First
Second
Third
)
}
```
### LinkOverlay
The link overlay pattern is used to expand a link's clickable area to its nearest parent with `position: relative`.
> We recommend using this pattern when the relative parent contains at most one clickable link.
```tsx
import { css } from '../styled-system/css'
import { linkOverlay } from '../styled-system/patterns'
function App() {
return (
)
}
```
```tsx
import { Box, LinkOverlay } from '../styled-system/jsx'
function App() {
return (
View more
)
}
```
### Float
The Float pattern is used to anchor an element to the top, bottom, left or right of the container.
> It requires a parent element with `position: relative` styles.
The `float` function accepts the following properties:
- `placement`: The placement of the element. Can be `top-start`, `top`, `top-end`, `bottom-start`, `bottom`,
`bottom-end`, `left-start`, `left`, `left-end`, `right-start`, `right` or `right-end`.
- `offset`: The offset of the element from the edge of the container. Can be a number or a string.
- `offsetX`: Same as `offset`, but only for the horizontal axis.
- `offsetY`: Same as `offset`, but only for the vertical axis.
```tsx
import { css } from '../styled-system/css'
import { float } from '../styled-system/patterns'
function App() {
return (
3
)
}
```
```tsx
import { css } from '../styled-system/css'
import { Float } from '../styled-system/jsx'
function App() {
return (
3
)
}
```
### Grid
The Grid pattern is used to create a grid layout.
The `grid` function accepts the following properties:
- `columns`: The number of columns in the grid.
- `gap`: The gap between grid items. Default is `8px` (omitted when `columnGap` or `rowGap` is set).
- `columnGap`: The gap between the elements in the stack horizontally.
- `rowGap`: The gap between the elements in the stack vertically.
- `minChildWidth`: The minimum width of the child elements before wrapping (must not be used with `columns`).
Change defaults in [config](/docs/design-systems/patterns#changing-pattern-defaults).
```tsx
import { grid } from '../styled-system/patterns'
function App() {
return (
First
Second
Third
)
}
```
```tsx
import { Grid } from '../styled-system/jsx'
function App() {
return (
First
Second
Third
)
}
```
For responsive column counts, pass breakpoints on `columns`: `columns={{ base: 1, md: 2 }}`. See
[responsive pattern props](#responsive-pattern-props).
#### Grid Item
The Grid Item pattern is used to style the children of a grid container.
The `gridItem` function accepts the following properties:
- `colSpan`: The number of columns the item spans.
- `rowSpan`: The number of rows the item spans.
- `rowStart`: The row the item starts at.
- `rowEnd`: The row the item ends at.
- `colStart`: The column the item starts at.
- `colEnd`: The column the item ends at.
```tsx
import { grid, gridItem } from '../styled-system/patterns'
function App() {
return (
First
Second
Third
)
}
```
```tsx
import { Grid, GridItem } from '../styled-system/jsx'
function App() {
return (
FirstSecondThird
)
}
```
### Divider
The Divider pattern is used to create a horizontal or vertical divider.
The `divider` function accepts the following properties:
- `orientation`: The orientation of the divider. Can be `horizontal` or `vertical`. Default is `horizontal`.
- `thickness`: The thickness of the divider. Can be a sizing token, or arbitrary value. Default is `1px`.
- `color`: The color of the divider. Can be a color token, or arbitrary value.
Change defaults in [config](/docs/design-systems/patterns#changing-pattern-defaults).
```tsx
import { divider, stack } from '../styled-system/patterns'
function App() {
return (
)
}
```
```tsx
import { Divider, Stack } from '../styled-system/jsx'
function App() {
return (
)
}
```
### Circle
The Circle pattern is used to create a circle.
The `circle` function accepts the following properties:
- `size`: The size of the circle. Can be a sizing token, or arbitrary value.
```tsx
import { circle } from '../styled-system/patterns'
function App() {
return
}
```
```tsx
import { Circle } from '../styled-system/jsx'
function App() {
return
}
```
### Square
The Square pattern is used to create a square with equal width and height.
The `square` function accepts the following properties:
- `size`: The size of the square. Can be a sizing token, or arbitrary value.
```tsx
import { square } from '../styled-system/patterns'
function App() {
return
}
```
```tsx
import { Square } from '../styled-system/jsx'
function App() {
return
}
```
### Visually Hidden
The Visually Hidden pattern is used to hide an element visually, but keep it accessible to screen readers.
```tsx
import { visuallyHidden } from '../styled-system/patterns'
export function Checkbox() {
return (
)
}
```
### Bleed
The Bleed pattern is used to create a full width element by negating the padding applied to its parent container.
The `bleed` function accepts the following properties:
- `inline`: The amount of padding to negate on the horizontal axis. Should match the parent's padding.
- `block`: The amount of padding to negate on the vertical axis. Should match the parent's padding.
```tsx
import { css } from '../styled-system/css'
import { bleed } from '../styled-system/patterns'
export function Page() {
return (
Welcome
)
}
```
```tsx
import { css } from '../styled-system/css'
import { Bleed } from '../styled-system/jsx'
export function Page() {
return (
Welcome
)
}
```
### cq (Container Query)
To make it easier to use container queries, we've added a new `cq` pattern to `@pandacss/preset-base`. It is used to
apply styles based on the width of the container.
The `cq` function accepts the following properties:
- `name`: The name of the container query, Maps to the
[`containerName` CSS property](https://developer.mozilla.org/en-US/docs/Web/CSS/container-name).
- `type`: The type of the container query. Maps to the
[`containerType` CSS property](https://developer.mozilla.org/en-US/docs/Web/CSS/container-type). Defaults to
`inline-size`.
```ts
import { cq } from 'styled-system/patterns'
function Demo() {
return (
)
}
```
You can also named container queries:
```tsx
// 1 - Define container conditions
export default defineConfig({
// ...
theme: {
containerNames: ['sidebar', 'content'],
containerSizes: {
xs: '40em',
sm: '60em',
md: '80em'
}
}
})
```
```tsx
// 2 - Automatically generate container query pattern
import { cq } from 'styled-system/patterns'
function Demo() {
return (
)
}
```
Read more about container queries [here](/docs/styling/conditional-styles#container-queries).
## Usage with JSX
To use the pattern in JSX, you need to set the `jsxFramework` property in the config. When this is set, Panda will emit
files for JSX elements based on the framework.
Every pattern can be used as a JSX element and imported from the `/jsx` entrypoint. By default, the pattern name is the
function name in PascalCase. You can override both the component name (with the `jsx` config property) and the element
rendered (with the `jsxElement` config property).
Learn more about pattern customization [here](/docs/design-systems/patterns).
```tsx
import { VStack, Center } from '../styled-system/jsx'
function App() {
return (
First
Second
Third
4
)
}
```
### Advanced JSX Tracking
We recommend that you use the pattern functions in most cases, in design systems there might be a need to compose
existing components to create new components.
To track the usage of the patterns in these cases, you'll need to add the `jsx` hint for the pattern config
```js {12} filename="button.pattern.ts"
import { definePattern } from '@pandacss/dev'
const scrollable = definePattern({
// ...
// Add the jsx hint to track the usage of the pattern in JSX, you can also use a regex to match multiple components
jsx: ['Scrollable', 'PageScrollable']
})
```
Then you can create a new component that uses the `PageScrollable` component and Panda will track the usage of the
`scrollable` pattern as well.
```tsx
const PageScrollable = (props: ButtonProps) => {
const { children, size } = props
return (
{children}
)
}
```
## Responsive pattern props
Pattern components take two kinds of props:
- **Pattern props**: compiled by the pattern (`columns`, `colSpan`, `direction`, `ratio`, β¦)
- **CSS utilities**: passed straight to the style system (`padding`, `display`, `color`, β¦)
Put breakpoints on the pattern prop, not on a separate `md` prop:
```tsx
// β
// β pattern props inside `md` skip the pattern transform
```
Pattern props are transformed at the top level (`columns` β `gridTemplateColumns`, `colSpan` β `gridColumn`, etc.).
Nesting them in `md={{ β¦ }}` skips that step, so nothing changes at the breakpoint.
CSS utilities on the same component still work with breakpoint keys, same as on `Box` or `styled.div`:
```tsx
```
The same call-site rules apply to custom components: use `css`, `*Css`, or CSS utility names, not semantic aliases.
See [custom component props](/docs/styling/style-props#custom-component-props).
---
## Performance & Optimization
What actually costs time in a Panda project, build time, IDE responsiveness, and strictTokens, and how to check before you tune.
Panda ships zero runtime cost, see [How Panda works](/docs/styling/how-panda-works), but that doesn't mean nothing
costs time. Two different things get blamed under "Panda is slow": the build (`panda cssgen`/`codegen`, or your
bundler plugin) and the editor (TypeScript checking your style calls). They have different causes and different
fixes.
## Measure before you tune
Don't guess which phase is slow. Add `--profile` to any Panda command to profile the whole run, including time spent in
the Rust engine:
```bash
panda --profile
```
This writes a `.panda/trace.json` file you can open in `chrome://tracing` or [ui.perfetto.dev](https://ui.perfetto.dev)
to see exactly which phase, config resolution, file parsing, CSS generation, is actually taking the time (plus a
`.panda/timings.json` summary). See [Debugging](/docs/reference/debugging#performance-profiling) for details.
## Build time: scope what gets scanned
Extraction time scales with how many files [`include`](/docs/reference/config#include) matches, and how many style
calls are inside them. The most direct lever is making that glob no wider than it needs to be, don't include test
files, storybook files, or generated output if they never call `css()`/`cva()` themselves. See
[Debugging](/docs/reference/debugging#panda-debug) for `panda debug`, which shows exactly which files were scanned
for a given run, useful for spotting an `include` pattern that's wider than intended.
## IDE responsiveness and `strictTokens`
If your editor feels sluggish specifically while editing `css()` calls or JSX style props, not while editing
anything else, this is usually TypeScript re-checking a large generated union type, not Panda itself running
anything.
[`strictTokens`](/docs/reference/config#stricttokens) is the main thing that changes how big that union gets. With
it off, most style properties accept a token union **or** a permissive string fallback, so an unrecognized value
still type-checks (just without catching typos). With it on, that permissive fallback is removed and the property
only accepts the real token union, which is what gives you the safety, but it also means TypeScript has to check
every value against the full token list instead of a loose `string` type. The more tokens a category has, the bigger
that union gets.
`strictTokens` is a single project-wide boolean today, there's no built-in way to enable it for some token
categories and not others. If you want that (for example, strict `colors` but permissive `fontSizes`), you'd build
it with a [`config:resolved` hook](/docs/design-systems/hooks) that filters which properties get the strict
treatment, Panda doesn't ship that filtering out of the box.
The generated style-prop types themselves are large by nature too, they enumerate every CSS property against its
token union, independent of `strictTokens`. If your editor is slow across a big project, check whether disabling
`strictTokens` measurably helps before assuming it's something else, that's the one config knob that changes the
size of what TypeScript has to check on every style call.
## See also
- [How Panda works](/docs/styling/how-panda-works) for why there's no runtime cost to weigh against these build/IDE
costs in the first place.
- [Debugging](/docs/reference/debugging) for `--profile`, `panda debug`, and `PANDA_DEBUG`.
- [Static CSS Generation](/docs/design-systems/static) and [Federated Micro-Frontends](/docs/design-systems/federated-microfrontends)
for scaling considerations once you're shipping to multiple apps, not just optimizing one.
---
## Using PostCSS
Installing Panda as a PostCSS plugin is the recommended way to integrate it with your project.
This guide shows you how to install Panda as a PostCSS plugin, which is the recommended way to integrate it with your
project.
### Install Panda
Install panda and create your `panda.config.ts` file.
{/* */}
```bash
pnpm install -D @pandacss/dev postcss
pnpm panda init -p
```
```bash
npm install -D @pandacss/dev postcss
npx panda init -p
```
```bash
yarn add -D @pandacss/dev postcss
yarn panda init -p
```
```bash
bun add -D @pandacss/dev postcss
bun panda init -p
```
{/* */}
### Add Panda to your PostCSS config
Add panda to your `postcss.config.cjs` file, or wherever PostCSS is configured in your project.
```js
module.exports = {
plugins: {
'@pandacss/dev/postcss': {}
}
}
```
### Configure the content
Add your panda config to your `panda.config.js` file, or wherever panda is configured in your project.
```js {5}
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
preflight: true,
include: ['./src/**/*.{ts,tsx,js,jsx}', './pages/**/*.{ts,tsx,js,jsx}'],
exclude: [],
outdir: 'styled-system'
})
```
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3}
{
"scripts": {
+ "prepare": "panda codegen",
}
}
```
The `prepare` script will run codegen after dependency installation. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the entry CSS with layers
Add this code to an `index.css` file which is going to be the root css of your project.
```css
@layer reset, base, tokens, recipes, utilities;
```
### Start your build process
Run your build process by feeding the [root css](#configure-the-entry-css-with-layers) to PostCSS in your preferred way.
{/* */}
```bash
postcss -o output.css index.css
```
```js
const postcss = require("postcss");
const fs = require("fs");
fs.readFile("index.css", (err, css) => {
postcss()
.process(css, { from: "index.css", to: "output.css" })
.then((result) => {
console.log(result.css);
});
});
```
{/* */}
> If you're using a framework, PostCSS is probably already integrated with your build process. Check our other guides or
> the documentation of your framework to see how to configure PostCSS.
### Start using Panda
Use the generated style utilities in your code and panda will extract them to the generated CSS file.
```jsx
import { css } from './styled-system/css'
export function App() {
return
}
```
## Troubleshooting
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
`tsconfig.json` file:
```json filename="tsconfig.json"
{
// ...
"include": ["src", "styled-system"]
}
```
---
## Using Preact
Easily use Panda with Preact with our dedicated integration.
This guide shows you how to set up Panda CSS in a Preact project using PostCSS.
## Start a new project
### Create Vite project
To get started, we will need to create a new Preact project using `typescript` template.
{/* */}
```bash
npx preact-cli create typescript test-app
cd test-app
```
```bash
npx preact-cli create typescript test-app --yarn
cd test-app
```
{/* */}
### Install Panda
Install panda and create your `panda.config.ts` file.
{/* */}
```bash
npm install -D @pandacss/dev
npx panda init --postcss
```
```bash
yarn add -D @pandacss/dev
yarn panda init --postcss
```
{/* */}
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3} filename="package.json"
{
"scripts": {
+ "prepare": "panda codegen",
"build": "cross-env NODE_OPTIONS=--openssl-legacy-provider preact build",
"serve": "sirv build --cors --single",
"dev": "cross-env NODE_OPTIONS=--openssl-legacy-provider preact watch",
"lint": "eslint src",
"test": "jest"
}
}
```
- `"prepare"` - script that will run Panda CSS CLI codegen before each build. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the content
Make sure that all of the paths of your Preact components are included in the `include` section of the `panda.config.ts`
file.
```js {6} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
preflight: true,
// Where to look for your css declarations
include: ['./src/**/*.{js,jsx,ts,tsx}', './pages/**/*.{js,jsx,ts,tsx}'],
exclude: [],
outdir: 'styled-system'
})
```
### Configure the entry CSS with layers
Add this code to an `src/style/index.css` file imported in the root component of your project.
```css filename="src/style/index.css"
@layer reset, base, tokens, recipes, utilities;
```
## Start your build process
Run the following command to start your development server.
{/* */}
```bash
npm run dev
```
```bash
yarn dev
```
{/* */}
### Start using Panda
Now you can start using Panda CSS in your project. Here is the snippet of code that you can use in your
`src/routes/home/index.tsx` file.
```tsx filename="src/routes/home/index.tsx"
import { h } from 'preact'
import { css } from '../../../styled-system/css'
const Home = () => {
return
Hello πΌ!
}
export default Home
```
## Troubleshooting
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
`tsconfig.json` file:
```json filename="tsconfig.json"
{
// ...
"include": ["src", "styled-system"]
}
```
---
## Using Qwik
Easily use Panda with Qwik with our dedicated integration.
Learn how to set up Panda CSS in a Qwik project using PostCSS.
## Start a new project
### Create Qwik project
To get started, we will need to create a new Qwik project using `typescript` template.
{/* */}
```bash
npm create qwik@latest
```
```bash
pnpm create qwik@latest
```
```bash
yarn create qwik
```
```bash
bun create qwik@latest
```
{/* */}
### Install and Configure Panda
Qwik provies an official script that installs panda and configures it for you.
{/* */}
```bash
npm run qwik add pandacss
```
```bash
pnpm qwik add pandacss
```
```bash
yarn qwik add pandacss
```
```bash
bun qwik add pandacss
```
{/* */}
## Start your build process
Run the following command to start your development server.
{/* */}
```bash
npm run dev
```
```bash
pnpm dev
```
```bash
yarn dev
```
```bash
bun dev
```
{/* */}
### Start using Panda
Now you can start using Panda CSS in your project.
```tsx filename="src/routes/layout.tsx"
import { component$, Slot } from '@builder.io/qwik'
import { routeLoader$ } from '@builder.io/qwik-city'
import { css } from 'styled-system/css'
export const useServerTimeLoader = routeLoader$(() => {
return {
date: new Date().toISOString()
}
})
export default component$(() => {
return (
)
})
```
## Troubleshooting
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
`tsconfig.json` file:
```json filename="tsconfig.json"
{
// ...
"include": ["src", "styled-system"]
}
```
---
## Using React Router
Easily use Panda with React Router with our dedicated integration.
This guide will show you how to set up Panda CSS in a React Router project using PostCSS.
## Start a new project
### Create project
To get started, we will need to create a new React Router project using the official
[Create React Router](https://reactrouter.com/start/framework/installation) CLI. In this guide, we will use TypeScript.
If you don't enter any parameter, the CLI will guide you through the process of creating a new React Router app.
{/* */}
```bash
pnpm dlx create-react-router@latest
```
```bash
npx create-react-router@latest
```
```bash
yarn dlx create-react-router@latest
```
```bash
bunx create-react-router@latest
```
{/* */}
You will be asked a few questions, answer these as follows:
```sh
? Where should we create your new project? test-app
? Install dependencies? No
```
> **Note:** You should decline the dependency installation step as we will install dependencies together with Panda CSS.
### Install Panda
Install panda and create your `panda.config.ts` file.
{/* */}
```bash
pnpm install -D @pandacss/dev
pnpm panda init --postcss
```
```bash
npm install -D @pandacss/dev
npx panda init --postcss
```
```bash
yarn add -D @pandacss/dev
yarn panda init --postcss
```
```bash
bun add -D @pandacss/dev
bun panda init --postcss
```
{/* */}
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3} filename="package.json"
{
"scripts": {
+ "prepare": "panda codegen",
"build": "cross-env NODE_ENV=production react-router build",
"dev": "react-router dev",
"start": "cross-env NODE_ENV=production react-router-serve ./build/server/index.js",
"typecheck": "react-router typegen && tsc"
},
}
```
- `"prepare"` - script that will run Panda CSS CLI codegen before each build. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the Panda CSS output directory to your `.gitignore` file and not worry about it.
### Configure the content
Make sure that all of the paths of your React Router components are included in the `include` section of the
`panda.config.ts` file.
```ts {5,8,11} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ['./app/**/{**,.client,.server}/**/*.{js,jsx,ts,tsx}'],
// Files to exclude
exclude: [],
// The output directory for your css system
outdir: 'styled-system'
})
```
### Replace TailwindCSS with PandaCSS
Update the `vite.config.ts` file to use PandaCSS instead of TailwindCSS.
```ts {3,10} filename="vite.config.ts"
import { reactRouter } from '@react-router/dev/vite'
import autoprefixer from 'autoprefixer'
import pandacss from '@pandacss/dev/postcss'
import { defineConfig } from 'vite'
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
css: {
postcss: {
plugins: [pandacss, autoprefixer]
}
},
plugins: [reactRouter(), tsconfigPaths()]
})
```
### Configure the entry CSS with layers
Create a new file `app/app.css` and add the following content:
```css filename="app/app.css"
@layer reset, base, tokens, recipes, utilities;
```
### Update the root component
Import the `app.css` file in your `app/root.tsx` file and add the `styles` variable to the `links` function.
Please note the `?url` query parameter in the `import` statement. This is required by Vite to generate the correct path
to the CSS file.
```tsx {4,8} filename="app/root.tsx"
import { isRouteErrorResponse, Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router'
import type { Route } from './+types/root'
import stylesheet from './app.css?url'
export const links: LinksFunction = () => [
// ...
{ rel: 'stylesheet', href: stylesheet }
]
export function Layout({ children }: { children: React.ReactNode }) {
return (
{children}
)
}
```
### Start your build process
Run the following command to start your development server.
{/* */}
```bash
pnpm dev
```
```bash
npm run dev
```
```bash
yarn dev
```
```bash
bun dev
```
{/* */}
### Start using Panda
Now you can start using Panda CSS in your project. Here is the snippet of code that you can use in your
`app/routes/home.tsx` file.
```tsx filename="app/routes/home.tsx"
import type { Route } from './+types/home'
import { css } from 'styled-system/css'
export function meta({}: Route.MetaArgs) {
return [{ title: 'New React Router App' }, { name: 'description', content: 'Welcome to React Router!' }]
}
export default function Home() {
return (
Welcome to the home page
)
}
```
## Troubleshooting
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
`tsconfig.json` file:
```json filename="tsconfig.json"
{
// ...
"include": ["app", "styled-system"]
}
```
---
## Recipes
Panda provides a way to write CSS-in-JS with better performance, developer experience, and composability.
Recipes are a way to create multi-variant styles with a type-safe runtime API.
A recipe consists of four properties:
- `base`: The base styles for the component
- `variants`: The different visual styles for the component
- `compoundVariants`: The different combinations of variants for the component
- `defaultVariants`: The default variant values for the component
> **Credit:** This API was inspired by [Stitches](https://stitches.dev/),
> [Vanilla Extract](https://vanilla-extract.style/), and [Class Variance Authority](https://cva.style/).
[Comparison table between the different types of recipes here: "Should I use atomic or config recipes ?"](/docs/styling/recipes#should-i-use-atomic-or-config-recipes-)
## Atomic Recipe (or cva)
Atomic recipes are a way to create multi-variant atomic styles with a type-safe runtime API.
They are defined using the `cva` function which was inspired by [Class Variance Authority](https://cva.style/). The
`cva` function which takes an object as its argument.
> **Note:** `cva` is not the same as [Class Variance Authority](https://cva.style/). The `cva` from Panda is a
> purpose-built function for creating atomic recipes that are connected to your design tokens and utilities.
### Defining the recipe
```jsx
import { cva } from '../styled-system/css'
const button = cva({
base: {
display: 'flex'
},
variants: {
visual: {
solid: { bg: 'red.200', color: 'white' },
outline: { borderWidth: '1px', borderColor: 'red.200' }
},
size: {
sm: { padding: '4', fontSize: '12px' },
lg: { padding: '8', fontSize: '24px' }
}
}
})
```
### Using the recipe
The returned value from the `cva` function is a function that can be used to apply the recipe to a component. Here's an
example of how to use the `button` recipe:
```jsx
import { button } from './button'
const Button = () => {
return
}
```
When a recipe is created, Panda will extract and generate CSS for every variant and compoundVariant `css` ahead of time,
as atomic classes.
```css
@layer utilities {
.d_flex {
display: flex;
}
.bg_red_200 {
background-color: #fed7d7;
}
.color_white {
color: #fff;
}
.border_width_1px {
border-width: 1px;
}
/* ... */
}
```
### Setting the default variants
The `defaultVariants` property is used to set the default variant values for the recipe. This is useful when you want to
apply a variant by default. Here's an example of how to use `defaultVariants`:
```jsx
import { cva } from '../styled-system/css'
const button = cva({
base: {
display: 'flex'
},
variants: {
visual: {
solid: { bg: 'red.200', color: 'white' },
outline: { borderWidth: '1px', borderColor: 'red.200' }
},
size: {
sm: { padding: '4', fontSize: '12px' },
lg: { padding: '8', fontSize: '24px' }
}
},
defaultVariants: {
visual: 'solid',
size: 'lg'
}
})
```
### Compound Variants
Compound variants are a way to combine multiple variants together to create more complex sets of styles. They are
defined using the `compoundVariants` property , which takes an array of objects as its argument. Each object in the
array represents a set of conditions that must be met in order for the corresponding styles to be applied.
Here's an example of how to use `compoundVariants` in Panda:
```js
import { cva } from '../styled-system/css'
const button = cva({
base: {
padding: '8px 16px',
borderRadius: '4px',
fontSize: '16px',
fontWeight: 'bold'
},
variants: {
size: {
small: {
fontSize: '14px',
padding: '4px 8px'
},
medium: {
fontSize: '16px',
padding: '8px 16px'
},
large: {
fontSize: '18px',
padding: '12px 24px'
}
},
color: {
primary: {
backgroundColor: 'blue',
color: 'white'
},
secondary: {
backgroundColor: 'gray',
color: 'black'
}
},
disabled: {
true: {
opacity: 0.5,
cursor: 'not-allowed'
}
}
},
// compound variants
compoundVariants: [
// apply when both small size and primary color are selected
{
size: 'small',
color: 'primary',
css: {
border: '2px solid blue'
}
},
// apply when both large size and secondary color are selected and the button is disabled
{
size: 'large',
color: 'secondary',
disabled: true,
css: {
backgroundColor: 'lightgray',
color: 'darkgray',
border: 'none'
}
},
// apply when both small or medium size, and secondary color variants are applied
{
size: ['small', 'medium'],
color: 'secondary',
css: {
fontWeight: 'extrabold'
}
}
]
})
```
Here's an example usage of the `button` recipe:
```jsx
import { button } from './button'
const Button = () => {
// will apply size: small, color: primary, css: { border: '2px solid blue' }
return
}
```
Overall, using compound variants allows you to create more complex sets of styles that can be applied to your components
based on multiple conditions.
By combining simple variants together in this way, you can create a wide range of visual styles without cluttering up
your code with lots of conditional logic.
For config recipes (`defineRecipe`), see [Using compound variants](#using-compound-variants) under Config Recipe.
### TypeScript Guide
Panda provides two type utilities for inferring the variant types of a recipe: `RecipeVariant` and `RecipeVariantProps`.
Use `RecipeVariant` to infer the raw variant type of a recipe. Each variant key is required.
```tsx
import { cva, type RecipeVariant } from '../styled-system/css'
const buttonStyle = cva({
base: {
color: 'red',
textAlign: 'center'
},
variants: {
size: {
small: {
fontSize: '1rem'
},
large: {
fontSize: '2rem'
}
}
}
})
export type ButtonVariants = RecipeVariant
// { size: 'small' | 'large' }
```
Use `RecipeVariantProps` when you want to use the recipe in JSX and need type safety for the variants as optional props.
```tsx
import { styled } from '../styled-system/jsx'
import { cva, type RecipeVariantProps } from '../styled-system/css'
const buttonStyle = cva({
base: {
color: 'red',
textAlign: 'center'
},
variants: {
size: {
small: {
fontSize: '1rem'
},
large: {
fontSize: '2rem'
}
}
}
})
export type ButtonVariants = RecipeVariantProps
// { size?: 'small' | 'large' | undefined } | undefined
```
### Usage in JSX
You can create a JSX component from any existing atomic recipe by using the `styled` function from the `/jsx`
entrypoint.
The `styled` function takes the element type as its first argument, and the recipe as its second argument.
> Make sure to add the `jsxFramework` option to your `panda.config` file, and run `panda codegen` to generate the JSX
> entrypoint.
```js
import { cva } from '../styled-system/css'
import { styled } from '../styled-system/jsx'
const buttonStyle = cva({
base: {
color: 'red',
textAlign: 'center'
},
variants: {
size: {
small: {
fontSize: '1rem'
},
large: {
fontSize: '2rem'
}
}
}
})
const Button = styled('button', buttonStyle)
```
Then you can use the component in JSX
```jsx
```
## Config Recipe
Config recipes are extracted and generated just in time, this means regardless of the number of recipes in the config,
only the recipes and variants you use will exist in the generated CSS.
The config recipe takes the following additional properties:
- `className`: The name of the recipe. Used in the generated class name
- `jsx`: An array of JSX components that use the recipe. Defaults to the uppercase version of the recipe name
- `description`: An optional description of the recipe (used in the js-doc comments)
> As of v0.9, the `name` property is removed in favor of `className`
### Defining the recipe
To define a config recipe, import the `defineRecipe` helper function
```jsx filename="button.recipe.ts"
import { defineRecipe } from '@pandacss/dev'
export const buttonRecipe = defineRecipe({
className: 'button',
description: 'The styles for the Button component',
base: {
display: 'flex'
},
variants: {
visual: {
funky: { bg: 'red.200', color: 'white' },
edgy: { border: '1px solid {colors.red.500}' }
},
size: {
sm: { padding: '4', fontSize: '12px' },
lg: { padding: '8', fontSize: '40px' }
},
shape: {
square: { borderRadius: '0' },
circle: { borderRadius: 'full' }
}
},
defaultVariants: {
visual: 'funky',
size: 'sm',
shape: 'circle'
}
})
```
### Adding recipe to config
To add the recipe to the config, youβd need to add it to the `theme.recipes` object.
```jsx filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
import { buttonRecipe } from './button.recipe'
export default defineConfig({
//...
jsxFramework: 'react',
theme: {
extend: {
recipes: {
button: buttonRecipe
}
}
}
})
```
### Generate JS code
This generates a recipes folder the specified `outdir` which is `styled-system` by default. If Panda doesnβt
automatically generate your CSS file, you can run the `panda codegen` command.
You only need to import the recipes into the component files where you need to use them.
### Using the recipe
To use the recipe, you can import the recipe from the `/recipes` entrypoint and use it in your component. Panda
tracks the usage of the recipe and only generates CSS of the variants used in your application.
```js
import { button } from '../styled-system/recipes'
function App() {
return (
)
}
```
The generated css is registered under the `recipe` [cascade layer](/docs/styling/cascade-layers.mdx) with the class
name that matches the recipe-variant name pattern `--`.
> **Technical Notes π:** Only the recipe and variants used in your application are generated. Not more!
```css
@layer recipes {
@layer base {
.button {
font-size: var(--font-sizes-lg);
}
}
.button--visual-funky {
background-color: var(--colors-red-200);
color: var(--colors-white);
}
.button--size-lg {
padding: var(--space-8);
font-size: var(--font-sizes-40px);
}
}
```
### Dynamic variant props
Config recipes only emit CSS for variant values Panda can see at build time. `button({ size })` still returns class
names at runtime, but if that CSS was never generated, the styles won't apply.
- `button({ size: 'lg' })` β emits `lg`
- `button({ size: wide ? 'sm' : 'lg' })` β emits both branches
- `button({ size })` or `` β emits `defaultVariants` only
When a variant comes from a prop or state, pre-generate it:
```ts filename="button.recipe.ts"
export const buttonRecipe = defineRecipe({
className: 'button',
staticCss: ['*'] // or [{ size: ['sm', 'md', 'lg'] }]
})
```
Or add literal calls in stories/tests, or use string literals in the call itself.
Slot recipes follow the same rules. See [slot recipes](/docs/styling/slot-recipes#dynamic-variant-props). More in
[static CSS](/docs/design-systems/static#generating-recipes).
### Using compound variants
Apply styles when **multiple** variant props match. Same shape as [`cva`](/docs/styling/recipes#compound-variants): a
flat `css` object on each entry:
```ts filename="input.recipe.ts"
import { defineRecipe } from '@pandacss/dev'
export const inputRecipe = defineRecipe({
className: 'input',
base: {
borderWidth: '1px',
borderColor: 'gray.200',
borderRadius: 'md'
},
variants: {
size: {
sm: { fontSize: 'sm' },
md: { fontSize: 'md' }
},
variant: {
outline: { bg: 'transparent' },
filled: { bg: 'gray.100' }
}
},
defaultVariants: {
size: 'md',
variant: 'outline'
},
compoundVariants: [
{
size: 'sm',
variant: 'outline',
css: { px: '2', py: '1' }
},
{
size: 'md',
variant: 'outline',
css: { px: '3', py: '2' }
}
]
})
```
```ts filename="panda.config.ts"
theme: {
extend: {
recipes: {
input: inputRecipe
}
}
}
```
```tsx
import { input } from '../styled-system/recipes'
input({ size: 'sm', variant: 'outline' })
// β "input input--size_sm input--variant_outline px_2 py_1"
```
First extracted use atomizes every compound `css` object into `@layer utilities`:
```css
@layer utilities {
.px_2 {
padding-inline: var(--spacing-2);
}
.py_1 {
padding-block: var(--spacing-1);
}
.px_3 {
padding-inline: var(--spacing-3);
}
.py_2 {
padding-block: var(--spacing-2);
}
}
```
Adding `compoundVariants` also drops responsive variant props. A recipe like **`datepicker`** without compounds keeps
`ConditionalValue`:
```ts
// styled-system/recipes/date-picker.ts
export type DatePickerVariantProps = {
size?: ConditionalValue<'sm' | 'md'>
}
```
```tsx
datePicker({ size: { base: 'sm', md: 'lg' } }) // β
```
**`input`** above accepts plain literals only:
```ts
// styled-system/recipes/input.ts
export type InputVariantProps = {
size?: 'sm' | 'md'
}
```
```tsx
input({ size: { base: 'sm', md: 'lg' } })
// β [recipe:input:size] Conditions are not supported when using compound variants.
```
At runtime, only the matching combo merges via `getCompoundVariantCss`:
```tsx
input({ size: 'sm', variant: 'outline' })
// "input input--size_sm input--variant_outline px_2 py_1"
input({ size: 'md', variant: 'outline' })
// "input input--size_md input--variant_outline px_3 py_2"
```
Runtime-only combos need [`staticCss`](/docs/design-systems/static#generating-recipes). Run `pnpm panda codegen` after editing
the recipe.
### Responsive and Conditional variants
Pass breakpoint objects to variant props instead of a single value. Generated types use `ConditionalValue`.
This only works when:
- The recipe is defined in config with `defineRecipe`, not [`cva`](/docs/styling/recipes#atomic-recipe-or-cva)
- The recipe has no `compoundVariants` (see [Using compound variants](#using-compound-variants))
```jsx
import { button } from '../styled-system/recipes'
function App() {
return (
)
}
```
> In most cases, we don't recommend applying conditional variants inline. Ideally, you might want to render different
> views for your responsive breakpoints.
### TypeScript Guide
Every recipe ships a type interface for its accepted variants. You can import them from the `styled-system/recipes`
entrypoint.
For the button recipe, we can import the `ButtonVariants` type like so:
```ts
import React from 'react'
import type { ButtonVariants } from '../styled-system/recipes'
type ButtonProps = ButtonVariants & {
children: React.ReactNode
}
```
### Usage in JSX
Layer recipes can be consumed directly in your custom JSX components. Panda will automatically track the usage of the
recipe if the component name matches the recipe name.
For example, if your recipe is called `button` and you create a `Button` component from it, Panda will automatically
track the usage of the variant properties.
```tsx
import React from 'react'
import { button, type ButtonVariants } from '../styled-system/recipes'
type ButtonProps = ButtonVariants & {
children: React.ReactNode
}
const Button = (props: ButtonProps) => {
const { children, size } = props
return (
)
}
const App = () => {
return (
)
}
```
### Advanced JSX Tracking
We recommend that you use the recipe functions in most cases, in design systems there might be a need to compose
existing components (like Button) to create new components.
To track the usage of the recipes in these cases, you'll need to add the `jsx` hint for the recipe config
```js {12} filename="button.recipe.ts"
import { defineRecipe } from '@pandacss/dev'
const button = defineRecipe({
base: {
color: 'red',
fontSize: '1.5rem'
},
variants: {
// ...
},
// Add the jsx hint to track the usage of the recipe in JSX, you can use regex to match multiple components
jsx: ['Button', 'PageButton']
})
```
Then you can create a new component that uses the `Button` component and Panda will track the usage of the `button`
recipe as well.
```tsx
const PageButton = (props: ButtonProps) => {
const { children, size } = props
return (
)
}
```
#### Extending a preset recipe
If you're using a recipe from a preset, you can still extend it in your config.
```js
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
//...
jsxFramework: 'react',
theme: {
extend: {
recipes: {
button: {
className: 'something-else', // π override the className
base: {
color: 'red', // π replace some part of the recipe
fontSize: '1.5rem' // or add new styles
},
variants: {
// ... // π add or extend new variants
},
jsx: ['Button', 'PageButton'] // π extend the jsx tracking hint
}
}
}
}
})
```
Learn more about the [extend](/docs/styling/extend.md) keyword.
## Methods and Properties
Both atomic and config recipe ships a helper methods and properties that can be used to get information about the
recipe.
- `variantKeys`: An array of the recipe variant keys
- `variantMap`: An object of the recipe variant keys and their values
- `splitVariantProps`: A function that takes an object as its argument and returns an array containing the recipe
variant props and the rest of the props
```js
import { cva } from '../styled-system/css'
const buttonRecipe = cva({
base: {
color: 'red',
fontSize: '1.5rem'
},
variants: {
size: {
sm: {
fontSize: '1rem'
},
md: {
fontSize: '2rem'
}
}
}
})
buttonRecipe.variantKeys
// => ['size']
buttonRecipe.variantMap
// => { size: ['sm', 'md'] }
buttonRecipe.splitVariantProps({ size: 'sm', onClick() {} })
// => [{ size: 'sm'}, { onClick() {} }]
```
These methods and properties are useful when creating custom components or writing Storybook stories for your recipes.
Here's a Storybook example.
```tsx filename="button.stories.tsx"
import { Button, buttonRecipe } from './components/button'
export default {
title: 'Button',
component: Button,
argTypes: {
size: {
control: {
type: 'select',
options: buttonRecipe.variantMap.size
}
}
}
}
export const Demo = {
render: args =>
}
```
## Best Practices
- Leverage css variables in the base styles as much as possible. Makes it easier to theme the component with JS
- Don't mix styles by writing complex selectors. Separate concerns and group them in logical variants
- Use the `compoundVariants` property to create more complex sets of styles
## Limitations
- [`cva`](/docs/styling/recipes#atomic-recipe-or-cva): no responsive variant props. Config recipes: yes, unless
`compoundVariants` is set (see [above](/docs/styling/recipes#using-compound-variants)).
- Due to static nature of Panda, it's not possible to track the usage of the recipes in all cases. Here are some of use
cases that Panda won't be able to track the usage of the recipe variants:
**When you change the name of the variant prop in the JSX component**
In below example, the `size` prop is renamed to `buttonSize`
```tsx
const Button = ({ buttonSize, children }) => {
return (
)
}
```
**When you use the recipe in a custom component that is not named as per the recipe name, Panda won't be able to track
the usage of the recipe variants.**
In below example, the component name `Button` is renamed to `Random` and we are using `button` recipe.
```tsx
const Random = ({ size, children }) => {
return (
)
}
```
## Static CSS
Use `staticCss` to pre-generate recipe CSS Panda can't extract, including [dynamic variant props](/docs/styling/recipes#dynamic-variant-props). See [Generating recipes](/docs/design-systems/static#generating-recipes).
## Should I use atomic or config recipes ?
[Config recipes](/docs/styling/recipes#config-recipe) are JIT: Panda only emits variants it finds in your code.
[`cva`](/docs/styling/recipes#atomic-recipe-or-cva) recipes live anywhere in the app, so every variant is generated up
front.
Use config recipes for design-system components and leaner CSS. Use `cva` for colocation and runtime merging.
| | Config recipe | Atomic recipe (cva) |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| Theme tokens, utilities, conditions | β | β |
| JIT: CSS for variants used in code | β | β all variants always |
| Shareable in a preset | β | β |
| Responsive variant props | β without `compoundVariants` | β |
| Colocate with components | β define in config | β |
| Atomic utility classes | β named recipe classes | β |
| Runtime merge with `css()` | β use [`cx`](/docs/styling/merging-styles#merging-config-recipe-and-style-object) for classes | β [`.raw()` + `css()`](/docs/styling/merging-styles#merging-cva--css-styles) |
---
## Using Redwood
Easily use Panda with Redwood.js with our dedicated integration.
This guide shows you how to set up Panda CSS in a Redwood project using PostCSS.
> Redwood uses `yarn` as its primary package manager.
## Start a new project
### Create Redwood project
To get started, we will need to create a new Redwood project using `typescript` template.
```bash
yarn create redwood-app app
```
Respond to the prompts as follows:
```bash
β Compatibility checks passed
β Select your preferred language Β· TypeScript
β Do you want to initialize a git repo? Β· no / Yes
β Enter a commit message Β· Initial commit
β Do you want to run yarn install? Β· no / Yes
β Project files created
β Initialized a git repo with commit message "Initial commit"
β Installed node modules
β Generated types
```
### Install Panda
Install panda and generate the `panda.config.ts` and `postcss.config.cjs` file.
```bash
cd web
yarn add -D @pandacss/dev postcss postcss-loader
yarn panda init --postcss
```
### Move to config folder
Redwood uses a `config` folder for all of the configuration files. Move the `panda.config.ts` and `postcss.config.cjs`
files to the `config` folder.
```bash
mv panda.config.ts postcss.config.cjs config/
```
### Update configs
Update the postcss config file to use the `panda.config.ts` file.
```diff filename="web/config/postcss.config.cjs"
+ const path = require('path')
module.exports = {
plugins: {
"@pandacss/dev/postcss": {
+ configPath: path.resolve(__dirname, 'panda.config.ts'),
},
},
}
```
Update the tsconfig file to include the `styled-system` folder.
```json {6} filename="web/tsconfig.json"
{
// ...
"compilerOptions": {
"paths": {
// ...
"styled-system/*": ["./styled-system/*"]
}
}
}
```
### Update package.json scripts
Open the `web/package.json` file and update the `scripts` section as follows:
```diff {3} filename="web/package.json"
{
"scripts": {
+ "prepare": "panda codegen"
}
}
```
- `"prepare"` - script that will run Panda CSS CLI codegen before each build. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the content
Make sure that all of the paths of your Redwood components are included in the `include` section of the
`panda.config.ts` file.
```js {5} filename="web/config/panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
preflight: true,
include: ['./src/**/*.{js,jsx,ts,tsx}'],
exclude: [],
outdir: 'styled-system'
})
```
### Configure the entry CSS with layers
Add this code to an `src/style/index.css` file imported in the root component of your project.
```css filename="web/src/index.css"
@layer reset, base, tokens, recipes, utilities;
```
## Start your build process
Run the following command to start your development server.
```bash
yarn rw dev
```
### Start using Panda
Now you can start using Panda CSS in your project.
```tsx filename="web/src/pages/HomePage/HomePage.tsx"
import { css } from 'styled-system/css'
import { stack } from 'styled-system/patterns'
import { Link, routes } from '@redwoodjs/router'
import { MetaTags } from '@redwoodjs/web'
const HomePage = () => {
return (
<>
HomePage
Hello World
>
)
}
export default HomePage
```
## Troubleshooting
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
`tsconfig.json` file:
```json filename="web/tsconfig.json"
{
// ...
"include": ["src", "styled-system"]
}
```
---
## Using Remix
How to use Panda with Remix with our dedicated integration.
This guide will show you how to set up Panda CSS in a Remix project using PostCSS.
## Start a new project
### Create Remix project
To get started, we will need to create a new Remix project using the official
[Create Remix](https://remix.run/docs/en/main/start/quickstart) CLI. In this guide, we will use TypeScript.
If you don't enter any parameter, the CLI will guide you through the process of creating a new Remix app.
{/* */}
```bash
pnpm dlx create-remix@latest
```
```bash
npx create-remix@latest
```
```bash
yarn dlx create-remix@latest
```
```bash
bunx create-remix@latest
```
{/* */}
You will be asked a few questions, answer these as follows:
```sh
? Where should we create your new project? test-app
? Install dependencies? No
```
> **Note:** You should decline the dependency installation step as we will install dependencies together with Panda CSS.
### Install Panda
Install panda and create your `panda.config.ts` file.
{/* */}
```bash
pnpm install -D @pandacss/dev
pnpm panda init --postcss
```
```bash
npm install -D @pandacss/dev
npx panda init --postcss
```
```bash
yarn add -D @pandacss/dev
yarn panda init --postcss
```
```bash
bun add -D @pandacss/dev
bun panda init --postcss
```
{/* */}
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3} filename="package.json"
{
"scripts": {
+ "prepare": "panda codegen",
"build": "remix build",
"dev": "remix dev",
"start": "remix-serve build",
"typecheck": "tsc"
}
}
```
- `"prepare"` - script that will run Panda CSS CLI codegen before each build. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the Panda CSS output directory to your `.gitignore` file and not worry about it.
### Configure the content
Make sure that all of the paths of your Remix components are included in the `include` section of the `panda.config.ts`
file.
```js {5, 11} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ['./app/routes/**/*.{ts,tsx,js,jsx}', './app/components/**/*.{ts,tsx,js,jsx}'],
// Files to exclude
exclude: [],
// The output directory for your css system
outdir: 'styled-system'
})
```
### Configure the entry CSS with layers
Create a new file `app/index.css` and add the following content:
```css filename="app/index.css"
@layer reset, base, tokens, recipes, utilities;
```
Import the `index.css` file in your `app/root.tsx` file and add the `styles` variable to the `links` function.
Please note the `?url` query parameter in the `import` statement. This is required by Vite to generate the correct path
to the CSS file.
```tsx filename="app/root.tsx" {4,6}
import type { LinksFunction } from '@remix-run/node'
import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react'
import styles from './index.css?url'
export const links: LinksFunction = () => [{ rel: 'stylesheet', href: styles }]
export default function App() {
return (
)
}
```
### Start your build process
Run the following command to start your development server.
{/* */}
```bash
pnpm dev
```
```bash
npm run dev
```
```bash
yarn dev
```
```bash
bun dev
```
{/* */}
### Start using Panda
Now you can start using Panda CSS in your project. Here is the snippet of code that you can use in your
`app/routes/_index.tsx` file.
```tsx filename="app/routes/_index.tsx"
import type { MetaFunction } from '@remix-run/node'
import { css } from 'styled-system/css'
export const meta: MetaFunction = () => {
return [{ title: 'New Remix App' }, { name: 'description', content: 'Welcome to Remix!' }]
}
export default function Index() {
return
Hello πΌ!
}
```
## Troubleshooting
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
`tsconfig.json` file:
```json filename="tsconfig.json"
{
// ...
"include": ["src", "styled-system"]
}
```
If your app doesn't reload when making changes to the `panda.config.ts` file, consider adding `watchPaths` in your remix
config file.
```js filename="remix.config.js"
export default {
// ...
watchPaths: ['panda.config.ts']
}
```
---
## Responsive Design
How to write mobile responsive designs in your CSS in Panda
Responsive design is a fundamental aspect of modern web development, allowing websites and applications to adapt
seamlessly to different screen sizes and devices.
Panda provides a comprehensive set of responsive utilities and features to facilitate the creation of responsive
layouts. It lets you do this through conditional styles for different breakpoints.
Let's say you want to change the font weight of a text on large screens, you can do it like this:
```jsx
Text
```
> Panda uses a mobile-first breakpoint system and leverages min-width media queries `@media(min-width)` when you write
> responsive styles.
Panda provides five breakpoints by default:
```ts
const breakpoints = {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px'
}
```
## Overview
### Property based modifier
Panda allows you apply the responsive condition directly to a style property, resulting in a more concise syntax:
```diff
Text
```
### The Array syntax
Panda also accepts arrays as values for responsive styles. Pass the corresponding value for each breakpoint in the
array. Using our previous code as an example:
```jsx
Text
```
> We're leaving the corresponding values of the unused breakpoints `md` and `lg` as undefined.
### Targeting a breakpoint range
By default, styles assigned to a specific breakpoint will be effective at that breakpoint and will persist as applied
styles at larger breakpoints.
If you wish to apply a utility exclusively when a particular range of breakpoints is active, Panda offers properties
that restrict the style to that specific range. To construct the property, combine the minimum and maximum breakpoints
using the "To" notation in camelCase format.
Let's say we want to apply styles between the `md` and `xl` breakpoints, we use the `mdToXl` property:
```jsx
Text
```
> This text will only be bold in `md`, `lg` and `xl` breakpoints.
### Targeting a single breakpoint
To target a single breakpoint, you can easily achieve this by simply adding the suffix "Only" to the breakpoint name in
camelCase format.
Let's say we want to apply styles only in the `lg` breakpoint, we use the `lgOnly` property:
```jsx
Text
```
### Customizing Breakpoints
When encountering certain scenarios, it may become necessary to establish custom breakpoints tailored to your
application's needs. It is advisable to utilize commonly used aliases such as `sm`, `md`, `lg`, and `xl` for this
purpose.
In order to define custom breakpoints, you can easily accomplish this by passing them as an object within your Panda
config.
> Note: Make sure that the CSS units of your breakpoints are consistent. Use either all pixels (`px`) or all `em`, but
> do not mix them.
```js filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// ...
theme: {
extend: {
breakpoints: {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px'
}
}
}
})
```
### Patterns and recipes
In `css()`, breakpoint keys like `md` and `lg` work on CSS utilities.
Pattern props are different. Props like `columns`, `colSpan`, and `direction` are compiled by the pattern, not passed
to CSS as-is. Put the breakpoint on the prop: `columns={{ base: 1, md: 2 }}`.
See [responsive pattern props](/docs/styling/patterns#responsive-pattern-props).
### Hiding elements by breakpoint
If you need to limit the visibility of an element to any breakpoint, Panda provides
[display utilities](/docs/reference/display) to help you achieve this.
---
## Using Rsbuild
Easily use Panda with Rsbuild, React and Typescript with our dedicated integration.
This guide shows you how to set up Panda CSS in a Rsbuild project using PostCSS.
## Start a new project
### Create Rsbuild project
To get started, we will need to create a new Rsbuild project using `react-ts` template.
{/* */}
```bash
pnpm create rsbuild@latest --dir test-app --template react-ts
cd test-app
pnpm install
```
```bash
npm create rsbuild@latest --dir test-app -- --template react-ts
cd test-app
npm install
```
```bash
yarn create rsbuild@latest --dir test-app --template react-ts
cd test-app
yarn
```
```bash
bun create rsbuild@latest --dir test-app --template react-ts
cd test-app
bun install
```
{/* */}
### Install Panda
Install panda and create your `panda.config.ts` file.
{/* */}
```bash
pnpm install -D @pandacss/dev
pnpm panda init --postcss
```
```bash
npm install -D @pandacss/dev
npx panda init --postcss
```
```bash
yarn add -D @pandacss/dev
yarn panda init --postcss
```
```bash
bun add -D @pandacss/dev
bun panda init --postcss
```
{/* */}
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {5} filename="package.json"
{
"scripts": {
"build": "rsbuild build",
"dev": "rsbuild dev --open",
+ "prepare": "panda codegen",
"preview": "rsbuild preview"
}
}
```
- `"prepare"` - script that will run Panda CSS CLI codegen before each build. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the content
Make sure that all of the paths of your React components are included in the `include` section of the `panda.config.ts`
file.
```js {8} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ['./src/**/*.{js,jsx,ts,tsx}', './pages/**/*.{js,jsx,ts,tsx}'],
// Files to exclude
exclude: [],
// Generates JSX utilities with options of React, Preact, Qwik, Solid, Vue
jsxFramework: 'react',
// The output directory for your css system
outdir: 'styled-system'
})
```
### Configure the entry CSS with layers
Add this code to an `src/App.css` file imported in the root component of your project.
```css filename="src/App.css"
@layer reset, base, tokens, recipes, utilities;
```
## Start your build process
Run the following command to start your development server.
{/* */}
```bash
pnpm dev
```
```bash
npm run dev
```
```bash
yarn dev
```
```bash
bun dev
```
{/* */}
### Start using Panda
Now you can start using Panda CSS in your project. Here is the snippet of code that you can use in your `src/App.tsx`
file.
```tsx filename="src/App.tsx"
import { css } from '../styled-system/css'
function App() {
return
Hello πΌ!
}
export default App
```
## Troubleshooting
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
`tsconfig.json` file:
```json filename="tsconfig.json"
{
// ...
"include": ["src", "styled-system"]
}
```
---
## Slot Recipes
Learn how to style multiple parts components with slot recipes.
When using `cva` or `defineRecipe` might be enough for simple cases, slot recipes are a better fit for more complex
cases.
A slot recipe consists of these properties:
- `slots`: An array of component parts to style
- `base`: The base styles per slot
- `variants`: The different visual styles for each slot
- `defaultVariants`: The default variant for the component
- `compoundVariants`: The compound variant combination and style overrides for each slot.
> **Credit:** This API was inspired by multipart components in
> [Chakra UI](https://chakra-ui.com/docs/styled-system/component-style) and slot variants in
> [Tailwind Variants](https://tailwind-variants.org)
[See the comparison table between atomic recipes (`cva`) and `config recipes` here.](/docs/styling/recipes#should-i-use-atomic-or-config-recipes-)
The same comparison applies to `sva` and `slot recipes`.
## Atomic Slot Recipe (or sva)
The `sva` function is a shorthand for creating a slot recipe with atomic variants. It takes the same arguments as `cva`
but returns a slot recipe instead.
### Defining the Recipe
```jsx filename="checkbox.recipe.ts"
import { sva } from '../styled-system/css'
const checkbox = sva({
slots: ['root', 'control', 'label'],
base: {
root: { display: 'flex', alignItems: 'center', gap: '2' },
control: { borderWidth: '1px', borderRadius: 'sm' },
label: { marginStart: '2' }
},
variants: {
size: {
sm: {
control: { width: '8', height: '8' },
label: { fontSize: 'sm' }
},
md: {
control: { width: '10', height: '10' },
label: { fontSize: 'md' }
}
}
},
defaultVariants: {
size: 'sm'
}
})
```
### Using the recipe
The returned value from `sva` is a function that can be used to apply the recipe for each component part. Here's an
example of how to use the `checkbox` recipe:
```jsx filename="Checkbox.tsx"
import { css } from '../styled-system/css'
import { checkbox } from './checkbox.recipe'
const Checkbox = () => {
const classes = checkbox({ size: 'sm' })
return (
)
}
```
When a slot recipe is created, Panda will pre-generate the css of all the possible combinations of variants and compound
variants as atomic classes.
```css
@layer utilities {
.border_width_1px {
border-width: 1px;
}
.rounded_sm {
border-radius: var(--radii-sm);
}
.margin_start_2 {
margin-inline-start: var(--spacing-2);
}
.w_8 {
width: var(--sizing-8);
}
.h_8 {
height: var(--sizing-8);
}
.font_size_sm {
font-size: var(--fontSizes-sm);
}
.w_10 {
width: var(--sizing-10);
}
.h_10 {
height: var(--sizing-10);
}
.font_size_md {
font-size: var(--fontSizes-md);
}
/* ... */
}
```
### Compound Variants
Apply styles when **multiple** variant props match. `css` is keyed by slot:
```jsx filename="checkbox.recipe.ts"
import { sva } from '../styled-system/css'
const checkbox = sva({
slots: ['root', 'control', 'label'],
base: {
root: { display: 'flex', alignItems: 'center', gap: '2' },
control: { borderWidth: '1px', borderRadius: 'sm' },
label: { marginStart: '2' }
},
variants: {
size: {
sm: {
control: { width: '8', height: '8' },
label: { fontSize: 'sm' }
},
md: {
control: { width: '10', height: '10' },
label: { fontSize: 'md' }
}
},
isChecked: {
true: { control: { borderColor: 'gray.300' } },
false: { control: { borderColor: 'gray.200' } }
}
},
compoundVariants: [
{
size: 'sm',
isChecked: true,
css: {
control: { borderColor: 'green.500' }
}
}
],
defaultVariants: {
size: 'sm',
isChecked: false
}
})
```
```tsx
const classes = checkbox({ size: 'sm', isChecked: true })
// classes.control includes compound styles as atomic utilities
```
For `defineSlotRecipe`, see [using compound variants](#using-compound-variants) under Config Slot Recipe.
### Targeting slots
You can set an optional `className` property in the `sva` config which can be used to target slots in the DOM.
> Each slot will contain a `${className}__${slotName}` class in addition to the atomic styles.
Let's say you want to apply a different border color to the button text directly from the `root` slot. Here's how you
would do it:
```tsx
import { sva } from '../styled-system/css'
const button = sva({
className: 'btn',
slots: ['root', 'text'],
base: {
root: {
bg: 'blue.500',
_hover: {
// v--- π― this will target the `text` slot
'& .btn__text': {
color: 'white'
}
}
}
}
})
```
> Note: This doesn't work when you have the `hash: true` option in your panda config. We recommend using `data-x`
> selectors to target slots.
### TypeScript Guide
Panda provides a `RecipeVariantProps` type utility that can be used to infer the variant properties of a slot recipe.
This is useful when you want to use the recipe in JSX and want to get type safety for the variants.
```tsx
import { sva, type RecipeVariantProps } from '../styled-system/css'
const checkbox = sva({...})
export type CheckboxVariants = RecipeVariantProps
// => { size?: 'sm' | 'md', isChecked?: boolean }
```
### Usage in JSX
Unlike the atomic recipe or `cva`, slot recipes are not meant to be used directly in the `styled` factory since it
returns an object of classes instead of a single class.
```jsx
import { css } from '../styled-system/css'
import { styled } from '../styled-system/jsx'
import { checkbox, type CheckboxVariants } from './checkbox.recipe'
// β Won't work
const Checkbox = styled('label', checkbox)
// β Works
const Checkbox = (props: CheckboxVariants) => {
const classes = checkbox(props)
return (
)
}
```
### Styling JSX Compound Components
Compound components are a great way to create reusable components for better composition. Slot recipes play nicely with
this pattern through a context provider for the component.
> **Note:** This is an advanced topic and you don't need to understand it to use slot recipes. If you use React, be
> aware that context requires adding 'use client' to the top of the file.
Let's say you want to design a Checkbox component that can be used like this:
```jsx
Checkbox Label
```
Panda ships `createSlotRecipeContext` in `styled-system/jsx`. Pass it your slot recipe and it returns `withProvider` and
`withContext` to wire each part to the right slot:
```jsx filename="Checkbox.tsx"
'use client'
import { createSlotRecipeContext } from '../styled-system/jsx'
import { checkbox } from './checkbox.recipe'
const { withProvider, withContext } = createSlotRecipeContext(checkbox)
// ππ» points to the root slot
const Root = withProvider('label', 'root')
// ππ» points to the control slot
const Control = withContext('div', 'control')
// ππ» points to the label slot
const Label = withContext('span', 'label')
const Checkbox = { Root, Control, Label }
```
For the full API β `withRootProvider`, forwarding props, and the `unstyled` prop β see
[JSX Style Context](/docs/styling/jsx-style-context).
## Config Slot Recipe
Config slot recipes are very similar atomic recipes except that they use well-defined classNames and store the styles in
the `recipes` cascade layer.
The config slot recipe takes the following additional properties:
- `className`: The name of the recipe. Used in the generated class name
- `jsx`: An array of JSX components that use the recipe. Defaults to the uppercase version of the recipe name
- `description`: An optional description of the recipe (used in the js-doc comments)
### Defining the recipe
To define a config slot recipe, import the `defineSlotRecipe` function
```jsx filename="checkbox.recipe.ts"
import { defineSlotRecipe } from '@pandacss/dev'
export const checkboxRecipe = defineSlotRecipe({
className: 'checkbox',
description: 'The styles for the Checkbox component',
slots: ['root', 'control', 'label'],
base: {
root: { display: 'flex', alignItems: 'center', gap: '2' },
control: { borderWidth: '1px', borderRadius: 'sm' },
label: { marginStart: '2' }
},
variants: {
size: {
sm: {
control: { width: '8', height: '8' },
label: { fontSize: 'sm' }
},
md: {
control: { width: '10', height: '10' },
label: { fontSize: 'md' }
}
}
},
defaultVariants: {
size: 'sm'
}
})
```
### Adding recipe to config
To add the recipe to the config, youβd need to add it to the `slotRecipes` property of the `theme`
```jsx filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
import { checkboxRecipe } from './checkbox.recipe'
export default defineConfig({
//...
jsxFramework: 'react',
theme: {
extend: {
slotRecipes: {
checkbox: checkboxRecipe
}
}
}
})
```
### Generate JS code
This generates a recipes folder the specified `outdir` which is `styled-system` by default. If Panda doesnβt
automatically generate your CSS file, you can run the `panda codegen` command.
You only need to import the recipes into the component files where you need to use them.
### Using the recipe
To use the recipe, you can import the recipe from the `/recipes` entrypoint and use it in your component. Panda
tracks the usage of the recipe and only generates CSS of the variants used in your application.
```js
import { css } from '../styled-system/css'
import { checkbox } from '../styled-system/recipes'
const Checkbox = () => {
const classes = checkbox({ size: 'sm' })
return (
)
}
```
The generated css is registered under the `recipe` [cascade layer](/docs/styling/cascade-layers.mdx) with the class
name that matches the recipe-slot-variant name pattern `__--`.
```css
@layer recipes {
@layer base {
.checkbox__root {
display: flex;
align-items: center;
gap: var(--space-2);
}
.checkbox__control {
border-width: var(--border-widths-1px);
border-radius: var(--radii-sm);
}
.checkbox__label {
margin-start: var(--space-2);
}
}
.checkbox__control--size-sm {
width: var(--space-8);
height: var(--space-8);
}
.checkbox__label--size-sm {
font-size: var(--font-sizes-sm);
}
.checkbox__control--size-md {
width: var(--space-10);
height: var(--space-10);
}
.checkbox__label--size-md {
font-size: var(--font-sizes-md);
}
}
```
### Dynamic variant props
Same rules as [config recipes](/docs/styling/recipes#dynamic-variant-props). The runtime function returns slot class
names; build time only sees static variant values.
```tsx filename="Media.tsx"
import { cx } from '../styled-system/css'
import { mediaGrid, type MediaGridVariant } from '../styled-system/recipes'
function Media({
items,
layout = 'one'
}: {
items: { id: string; src: string }[]
layout?: MediaGridVariant['template']
}) {
const classes = mediaGrid({ template: layout })
return (
{items.map((item) => (
))}
)
}
```
`layout` is dynamic: Panda only emits CSS for `defaultVariants` unless you pre-generate the rest.
When variants come from props, add `staticCss`:
```ts filename="media-grid.recipe.ts"
export const mediaGridRecipe = defineSlotRecipe({
className: 'media-grid',
slots: ['container', 'item'],
variants: {
template: {
one: { container: { gridTemplateColumns: '1fr' }, item: {} },
two: { container: { gridTemplateColumns: '1fr 1fr' }, item: {} }
}
},
defaultVariants: { template: 'one' },
staticCss: ['*']
})
```
Register as `theme.slotRecipes.mediaGrid`, then import `mediaGrid` and `MediaGridVariant` from `/recipes`.
### Using compound variants
Apply styles when **multiple** variant props match. Each entry's `css` is keyed by **slot** (not a flat style object):
```ts filename="tabs.recipe.ts"
import { defineSlotRecipe } from '@pandacss/dev'
export const tabsRecipe = defineSlotRecipe({
className: 'tabs',
slots: ['root', 'trigger'],
base: {
root: { display: 'flex', gap: '2' },
trigger: { px: '3', py: '2', borderRadius: 'md' }
},
variants: {
size: {
sm: { root: { gap: '1' } },
md: { root: { gap: '2' } }
},
active: {
true: { trigger: { fontWeight: 'semibold' } },
false: { trigger: { fontWeight: 'normal' } }
}
},
defaultVariants: {
size: 'md',
active: false
},
compoundVariants: [
{
size: 'sm',
active: true,
css: { trigger: { color: 'blue.500' } }
},
{
size: 'md',
active: true,
css: { trigger: { color: 'red.500' } }
}
]
})
```
```ts filename="panda.config.ts"
theme: {
extend: {
slotRecipes: {
tabs: tabsRecipe
}
}
}
```
```tsx
import { tabs } from '../styled-system/recipes'
const { root, trigger } = tabs({ size: 'sm', active: true })
// root β "tabs__root tabs__root--size_sm"
// trigger β "tabs__trigger tabs__trigger--active_true text_blue.500"
```
First extracted use atomizes every compound slot `css` into `@layer utilities`:
```css
@layer utilities {
.text_blue\.500 {
color: var(--colors-blue-500);
}
.text_red\.500 {
color: var(--colors-red-500);
}
}
```
Only slots listed in `css` are matched at runtime. Adding `compoundVariants` drops responsive variant props. Compare
**`accordion`** without compounds:
```ts
export type AccordionVariantProps = {
size?: ConditionalValue<'sm' | 'md'>
}
```
```tsx
const { root } = accordion({ size: { base: 'sm', md: 'lg' } }) // β
```
**`tabs`** above accepts plain literals only:
```ts
export type TabsVariantProps = {
size?: 'sm' | 'md'
}
```
```tsx
tabs({ size: { base: 'sm', md: 'lg' }, active: true })
// β [recipe:tabs__trigger:size] Conditions are not supported when using compound variants.
```
At runtime, matching combos merge via `getCompoundVariantCss` per slot:
```tsx
const { root, trigger } = tabs({ size: 'md', active: true })
// root β "tabs__root tabs__root--size_md"
// trigger β "tabs__trigger tabs__trigger--active_true text_red.500"
```
Runtime-only combos need [`staticCss`](/docs/design-systems/static#generating-recipes). Run `pnpm panda codegen` after editing
the recipe.
[`sva`](/docs/styling/slot-recipes#compound-variants), all compound combos ship as atomic classes when the recipe is
defined.
### TypeScript Guide
Every slot recipe ships a type interface for its accepted variants. You can import them from the `styled-system/recipes`
entrypoint.
For the checkbox recipe, we can import the `CheckboxVariants` type like so:
```ts
import React from 'react'
import type { CheckboxVariants } from '../styled-system/recipes'
type CheckboxProps = CheckboxVariants & {
children: React.ReactNode
value?: string
onChange?: (value: string) => void
}
```
### `defineParts`
It can be useful when you want to have the equivalent of a slot recipe without needing to split the class names bindings
and instead just having a className that handles children on 1 DOM element.
It pairs well with [ZagJs](https://zagjs.com/) and [Ark-UI](https://ark-ui.com/)
Let's refactor the previous example to use parts instead of slots:
```ts
import { defineParts, defineRecipe } from '@pandacss/dev'
const parts = defineParts({
root: { selector: '& [data-part="root"]' },
control: { selector: '& [data-part="control"]' },
label: { selector: '& [data-part="label"]' }
})
export const checkboxRecipe = defineRecipe({
className: 'checkbox',
description: 'A checkbox style',
base: parts({
root: { display: 'flex', alignItems: 'center', gap: '2' },
control: { borderWidth: '1px', borderRadius: 'sm' },
label: { marginStart: '2' }
}),
variants: {
size: {
sm: parts({
control: { width: '8', height: '8' },
label: { fontSize: 'sm' }
}),
md: parts({
control: { width: '10', height: '10' },
label: { fontSize: 'md' }
})
}
},
defaultVariants: {
size: 'sm'
}
})
```
---
## Using SolidJS
Easily use Panda with SolidJS with our dedicated integration.
This guide will show you how to set up Panda CSS in a Solid.js project using PostCSS.
## Start a new project
### Create Vite project
To get started, we will need to create a new SolidJS project using `solidjs/templates/ts` template.
{/* */}
```bash
pnpm dlx degit solidjs/templates/ts test-app
cd test-app
pnpm install
```
```bash
npx degit solidjs/templates/ts test-app
cd test-app
npm install
```
```bash
yarn dlx degit solidjs/templates/ts test-app
cd test-app
yarn
```
```bash
bunx degit solidjs/templates/ts test-app
cd test-app
bun install
```
{/* */}
### Install Panda
Install panda and create your `panda.config.ts` file.
{/* */}
```bash
pnpm install -D @pandacss/dev
pnpm panda init --postcss
```
```bash
npm install -D @pandacss/dev
npx panda init --postcss
```
```bash
yarn add -D @pandacss/dev
yarn panda init --postcss
```
```bash
bun add -D @pandacss/dev
bun panda init --postcss
```
{/* */}
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3} filename="package.json"
{
"scripts": {
+ "prepare": "panda codegen",
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
}
}
```
- `"prepare"` - script that will run Panda CSS CLI codegen before each build. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the content
Make sure that all of the paths of your SolidJS components are included in the `include` section of the
`panda.config.ts` file.
```ts {8,17} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ['./src/**/*.{js,jsx,ts,tsx}', './pages/**/*.{js,jsx,ts,tsx}'],
// Files to exclude
exclude: [],
// The output directory for your css system
outdir: 'styled-system'
})
```
### Configure the entry CSS with layers
Add this code to an `src/index.css` file imported in the root component of your project.
```css filename="src/index.css"
@layer reset, base, tokens, recipes, utilities;
```
> **Note:** Feel free to remove `src/App.module.css` file as we don't need it anymore, and make sure to remove the
> import from the `src/App.tsx` file.
## Start your build process
Run the following command to start your development server.
{/* */}
```bash
pnpm dev
```
```bash
npm run dev
```
```bash
yarn dev
```
```bash
bun dev
```
{/* */}
### Start using Panda
Now you can start using Panda CSS in your project. Here is the snippet of code that you can use in your `src/App.tsx`
file.
```tsx filename="src/App.tsx"
import type { Component } from 'solid-js'
import { css } from '../styled-system/css'
const App: Component = () => {
return
Hello πΌ!
}
export default App
```
## Troubleshooting
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
`tsconfig.json` file:
```json filename="tsconfig.json"
{
// ...
"include": ["src", "styled-system"]
}
```
---
## Migrating from Stitches
Migrate your project from Stitches to Panda.
This guide helps you migrate from Stitches to Panda and understand the design differences between the libraries.
> **Disclaimer:** This isn't about comparing which one is best. Panda and Stitches are two different CSS-in-JS solutions
> with design decisions.
Here are some similarities between the two libraries.
- Panda uses the object literal syntax to define styles. It also supports the shorthand syntax for the `margin` and
`padding` properties.
- Panda supports the `variants`, `defaultVariants` and `compoundVariants` APIs.
- Panda supports design tokens and themes.
- Panda 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.
```tsx
import { css } from '@stitches/react'
// definition
const styles = css({
border: 'solid 1px red',
backgroundColor: 'transparent',
variants: {
variant: {
// ...
}
}
})
// usage
```
In Panda, the `css` function is only used to author atomic styles, and the `cva` function to create variant style
objects.
**The css function**
```tsx
import { css } from '../styled-system/css'
// definition
const styles = css({
border: 'solid 1px red',
backgroundColor: 'transparent'
})
// usage
```
**The cva function**
```tsx
import { cva } from '../styled-system/css'
// definition
const styles = cva({
base: {
border: 'solid 1px red',
backgroundColor: 'transparent'
},
variants: {
variant: {
// ...
}
}
})
// usage
```
## styled function
In Stitches, the `styled` function can be used to create components that are bound to both regular and variant styles
objects.
```tsx
import { styled } from '@stitches/react'
const Button = styled('button', {
// base styles
backgroundColor: 'gainsboro',
borderRadius: '9999px',
variants: {
// variant styles
}
})
```
In Panda, the base styles object needs to added to the `base` key.
```tsx
import { styled } from '../styled-system/jsx'
const Button = styled('button', {
// base styles
base: {
backgroundColor: 'gainsboro',
borderRadius: '9999px'
},
variants: {
// variant styles
}
})
```
In Stitches, the styled function generates a unique className for each variant.
```tsx
import { styled } from '@stitches/react'
const Button = styled('button', {})
// =>
```
In Panda, you can decide if you want unique classNames for the recipe or you want atomic classNames.
- **Atomic classes** using the `cva` function or defining the recipe inline in the `styled` function
```tsx
import { styled } from '../styled-system/jsx'
const Button = styled('button', {
base: {
backgroundColor: 'gainsboro',
borderRadius: '9999px'
}
})
// =>
```
- **Selector classes** by defining the recipe in the `panda.config.ts` file. This approach only generates the classes
and css for the variants that are used in the project.
```ts
import { defineConfig, defineRecipe } from '@pandacss/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 `panda codegen --clean` to generate the recipe functions.
```tsx
import { styled } from '../styled-system/jsx'
import { buttonStyle } from '../styled-system/recipes'
// create a styled component using the recipe function
const Button = styled('button', buttonStyle)
// or you can use directly in the JSX
// =>
```
## Responsive Styles
In Stitches, you configure breakpoints in the `media` key of the `createStitches` method, and use it via the
`@` syntax.
```ts
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 Panda, you configure breakpoints in the `theme.breakpoints` key of the `panda.config` function
```ts
import { defineConfig } from '@pandacss/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 Panda, 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.
```ts
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 Panda, tokens are defined in the `theme` key of the `panda.config` function.
```ts
import { defineConfig } from '@pandacss/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 Panda, you don't need to use the `$` prefix to access the tokens. If you really want the `$` prefix,
name the token with it:
```diff
export default defineConfig({
theme: {
colors: {
- gray100: { value: 'hsl(206,22%,99%)' },
+ $gray100: { value: 'hsl(206,22%,99%)' },
},
}
})
```
### Themes
In Stitches, the `createTheme` function is used to define dark theme values.
```tsx
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
Content nested in dark theme.
```
In Panda, themes are designed as semantic tokens. You can define the semantic tokens in the `semanticTokens` key of the
`panda.config` function.
```ts
import { defineConfig } from '@pandacss/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
```ts
import { styled } from '@stitches/react'
const Button = styled('button', {
$$shadowColor: '$colors$pink500',
boxShadow: '0 0 0 15px $$shadowColor'
})
```
In Panda, 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
```ts
import { styled } from '../styled-system/jsx'
const Button = styled('button', {
base: {
'--shadowColor': 'colors.pink500',
boxShadow: '0 0 0 15px var(--shadowColor)'
}
})
```
## Animations
In Stitches, you can define keyframes using the `keyframes` method.
```ts
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 Panda, you define keyframes in the `theme.keyframes` key of the `panda.config` function.
```ts
import { defineConfig } from '@pandacss/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.
```ts
import { createStitches, type PropertyValue } from '@stitches/react'
const { styled, css } = createStitches({
utils: {
linearGradient: (value: PropertyValue<'backgroundImage'>) => ({
backgroundImage: `linear-gradient(${value})`
})
}
})
```
In Panda, 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 `panda.config` function.
The utilities API allows you define the connected token scale, generated className, and transform function.
```ts
import { defineConfig } from '@pandacss/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 `panda codegen` will update the typings for the utilities, allowing for a type-safe developer experience.
Then you can use the utility in your styles.
```tsx
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 `globalCss` function, and then call it in your app.
```tsx
import { globalCss } from '@stitches/react'
const globalStyles = globalCss({
'*': { margin: 0, padding: 0 }
})
// then in your app
globalStyles()
```
In Panda, you define the global styles in the `panda.config.ts` using the `globalCss` function.
> The styles be injected automatically under the `base` cascade layer via PostCSS
```ts {3-5}
import { defineConfig, defineGlobalStyles } from '@pandacss/dev'
const globalCss = defineGlobalStyles({
'*': { margin: 0, padding: 0 }
})
export default defineConfig({
// ...
globalCss
})
```
## Targeting Components
In Stitches, you can directly target React or styled components via the `toString()` method.
```tsx
import { css } from '@stitches/react'
const Icon = () => (
);
// add a `toString` method
Icon.toString = () => '.right-arrow';
const buttonClass = css({
[`& ${Icon}`]: {
marginLeft: '5px'
}
})
```
In Panda, you need to use the native selector directly. This is largely due to the static nature of Panda
```tsx
import { css } from '../styled-system/css'
const Icon = () => (
);
const buttonClass = css({
"& .right-arrow": {
marginLeft: '5px'
}
})
```
## Server Side Rendering
In Stitches, you need to configure the server-side rendering for your framework.
```tsx
// stitches.config.ts
import { createStitches } from '@stitches/react'
export const { getCssText } = createStitches()
// _document.tsx
export default class Document extends NextDocument {
render() {
return (
)
}
}
```
In Panda, you don't need to configure anything. Panda automatically extracts the styles and injects them at build time
using PostCSS.
## Conclusion
Before choosing your preferred CSS-in-JS library, be sure to consider your engineering and design goals. Both Stitches
and Panda are capable of achieving many of the same styling goals, but they have different approaches.
---
## Using Storybook
Easily use Panda with Storybook with our dedicated integration.
Learn how to set up Panda CSS in a Storybook project using PostCSS.
## Setup
We are assuming that you already have a project set up with a framework like React, Vue or Svelte.
### Install Storybook
Storybook needs to be installed into a project that is already set up with a framework. It will not work on an empty
project.
{/* */}
```bash
pnpm dlx storybook@latest init
```
```bash
npx storybook@latest init
```
```bash
yarn dlx storybook@latest init
```
```bash
bunx storybook@latest init
```
{/* */}
### Install Panda
Install panda and create your `panda.config.ts` file.
{/* */}
```bash
pnpm install -D @pandacss/dev
pnpm panda init --postcss
```
```bash
npm install -D @pandacss/dev
npx panda init --postcss
```
```bash
yarn add -D @pandacss/dev
yarn panda init --postcss
```
```bash
bun add -D @pandacss/dev
bun panda init --postcss
```
{/* */}
If you are using Storybook with the Vite builder, you will have to update your PostCSS config file to use the array
syntax for the plugins instead of the object syntax. Simply change `postcss.config.cjs`:
```diff filename="postcss.config.cjs"
module.exports = {
- plugins: {
- '@pandacss/dev/postcss': {}
- }
+ plugins: [require('@pandacss/dev/postcss')()]
}
```
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3} filename="web/package.json"
{
"scripts": {
+ "prepare": "panda codegen"
}
}
```
- `"prepare"` - script that will run Panda CSS CLI codegen before each build. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the content
Make sure that all of the paths of your Storybook components are included in the `include` section of the
`panda.config.ts` file.
```ts {7} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ['./src/**/*.{js,jsx,ts,tsx}', './pages/**/*.{js,jsx,ts,tsx}', './stories/**/*.{js,jsx,ts,tsx}'],
// Files to exclude
exclude: [],
// The output directory for your css system
outdir: 'styled-system'
})
```
### Configure the entry CSS with layers
Locate your main CSS file and add the following layers:
```css filename="src/index.css"
@layer reset, base, tokens, recipes, utilities;
```
### Import the CSS in your Storybook config
Locate your `.storybook/preview.ts` file and import the CSS file.
In this example CSS file is located in the `src` folder.
```ts {1} filename=".storybook/preview.ts"
import '../src/index.css'
import type { Preview } from '@storybook/react'
const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/
}
}
}
}
export default preview
```
### Start the Storybook server
Run the following command to start your Storybook server.
{/* */}
```bash
pnpm storybook
```
```bash
npm run storybook
```
```bash
yarn storybook
```
```bash
bun storybook
```
{/* */}
### Start using Panda
Now you can start using Panda CSS in Storybook.
Here is the example of a Button component and its corresponding Storybook story:
```tsx filename="src/stories/Button.tsx"
import { ReactNode } from 'react'
import { css } from '../../styled-system/css'
export interface IButtonProps {
children: ReactNode
}
export const Button = ({ children }: IButtonProps) => {
return (
)
}
```
```tsx filename="src/stories/Button.stories.tsx"
import type { Meta, StoryObj } from '@storybook/react'
import { css } from '../../styled-system/css'
import { Button } from './Button'
const meta = {
title: 'Example/Button',
component: Button,
tags: ['autodocs'],
decorators: [
Story => (
)
]
} satisfies Meta
export default meta
type Story = StoryObj
export const Default: Story = {
args: {
children: 'Hello πΌ!'
}
}
```
## Configuring Dark Mode
To enable dark mode in Storybook, you can use the `@storybook/addon-themes` package.
```bash
pnpm add -D @storybook/addon-themes
```
Then, update your `.storybook/preview.ts` file to include the following:
```ts filename=".storybook/preview.ts"
import { withThemeByClassName } from '@storybook/addon-themes'
import type { Preview, ReactRenderer } from '@storybook/react'
const preview: Preview = {
// ...
decorators: [
withThemeByClassName({
themes: {
light: '',
dark: 'dark'
},
defaultTheme: 'light'
})
]
}
export default preview
```
With that in place, you should see the light/dark switcher in Storybook's toolbar.
## Troubleshooting
### Cannot find postcss plugin
If you are having issues with the PostCSS plugin similar to `Cannot find module '@pandacss/dev/postcss'`, update the
PostCSS config as follows:
```js filename="postcss.config.cjs"
module.exports = {
plugins: [require('@pandacss/dev/postcss')]
}
```
### HMR not triggered
If you are having issues with HMR not being triggered after a `panda.config.ts` change (or one of its
[dependencies](/docs/reference/config#dependencies), you can manually specify the files that should trigger a rebuild
by adding the following to your `panda.config.ts`:
```js filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// ...
dependencies: ['path/to/files/**.ts']
})
```
### Styles in `args` is not generated
If you are having issues with your `args` not generating the expected CSS, it's probably because of:
- you didn't add a file glob for the Storybook files in your [`config.include`](/docs/reference/config#include) like
`"./stories/**/*.{js,jsx,ts,tsx}"`
- you didn't wrap your `args` object (or some part of it) with the
[`.raw()` marker that helps Panda find style usage](https://panda-css.com/docs/styling/dynamic-styling#alternative)
```tsx filename="stories/Button.stories.tsx"
import type { Meta, StoryObj } from '@storybook/react'
import { button } from '../../styled-system/recipes'
export const Funky: Story = {
// mark this as a button recipe usage
args: button.raw({
visual: 'funky',
shape: 'circle',
size: 'sm'
})
}
```
### Some recipes styles are missing
If you are having issues with your config `recipes` or `slotRecipes` not generating the expected CSS, it's probably
because of:
- you didn't add a file glob for the Storybook files in your [`config.include`](/docs/reference/config#include) like
`"./stories/**/*.{js,jsx,ts,tsx}"`
- you haven't used every recipes variants in your app, so you might want to pre-generate it (only for storybook usage)
with [`staticCss`](/docs/design-systems/static)
```tsx filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// ...
staticCss: {
recipes: '*'
}
})
```
---
## Style props
Build UIs quickly by passing css properties as "props" to your components.
While you can get very far by using the `className` prop and function from Panda, style props provide a more ergonomic
way of expressing styles.
Panda will extract the style props through static analysis and generate the CSS at build time.
> If you use Chakra UI, Styled System, or Theme UI, you'll feel right at home right away π
```jsx
import { css } from '../styled-system/css'
import { styled } from '../styled-system/jsx'
// The className approach
const Button = ({ children }) => (
)
// The style props approach
const Button = ({ children }) => (
{children}
)
```
## Configure JSX
Using JSX style props is turned off by default in Panda, but you can opt-in to this feature by using the `jsxFramework`
property in the panda config.
> β οΈ Panda will not extract style props from JSX elements if you don't set the `jsxFramework` property. This is to avoid
> unnecessary work for projects that don't use JSX.
### Choose Framework
JSX is a JavaScript syntax extension that allows you to write HTML-like code directly within your JavaScript code and is
supported by most popular frameworks. Panda supports JSX style props in React, Preact, Vue 3, Qwik and Solid.js.
```js filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// ...
jsxFramework: 'react'
})
```
### Generate JSX runtime
Next, you need to run `panda codegen` to generate the JSX runtime for your framework.
{/* */}
```bash
pnpm panda codegen --clean
```
```bash
npm panda codegen --clean
```
```bash
yarn panda codegen --clean
```
```bash
bun panda codegen --clean
```
{/* */}
That's it! You can now use JSX style props in your components.
## Using Style Props
### JSX Element
Style props are just CSS properties that you can pass to your components as props. With the JSX runtime, you can use
`styled.` syntax to create supercharged JSX elements that support style props.
```jsx
import { styled } from '../styled-system/jsx'
const Button = ({ children }) => (
{children}
)
```
### Property Renaming
Due to the static nature of Panda, you can't rename properties at runtime.
```tsx filename="App.tsx"
import { Circle, CircleProps } from '../styled-system/jsx'
type Props = {
circleSize?: CircleProps['size']
}
const CustomCircle = (props: Props) => {
const { circleSize = '3' } = props
return (
)
}
// ...
const App = () => {
return (
// In this case, you should keep the `size` naming
)
}
```
The same principles apply to all style props, recipe variants, and pattern props.
If you still need to rename properties at runtime, you can use `config.staticCss` as an escape-hatch to pre-generate
the CSS anyway for the properties you need.
### Custom component props
Panda can't map custom prop names to CSS properties. There's no config that turns `elementWidth` into `width`. Styles
are extracted at the call site, on props the compiler recognizes: CSS utilities (`width`, `padding`, β¦) when
`jsxStyleProps` is `'all'`, the `css` prop, props ending in `Css` (`trackCss`, `wrapperCss`), and pattern or recipe
props on tracked components.
```tsx
// β
// β
```
One override β `css`. Named parts β `trackCss`, `slideCss`, etc. Other prop names β `css.raw()`. See
[passing styles to custom components](/docs/styling/merging-styles#passing-styles-to-custom-components).
### Recipe
You can use recipe variants as JSX props to quickly change the styles of your components, as long as
[you're tracking those components in your recipe config](/docs/styling/recipes#advanced-jsx-tracking).
```tsx
import { styled } from '../styled-system/jsx'
import { button, type ButtonVariantProps } from '../styled-system/recipes'
const Button = (props: ButtonVariantProps) =>
const App = () =>
```
## Factory Function
You can also use the `styled` function to create a styled component from any component or JSX intrinsic element (like
"a", "button").
```jsx
import { styled } from '../styled-system/jsx'
import { Button } from 'component-library'
const StyledButton = styled(Button)
const App = () => (
Button
)
```
> You can configure the `styled` function name using the [`config.jsxFactory`](/docs/reference/config#jsxfactory)
> option.
### Factory Recipe
You can define a recipe for your component using the `styled` function. This is useful when you want to create a
component that has a specific set of style props.
```jsx
import { styled } from '../styled-system/jsx'
const Button = styled('button', {
base: {
py: '2',
px: '4',
rounded: 'md'
},
variants: {
variant: {
primary: {
bg: 'blue.500',
color: 'white'
},
secondary: {
bg: 'gray.500',
color: 'white'
}
}
}
})
const App = () => (
)
```
### Factory Options
There's a few options you can pass to the `styled` function to customize the behavior of the generated component.
```ts
interface FactoryOptions {
dataAttr?: boolean
defaultProps?: TProps
shouldForwardProp?(prop: string, variantKeys: string[]): boolean
forwardProps?: string[]
}
```
#### `dataAttr`
Setting `dataAttr` to true will add a `data-recipe="{recipeName}"` attribute to the element with the recipe name. This
is useful for testing and debugging.
```jsx
import { styled } from '../styled-system/jsx'
import { button } from '../styled-system/recipes'
const Button = styled('button', button, { dataAttr: true })
const App = () =>
// =>
```
#### `defaultProps`
allows you to skip writing wrapper components just to set a few props. It also allows you to locall override the default
variants or base styles of a recipe.
```jsx
import { styled } from '../styled-system/jsx'
import { button } from '../styled-system/recipes'
const Button = styled('button', button, {
defaultProps: {
variant: 'secondary',
px: '10px'
}
})
const App = () =>
// =>
```
#### `shouldForwardProp`
Used to customize which props are forwarded to the underlying element. By default, all props except recipe variants and
style props are forwarded.
For example, you could use it to integrate with [Framer Motion](https://www.framer.com/motion/).
```jsx
import { styled } from '../styled-system/jsx'
import { button } from '../styled-system/recipes'
import { motion, isValidMotionProp } from 'framer-motion'
const StyledMotion = styled(
motion.div,
{},
{
shouldForwardProp: (prop, variantKeys) =>
isValidMotionProp(prop) || (!variantKeys.includes(prop) && !isCssProperty(prop))
}
)
```
#### `forwardProps`
A simpler version of `shouldForwardProp`. Pass the prop names you want sent to the underlying element, even when they'd
normally be treated as variants or style props.
```jsx
import { styled } from '../styled-system/jsx'
const Input = styled('input', {}, { forwardProps: ['size'] })
const App = () =>
// =>
```
> **Note:** A forwarded prop becomes a plain HTML prop, so it no longer feeds recipe styling. To keep a prop styling
> _and_ pass it to your component, see [forwarding props](/docs/styling/jsx-style-context#forwarding-props) in
> `createSlotRecipeContext`.
### Unstyled prop
All styled components accept an `unstyled` prop that allows you to disable the recipe styles. This is useful when you
want to use a component's structure but apply completely custom styling.
```jsx
import { styled } from '../styled-system/jsx'
import { button } from '../styled-system/recipes'
const Button = styled('button', button)
const App = () => (
<>
{/* With recipe styles */}
{/* Without recipe styles, but inline styles still work */}
>
)
```
### Reducing the allowed style props
You can reduce the allowed JSX properties on the factory using
[`config.jsxStyleProps`](/docs/reference/config#jsxstyleprops):
- When set to 'all', all style props are allowed.
- When set to 'minimal', only the `css` prop and props ending in `Css` are allowed as style objects.
- When set to 'none', no style props are allowed and therefore the `jsxFactory` will not be usable as a component:
- `` and `styled("div")` aren't valid
- but the recipe usage is still valid `styled("div", { base: { color: "red.300" }, variants: { ...} })`
> Removing style props (from `all` to either `minimal` or `none`) will reduce the size of the generated code due to not
> having to check which props are style props at runtime.
## JSX Patterns
Patterns are common layout patterns like `stack`, `grid`, `circle` that can be used to speed up your css. Think of them
as a way to avoid repetitive layout styles.
All the patterns provided by Panda are available as JSX components.
> Learn more about the [patterns](/docs/design-systems/patterns) we provide.
```jsx
import { Stack, Circle } from '../styled-system/jsx'
const App = () => (
4
)
```
## Making your own styled components
To make a custom JSX component that accepts style props, Use the `splitCssProps` function to split style props from
other component props.
> For this to work correctly, set the `jsxFramework` to the framework you're using in your panda config.
```tsx
import { splitCssProps } from '../styled-system/jsx'
import type { HTMLStyledProps } from '../styled-system/types'
export function Component(props: HTMLStyledProps<'div'>) {
const [cssProps, restProps] = splitCssProps(props)
const { css: cssProp, ...styleProps } = cssProps
const className = css({ display: 'flex', height: '20', width: '20' }, styleProps, cssProp)
return
}
// Usage
function App() {
return Click me
}
```
## TypeScript
Panda provides type definitions for all the style props that are supported by the JSX runtime.
You can use these types to get type safety in your components.
### Style Object
Use the `JsxStyleProps` to get the types of the style object that is compatible with JSX elements.
```tsx
import { styled } from '../styled-system/jsx'
import type { JsxStyleProps } from '../styled-system/types'
interface ButtonProps {
color?: JsxStyleProps['color']
}
const Button = (props: ButtonProps) => {
return
}
```
### Style Props
Use the `HTMLStyledProps` type to get the types of an element in addition to the style props.
```tsx {2}
import { styled } from '../styled-system/jsx'
import type { HTMLStyledProps } from '../styled-system/jsx'
type ButtonProps = HTMLStyledProps<'button'>
const Button = (props: ButtonProps) => {
return
}
```
### Variant Props
Use the `StyledVariantProps` type to extract the variants from a styled component.
```tsx {2}
import { styled } from '../styled-system/jsx'
import type { StyledVariantProps } from '../styled-system/jsx'
const Button = styled('button', {
base: { color: 'black' },
variants: {
state: {
error: { color: 'red' },
success: { color: 'green' }
}
}
})
type ButtonVariantProps = StyledVariantProps
// ^ { state?: 'error' | 'success' | undefined }
```
### Patterns
Every pattern provided by Panda has a corresponding type that you can use to get type safety in your components.
```tsx {2}
import { Stack } from '../styled-system/jsx'
import type { StackProps } from '../styled-system/jsx'
```
---
## Migrating from Styled Components
Migrate your project from Styled Components to Panda.
This guide outlines the steps needed to migrate your project from Styled Components to Panda and highlights key design
differences between the two libraries.
> **Disclaimer:** This isn't about comparing which one is best. Panda and Styled Components are two different CSS-in-JS
> solutions with design decisions.
Here are some similarities between the two libraries.
- Both libraries support the use of tagged template literals or object syntax to style components.
- Both libraries provide a way to define design tokens (variables) and use them in your styles.
- Both libraries require the use of `&` for nested selectors.
Below are some differences between the two libraries.
## Installation and Syntax
In styled-components, you can use both tagged template literals and object syntax to style components.
In Panda, you need to decide which syntax you want to use. Panda recommends using the object syntax, but provides a way
to opt-in to tagged template literals.
To initialize a project with the object syntax, run the following command.
```bash
panda init -p --jsx-framework react
```
To initialize a project with the tagged template literal syntax, run the following command.
```bash
panda init -p --syntax template-literal --jsx-framework react
```
Then you need to add the cascade layers to the global styles of your project.
```css
@layer reset, base, tokens, recipes, utilities;
```
## Tagged Template Syntax
In styled-components, the recommended way to style components is to use tagged template literals.
```jsx
import styled from 'styled-components'
const Button = styled.button`
background-color: #fff;
border: 1px solid #000;
color: #000;
padding: 0.5rem 1rem;
`
```
In Panda, you will use the autogenerate code in the `styled-system` directory at the root of your project.
> Remember to initialize your project with the `--syntax template-literal` flag or update the panda.config.ts file.
```jsx
import { styled } from '../styled-system/jsx'
const Button = styled.button`
background-color: #fff;
border: 1px solid #000;
color: #000;
padding: 0.5rem 1rem;
`
```
## Object Syntax
In styled-components, you can use the object syntax to style components.
```jsx
import styled from 'styled-components'
const Button = styled.button({
backgroundColor: '#fff',
border: '1px solid #000',
color: '#000',
padding: '0.5rem 1rem'
})
```
In Panda, you add the style object to the `base` key of the style object. The `styled` factory allows you define base
styles, variants and compound variants of your component.
```jsx
import { styled } from '../styled-system/jsx'
const Button = styled('button', {
base: {
backgroundColor: '#fff',
border: '1px solid #000',
color: '#000',
padding: '0.5rem 1rem'
}
})
```
## Prop Interpolation
In styled-components, you can interpolate the component's props to conditionally set styles.
```jsx
const Button = styled.button`
${props =>
props.color === 'violet' &&
`
background-color: 'blueviolet'
`}
${props =>
props.color === 'gray' &&
`
background-color: 'gainsboro'
`}
`
```
In Panda, we model interpolations using the variants API. This allows define style groups or recipes that can be applied
to components.
```jsx
const Button = styled('button', {
variants: {
color: {
violet: css`
background-color: blueviolet;
`,
gray: css`
background-color: gainsboro;
`
}
}
})
// Usage
```
## Tokens and Themes
### Defining Tokens
In styled-components, you can define tokens in a theme object that is passed to the `ThemeProvider`.
This requires the use of React's context API to access the theme object in your styles
```tsx
import { ThemeProvider } from 'styled-components'
const theme = {
colors: {
primary: 'blue',
secondary: 'red'
}
}
const App = () => (
)
````
In Panda, you define tokens in the `theme` key of the `panda.config.ts` file. This allows you to access the tokens in
your styles without the need for React's context API.
```tsx
// panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
tokens: {
colors: {
primary: { value: 'blue' },
secondary: { value: 'red' }
}
}
}
}
})
```
### Using Tokens
In styled-components, you can use tokens in your styles using a function approach that provides the `theme` prop, and
requires ambient type declarations to get type safety.
```tsx
import styled from 'styled-components'
// link.tsx
const StyledLink = styled.a(({ theme }) => ({
color: theme.colors.primary,
display: 'block',
textDecoration: 'none'
}))
// theme.d.ts
declare module 'styled-components' {
export interface DefaultTheme {
colors: {
primary: string
secondary: string
}
}
}
```
In Panda, the tokens are automatically available in your styles and connected to each css property, removing the need
for an interpolation function.
```tsx
// panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
// extend the base theme
extend: {
tokens: {
// add custom colors
colors: {
primary: { value: 'blue' },
secondary: { value: 'red' }
}
}
}
}
})
// link.tsx
import { styled } from '../styled-system/jsx'
const StyledLink = styled('a', {
base: {
color: 'primary',
display: 'block',
textDecoration: 'none'
}
})
```
## Responsive Styles
### Tagged Template Syntax
In styled-components, you need to write the media query styles manually or use a helper function like
`styled-media-query`.
```tsx
import styled from 'styled-components'
const Button = styled.button`
background-color: #fff;
border: 1px solid #000;
color: #000;
padding: 0.5rem 1rem;
@media (min-width: 768px) {
padding: 1rem 2rem;
}
`
```
In Panda, it's pretty much the same thing except that you can't do any interpolation in the media query styles due the
static nature of Panda.
```tsx
import { styled } from '../styled-system/jsx'
const Button = styled.button`
background-color: #fff;
border: 1px solid #000;
color: #000;
padding: 0.5rem 1rem;
@media (min-width: 768px) {
padding: 1rem 2rem;
}
`
```
### Object Syntax
In styled-components, you can use the `styled-media-query` helper function to write responsive styles.
```tsx
import styled from 'styled-components'
import media from 'styled-media-query'
const Button = styled.button({
backgroundColor: '#fff',
border: '1px solid #000',
color: '#000',
padding: '0.5rem 1rem',
[media.greaterThan('medium')]: {
padding: '1rem 2rem'
}
})
```
In Panda, you can use the pseudo props API to define responsive styles.
```tsx
import { styled } from '../styled-system/jsx'
const Button = styled('button', {
base: {
backgroundColor: '#fff',
border: '1px solid #000',
color: '#000',
padding: { base: '0.5rem 1rem', md: '1rem 2rem' }
}
})
```
## Global Styles
In styled-components, you can use the `createGlobalStyle` function to define global styles.
```tsx
import { createGlobalStyle } from 'styled-components'
const GlobalStyle = createGlobalStyle`
body {
margin: 0;
padding: 0;
}
`
```
In Panda, you can use the `globalCss` key of the `panda.config.ts` file to define global styles. This will automatically
add styles to the project via PostCSS.
```tsx
// panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
globalCss: {
body: {
margin: 0,
padding: 0
}
}
})
```
## Targeting Components
In styled-components, you can target existing styled components within the styled function
```tsx
import styled from 'styled-components'
const Link = styled.a`
background: papayawhip;
color: #bf4f74;
`
const Icon = styled.svg`
width: 48px;
height: 48px;
${Link}:hover & {
fill: rebeccapurple;
}
`
```
In Panda, you need to use the native selector directly. This is largely due to the static nature of Panda
```tsx
import { styled } from '../styled-system/jsx'
const Link = styled.a`
background: papayawhip;
color: #bf4f74;
`
const Icon = styled.svg`
width: 48px;
height: 48px;
.Link:hover & {
fill: rebeccapurple;
}
`
const App = () => (
)
```
## Animations
In styled components, you can define keyframes using the `keyframes` method.
```ts
import styled, { keyframes } from 'styled-components'
const rotate = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`
// usage
const Button = styled.button`
&:hover {
animation: ${rotate} 200ms;
}
`
```
In Panda, you define keyframes in the `theme.keyframes` key of the `panda.config` function.
```ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
keyframes: {
rotate: {
from: {
transform: 'rotate(0deg)'
},
to: {
transform: 'rotate(360deg)'
}
}
}
}
}
})
// usage
import { styled } from '../styled-system/jsx'
const Button = styled.button`
&:hover {
animation: rotate 200ms;
}
`
```
## Server-Side Rendering
In styled components, you need to configure the server-side rendering for your framework.
```tsx
import { renderToString } from 'react-dom/server'
import { ServerStyleSheet } from 'styled-components'
const sheet = new ServerStyleSheet()
try {
const html = renderToString(sheet.collectStyles())
const styleTags = sheet.getStyleTags() // or sheet.getStyleElement();
} catch (error) {
// handle error
console.error(error)
} finally {
sheet.seal()
}
```
In Panda, you don't need to configure anything. Panda automatically extracts the styles and injects them at build time
using PostCSS.
## Conclusion
Before choosing your preferred CSS-in-JS library, be sure to consider your engineering and design goals. Both Styled
components and Panda are capable of achieving many of the same styling goals, but they have different approaches.
---
## Styled System
What is the styled-system folder and how does it work?
While Panda generates your CSS at **build-time** using static extraction, we still need a lightweight runtime to
transform the CSS-in-JS syntax (either [`object`](/docs/styling/writing-styles#atomic-styles) or
[`template-literal`](/docs/styling/template-literals)) to class names strings. This is where the `styled-system` folder
comes in.
When running the `panda` or `panda codegen` commands, the [`config.outdir`](/docs/reference/config#outdir) will be used
as output path to generate the `styled-system` in.
This is the core of what the `styled-system` does:
```ts
css({ color: 'blue.300' }) // => "c_blue.300"
```
Since Panda doesn't rely on any bundler's (`vite`, `webpack`, etc) plugin, there is no code transformation happening to
convert the CSS-in-JS syntax to class names at compile-time. This is why we need a lightweight runtime to do that.
The same principles applies to `patterns`, `recipes` and even `jsx` components, as they all use the `css` function under
the hood.
If you look inside your `styled-system` folder, you should see the main entrypoints for the runtime:
Feel free to explore the files inside the `styled-system` folder to get a better understanding of how it works in
details!
> Note: The `styled-system` folder is not meant to be edited manually. It is generated by Panda and should be treated as
> a build artifact. This also means you don't need to commit it to your repository.
## How the folder is generated
Panda writes the `styled-system` folder from your resolved config β every token, pattern, recipe, and utility. The
runtime is tailored to your app: it only contains enough code (and types) to support the styles you use.
This is one step of the build. For which authoring API to reach for next, see
[Thinking in Panda](/docs/overview/thinking-in-panda).
## Pre-rendering
If you use some way to pre-render your components to static HTML, for example using Astro or RSC, the `styled-system`
functions like `css` and others will be removed at build-time and replaced by the generated class names, so that you
don't have to ship the runtime to your users.
---
## Migrating from StyleX
Migrate your project from Meta's StyleX to Panda and see how create, defineVars, and props map across.
This guide outlines the steps needed to migrate your project from StyleX to Panda and highlights key design
differences between the two.
> **Disclaimer:** This isn't about which one is best. Both compile to static CSS at build time, so the
> differences below are about strictness and scope, not runtime cost.
Here are some similarities between the two:
- Both extract to static CSS at build time, no runtime style computation in either.
- Both use object syntax for styles rather than tagged template strings.
- Both define theme values in a central place and reference them by name in style objects.
The foundation is the same, so the decision is about scope. StyleX stays deliberately minimal, single-element styles,
no globals, no built-in variants, which keeps specificity predictable but leaves variants and layout for you to build.
Panda ships that layer, recipes, patterns, and semantic tokens as typed APIs, on the same static-CSS output. Reach for
Panda when you want the design system in the box; stay on StyleX if strict per-element scoping is the point. The rest of
this guide maps `defineVars` / `create` / `props` to Panda's equivalents, and flags the constraints that don't carry
over.
## How styles compile
Both are already build-time, static systems, so unlike a migration from Emotion or Chakra, this isn't a
runtime-vs-static story. StyleX's atomic CSS model is deliberately strict about style precedence (the last property
wins, resolved partly through property order at the call site) and requires a compiler plugin (Babel, SWC, or the
Rust-based one) wired into your build. Panda extracts with its own Rust engine built on [Oxc](https://oxc.rs), native
for your build and WebAssembly in the browser. Structurally the same, static extraction plus atomic output, but it
doesn't require you to reason about call-site property order the way StyleX does.
## Theming
StyleX defines theme values with `stylex.defineVars`, which compiles to real CSS custom properties. The call has to
live in a file with a `.stylex.js`/`.stylex.ts` extension and be a named export, StyleX's compiler enforces this:
```ts filename="vars.stylex.ts"
import * as stylex from '@stylexjs/stylex'
export const colors = stylex.defineVars({
brand: '#0ea5e9'
})
```
```ts
const styles = stylex.create({
button: { backgroundColor: colors.brand }
})
```
Panda's theme tokens live in `panda.config.ts` instead of a separate `defineVars` call, and are referenced by name
as string values rather than imported variable objects:
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
tokens: {
colors: {
brand: { value: '#0ea5e9' }
}
}
}
}
})
```
```jsx
import { css } from '../styled-system/css'
```
See [Tokens](/docs/theming/tokens).
## The `sx` prop
StyleX has no inline-prop escape hatch, every element applies its styles explicitly through `stylex.props()`,
spread onto the element:
```jsx
import * as stylex from '@stylexjs/stylex'
const styles = stylex.create({
base: { color: 'white', backgroundColor: 'blue' }
})
```
Panda's `css()` is closer to this than a dedicated `sx` prop, you build the class name and pass it to `className`
directly, or use the `styled` factory's `css` prop for the same one-off styling on top of an existing component:
```jsx
import { css } from '../styled-system/css'
```
See [JSX Style Props](/docs/styling/style-props).
## Variants
StyleX has no built-in variant system, the common pattern is a `stylex.create` call with multiple named style
objects, and picking one at render time based on a prop:
```jsx
const styles = stylex.create({
primary: { backgroundColor: 'blue' },
secondary: { backgroundColor: 'gray' }
})
```
Panda's [recipes](/docs/styling/recipes) formalize this as a typed variant map, the prop and its allowed values are
checked at compile time instead of being an untyped string key lookup:
```ts filename="button.ts"
import { cva } from '../styled-system/css'
export const button = cva({
variants: {
variant: {
primary: { bg: 'blue.500' },
secondary: { bg: 'gray.500' }
}
}
})
```
```jsx
```
See [Recipes](/docs/styling/recipes) and [Slot Recipes](/docs/styling/slot-recipes) for multi-part components.
## Color Modes
StyleX handles color mode inside `defineVars` itself, keying a value to a media query:
```ts
const DARK = '@media (prefers-color-scheme: dark)'
export const colors = stylex.defineVars({
bg: { default: 'white', [DARK]: 'black' }
})
```
Panda's semantic tokens are the direct equivalent, a token whose value branches on the `_dark` condition instead of
an inline media-query key:
```ts filename="panda.config.ts"
theme: {
extend: {
semanticTokens: {
colors: {
bg: { value: { base: 'white', _dark: 'black' } }
}
}
}
}
```
```jsx
```
See [Theme](/docs/theming/theme) and [Multiple Themes](/docs/theming/multiple-themes) for color-mode setup beyond
media-query-only switching (a manually toggled class or attribute, for example).
## Global Styles
This is the one place StyleX and Panda genuinely disagree, not just in syntax. StyleX deliberately has no global
styles and no descendant or nested selectors targeting other elements, styles on an element have to come from class
names on that element itself. The only exception is ordinary CSS inheritance (a `color` cascading to children the
normal CSS way), and StyleX offers a separate `stylex.when` API for the controlled cases where you do need one
element's state to affect another, rather than reaching for an implicit descendant selector. This is a core design
constraint, not a missing feature, it's what lets StyleX guarantee predictable specificity at scale.
Panda does support global styles, through `globalCss` in `panda.config.ts`:
```ts filename="panda.config.ts"
export default defineConfig({
globalCss: {
body: { margin: 0 }
}
})
```
See [Global Styles](/docs/styling/global-styles). If you were relying on StyleX's strict per-element scoping as a
guardrail against accidental global overrides, note that Panda doesn't enforce that for you, it's on your team to
avoid reaching for `globalCss` or descendant selectors where a scoped style would do.
## Component Styles
StyleX has no built-in layout primitives (no `Box`, `Stack`, `Grid`), consistent with its low-level, single-element
scope. Panda ships [patterns](/docs/styling/patterns) for common layout shapes, as both a function and a JSX
component:
```jsx
import { Box, Stack } from '../styled-system/jsx'
Item
```
## Conclusion
StyleX and Panda share the same foundation, static extraction, atomic output, no runtime cost, but StyleX adds real
constraints (no global styles, no descendant selectors, strict call-site property ordering) that Panda doesn't
enforce. Moving from StyleX to Panda mostly means translating `defineVars`/`create`/`props` to Panda's config and
`css()`/`cva()` equivalents, and deciding deliberately, since Panda won't enforce it for you, whether to keep
StyleX's scoping discipline in your own styles.
## See also
- [Migration strategy](/docs/styling/migration-strategy) for running both side by side during the migration.
- [Cascade Layers](/docs/styling/cascade-layers) for how Panda manages selector precedence, the nearest equivalent
to StyleX's specificity guarantees.
---
## Using Svelte
Easily use Panda with Svelte with our dedicated integration.
This guide will show you how to set up Panda CSS in a Svelte project using PostCSS.
## Start a new project
### Create Svelte project
To get started, we will need to create a new Svelte project.
{/* */}
```bash
pnpm create svelte@latest test-app
```
```bash
npm create svelte@latest test-app
```
```bash
yarn create svelte@latest test-app
```
```bash
bun create svelte@latest test-app
```
{/* */}
You will be asked a few questions, answer them as follows:
```sh
β Welcome to SvelteKit!
β
β Which Svelte app template?
β Skeleton project
β
β Add type checking with TypeScript?
β Yes, using TypeScript syntax
β
β Select additional options (use arrow keys/space bar)
β βΌ Add ESLint for code linting
β βΌ Add Prettier for code formatting
β β» Add Playwright for browser testing
β β» Add Vitest for unit testing
β
β Your project is ready!
```
Enter the newly created directory and install the dependencies.
{/* */}
```bash
cd test-app
pnpm install
```
```bash
cd test-app
npm install
```
```bash
cd test-app
yarn
```
```bash
cd test-app
bun install
```
{/* */}
### Install Panda
To install Panda and corresponding dependencies run the following commands:
{/* */}
```bash
pnpm install -D @pandacss/dev
pnpm panda init --postcss
```
```bash
npm install -D @pandacss/dev
npx panda init --postcss
```
```bash
yarn add -D @pandacss/dev
yarn panda init --postcss
```
```bash
bun add -D @pandacss/dev
bun panda init --postcss
```
{/* */}
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```json {3}
{
"scripts": {
"prepare": "panda codegen",
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --plugin-search-dir . --check . && eslint .",
"format": "prettier --plugin-search-dir . --write ."
}
}
```
This `"prepare"` script will run Panda CSS CLI codegen before each build. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the content
Make sure that all of the paths of your Svelte components are included in the `include` section of the `panda.config.ts`
file.
```js {8} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ['./src/**/*.{js,ts,svelte}'],
// Files to exclude
exclude: [],
// The output directory for your css system
outdir: 'styled-system'
})
```
### Update Svelte config
To configure Svelte preprocess to use PostCSS and add Panda alias update the `svelte.config.js` file as follows:
```js {15} filename="svelte.config.js"
import adapter from '@sveltejs/adapter-auto'
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: [vitePreprocess()],
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter(),
alias: {
'styled-system': './styled-system/*'
}
}
}
export default config
```
### Update Vite config
To be able to import `styled-system` files in your Svelte components you will need to update the `vite.config.js` file
as follows:
```js {6-10} filename="vite.config.js"
import { sveltekit } from '@sveltejs/kit/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [sveltekit()],
server: {
fs: {
allow: ['styled-system']
}
}
})
```
If youβre using Storybook for a SvelteKit project, you need to replicate the same Vite server config changes. In your
.storybook folder, you likely have a `main.js` (or `vite.config.js` in older Storybook versions). Update it as follows:
```js filename="main.js"
import { defineConfig, mergeConfig } from 'vite'
/** @type { import('@storybook/sveltekit').StorybookConfig } */
const config = {
// other Storybook config...
viteFinal: async config => {
return mergeConfig(
config,
defineConfig({
server: {
fs: {
allow: ['styled-system']
}
}
})
)
}
}
export default config
```
### Configure the entry CSS with layers
Create the `app.css` file in the `src` directory and add the following content:
```css filename="src/app.css"
@layer reset, base, tokens, recipes, utilities;
```
### Import styles in the layout file
Create the `src/routes/+layout.svelte` file and add the following content:
```svelte {2} filename="src/routes/+layout.svelte"
```
### Start your build process
Run the following command to start your development server.
{/* */}
```bash
pnpm dev
```
```bash
npm run dev
```
```bash
yarn dev
```
```bash
bun dev
```
{/* */}
### Start using Panda
Now you can start using Panda CSS in your project. Here is the snippet of code that you can use in your
`src/routes/+page.svelte` file.
```svelte filename="src/routes/+page.svelte"
Hello πΌ!
```
## Troubleshooting
### Autocomplete not working
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
TypeScript config. However, in Svelte your main `tsconfig.json` file is extending the autogenerated one. To extend it
without overriding the defaults adjust your `svelte.config.js` to include following entry:
```js filename="svelte.config.js"
import adapter from '@sveltejs/adapter-auto'
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
/** @type {import('@sveltejs/kit').Config} */
const config = {
// ...
kit: {
// ...
typescript: {
config: config => {
config.include.push('../styled-system')
return config
}
}
}
}
export default config
```
---
## Migrating from Tailwind CSS
Migrate your project from Tailwind CSS to Panda and see how utility classes map to Panda's style objects.
This guide outlines the steps needed to migrate your project from Tailwind CSS to Panda and highlights key design
differences between the two.
> **Disclaimer:** This isn't about which one is best. Both are utility-driven and both extract to static CSS at
> build time, the difference is mostly in syntax and in what ships built in.
Here are some similarities between the two:
- Both are utility-first: most styling happens through short property/value pairs rather than hand-written CSS
classes or selectors.
- Both extract styles statically at build time by scanning your source files, there's no client-side style engine
in either.
- Both support responsive and state-based variants (hover, focus, dark mode) as a first-class part of the syntax.
- Both let you define your own design tokens (colors, spacing, fonts) in a central config.
Here's where they differ.
## Type safety
This is the sharpest difference between the two, and it isn't about runtime cost, both tools extract to static CSS at
build time. It's about what's checked before that extraction runs.
Tailwind styles are class-name strings. Your editor can't check them, so a misspelled or unknown class is a silent
no-op, the style just doesn't apply and nothing warns you. As a project grows, that's how you get class-name soup: long
`class="..."` strings no one can safely refactor, variants copy-pasted across files, and dead classes that never did
anything.
Panda styles are objects. Properties are typed keys and values draw from your typed tokens, so a mistyped property is a
TypeScript error at the call site, before you run a build. Autocomplete offers your real tokens and recipe variants as
you type.
```jsx
import { css } from '../styled-system/css'
// β Tailwind: a mistyped class is a silent no-op
;
// β Panda: a mistyped property won't type-check
; // 'aligns' does not exist
```
Turn on [`strictTokens`](/docs/styling/writing-styles) to reject raw values too, so a color has to be a token, not a
one-off like `#f00`.
## Theming
Tailwind v4 is CSS-first: there's no `tailwind.config.js` by default, tokens are defined directly in CSS with
`@theme`:
```css filename="app.css"
@import 'tailwindcss';
@theme {
--color-brand: #0ea5e9;
}
```
If you're still on Tailwind v3, the equivalent lives in `tailwind.config.js`'s `theme` key instead. Either way,
Panda's tokens live in `panda.config.ts`'s `theme` key, the same idea, named token categories, but Panda's theme also
generates typed token names you get autocomplete for in `css()` calls:
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
tokens: {
colors: {
brand: { value: '#0ea5e9' }
}
}
}
}
})
```
See [Tokens](/docs/theming/tokens) for the full token model, including semantic tokens, tokens whose value changes
per theme or color mode, which Tailwind handles by hand through CSS variables rather than as a first-class config
concept.
## The `sx` prop, and arbitrary values
Tailwind has no dedicated escape-hatch prop, since every style is already inline as a class name. Its nearest
equivalent is bracket syntax, for a one-off value outside the configured scale:
```html
```
Panda doesn't need an escape hatch for this. `css()` is a plain object, not a class name Tailwind has to parse and
generate a matching rule for, so any valid CSS value works directly, no bracket syntax:
```jsx
import { css } from '../styled-system/css'
```
See [JSX Style Props](/docs/styling/style-props) for the other ways Panda lets you style a component inline.
## Variants
Tailwind doesn't ship a built-in way to define a component's variants (a button's `size`/`intent` combinations)
itself, that typically means composing class strings by hand, or reaching for a separate package like
`tailwind-variants`. Panda has this built in as [recipes](/docs/styling/recipes):
```ts filename="button.ts"
import { cva } from '../styled-system/css'
export const button = cva({
base: { borderRadius: 'md', fontWeight: 'semibold' },
variants: {
size: {
sm: { fontSize: 'sm', px: '3', py: '1.5' },
lg: { fontSize: 'lg', px: '6', py: '3' }
}
}
})
```
```jsx
```
The variant combinations are typed, so `button({ size: 'xl' })` is a type error if `xl` was never defined. See
[Recipes](/docs/styling/recipes) and [Slot Recipes](/docs/styling/slot-recipes) for multi-part components.
## Color Modes
Tailwind's dark mode is a `dark:` variant prefix. In v4 it follows the OS's `prefers-color-scheme` automatically,
with no config, switch to manual class-based toggling with `@custom-variant` in CSS if you need it:
```css filename="app.css"
@import 'tailwindcss';
@custom-variant dark (&:where(.dark, .dark *));
```
```html
```
(On Tailwind v3, this was the `darkMode: 'class' | 'media'` key in `tailwind.config.js` instead.)
Panda's equivalent is the `_dark` condition, paired with a semantic token so the light/dark pair is defined once and
every user of that token gets the right value automatically:
```ts filename="panda.config.ts"
theme: {
extend: {
semanticTokens: {
colors: {
bg: { value: { base: 'white', _dark: 'gray.900' } }
}
}
}
}
```
```jsx
```
See [Theme](/docs/theming/theme) and [Multiple Themes](/docs/theming/multiple-themes).
## Global Styles
Tailwind expects global, element-level styles written as real CSS, typically under `@layer base` in your main
stylesheet:
```css
@layer base {
button {
margin: 0;
border: 0;
}
}
```
Panda lets you declare the same thing as a config object, `globalCss` in `panda.config.ts`, no separate CSS file
needed:
```ts filename="panda.config.ts"
export default defineConfig({
globalCss: {
button: { margin: 0, border: 0 }
}
})
```
See [Global Styles](/docs/styling/global-styles).
## Component Styles
Tailwind has no built-in layout components, it's class-name-only, layout primitives like a `Box` or `Stack` are
something you'd build yourself or pull from a separate component library (Headless UI, shadcn/ui). Panda ships
[patterns](/docs/styling/patterns) for exactly this, as both a function and a JSX component, out of the box:
```jsx
import { Box, Grid } from '../styled-system/jsx'
Box
```
Tailwind's `@apply` directive (composing utility classes inside a hand-written CSS rule) also has no Panda
equivalent, since Panda doesn't generate named utility classes for you to reference from separate CSS, use a
[recipe](/docs/styling/recipes) or the [`cx`](/docs/styling/merging-styles) helper for the same composition instead.
Tailwind plugins, loaded with `@plugin` in v4 (`@tailwindcss/typography`, `@tailwindcss/forms`) don't carry over
either, check [Presets](/docs/design-systems/presets) for the equivalent idea, a shareable package of tokens,
recipes, and patterns.
## Conclusion
Both tools solve the same problem, utility-first styling with a static, build-time extraction step. Panda's
differences are mostly about typing that utility layer (typed tokens, typed recipe variants, typed semantic tokens)
rather than a different mental model, which is why most of the migration above is closer to a syntax translation
than a redesign.
## See also
- [Migration strategy](/docs/styling/migration-strategy) for running both tools side by side during the migration.
- [Writing Styles](/docs/styling/writing-styles) and [Recipes](/docs/styling/recipes) for the two APIs this guide
leans on most.
---
## Template Literals
Panda allows you to write styles using template literals.
Writing styles using template literals provides a similar experience to
[styled-components](https://styled-components.com/) and [emotion](https://emotion.sh/), except that Panda generates
atomic class names instead of a single unique class name.
> Emitting atomic class names allows Panda to generate smaller CSS bundles.
Panda provides two functions to write template literal styles: `css` and `styled`.
## Getting started
To use template literals, you need to set the `syntax` option in your `panda.config.ts` file to `templateLiteral`:
```ts
// panda.config.ts
export default defineConfig({
// ...
syntax: 'template-literal', // required
jsxFramework: 'react' // required for JSX utilities, e.g. `styled`
})
```
Then run the codegen command to generate the functions:
```sh
panda codegen --clean
```
## The `css` function
This the basic way of writing template styles. It converts the template literal into a set of atomic class name which
you can pass to the `className` prop of an element.
```js
import { css } from '../styled-system/css'
const className = css`
font-size: 16px;
font-weight: bold;
`
function Heading() {
return
This is a title
}
// =>
```
Here's what the emitted atomic CSS looks like:
```css
.font-size_16px {
font-size: 16px;
}
.font-weight_bold {
font-weight: bold;
}
```
## The `styled` tag
The `styled` tag allows you to create a component with encapsulated styles. It's similar to the `styled-components` or
`emotion` library.
```js
import { styled } from '../styled-system/jsx'
// Create a styled component
const Heading = styled.h1`
font-size: 16px;
font-weight: bold;
`
function Demo() {
// Use the styled component
return This is a title
}
// =>
This is a title
```
Here's what the emitted atomic CSS looks like:
```css
.font-size_16px {
font-size: 16px;
}
.font-weight_bold {
font-weight: bold;
}
```
## Nested styles
You can nest selectors, pseudo-elements and pseudo-selectors.
```js
const Button = styled.button`
color: black;
&:hover {
color: blue;
}
`
```
Using css nesting syntax, pseudo-elements, pseudo-selectors and combinators are also supported:
```js
const Demo = styled.div`
color: black;
&::after {
content: 'πΌ';
}
& + & {
background: yellow;
}
&.bordered {
border: 1px solid black;
}
.parent & {
color: red;
}
`
```
Nested media and container queries are also supported:
```js
const Demo = styled.div`
color: black;
@media (min-width: 200px) {
color: blue;
}
@container (min-width: 200px) {
color: red;
}
`
```
## Hashing class names
In some cases, it might be useful to shorten the class names by hashing them. Set the `hash: true` option in your
`panda.config.ts` file to enable this. This will generate shorter class names but will make it harder to debug.
To achieve this, set the `hash` option in your `panda.config.ts` file to `true`:
```ts
// panda.config.ts
export default defineConfig({
// ...
hash: true // optional
})
```
> Run the `codegen` command to regenerate the functions with hashing enabled.
When hashing is enabled, the class names will go from this:
```css
.font-size_16px {
font-size: 16px;
}
.font-weight_bold {
font-weight: bold;
}
```
To a unique six character hash regardless of the length of the selector or the number of declarations:
```css
.adfg5r {
font-size: 16px;
}
.bsdf35 {
font-weight: bold;
}
```
## Using tokens
Use the `token()` function or `{}` syntax in your template literals to reference design tokens in your styles. Panda
will automatically generate the corresponding CSS variables.
```js
import { css } from '../styled-system/css'
const className = css`
font-size: {fontSizes.md};
font-weight: token(fontWeights.bold, 700);
`
```
## Caveats
The object literal syntax is the recommended way of writing styles. But, if you stick with the template literal syntax,
there are some caveats to be aware of:
- Patterns and recipes are not generated
- Dynamic interpolation or component targeting is not supported
- Lack of autocompletion for tokens within the template literal Our
[Eslint plugin](https://github.com/chakra-ui/eslint-plugin-panda/blob/main/docs/rules/no-invalid-token-paths.md) can
help you overcome this by detecting invalid tokens
- JSX Style props are not supported
---
## Migrating from Theme UI
Migrate your project from Theme UI to Panda.
This guide outlines the steps needed to migrate your project from Theme UI to Panda and highlights key design
differences between the two libraries.
Here are some similarities between the two libraries.
- Panda and Theme UI both support JSX style props.
- Supports design tokens and themes.
- Support for styling primitives like `Box`, `Flex`, `Grid`, etc.
Below are some of the differences between the two libraries.
## 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.
Panda relies on `postcss` and converts CSS-in-JS styles to static CSS at build-time, leading to better performance in
larger applications.
## Theming
In Theme UI, you need to wrap your application in a `ThemeProvider` component which is a wrapper around `@emotion/react`
theme context.
```jsx
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 (
)
}
```
In Panda, you don't need to wrap your application in a `ThemeProvider` component. Instead, you can pass the theme object
to the `panda.config.js` file.
The theme object in Panda is broken down into multiple parts, `tokens` and `semanticTokens`. The theme specification
also required passing the tokens as `{ value: XX }`
```js
// panda.config.js
import { defineConfig } from '@pandacss/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.
```jsx
/** @jsxImportSource theme-ui */
export const Demo = props => (
)
```
Panda offers three similar ways to style components. The first approach is to use the `styled` element syntax and rename
`sx` to `css`
```jsx
import { styled } from 'styled-system/jsx'
export const Demo = props => (
)
```
The second approach is to create styled components using the `styled` function. This approach allows you to create style
variants.
```jsx
import { styled } from 'styled-system/jsx'
export const Demo = styled('div', {
base: {
color: 'white',
bg: 'primary',
fontSize: 4
}
})
```
The simplest approach is to use the `css` function to write one-off styles.
```jsx
import { css } from 'styled-system/css'
export const Demo = props => (
)
```
## 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.
- `Grid` maps to `theme.grids`
- `Button`, `IconButton` maps to `theme.buttons`
- `NavLink`, `Link` maps to `theme.links`
- `Input`, `Select`, `Textarea` maps to `theme.forms`
- `Heading`, `Text` maps to `theme.text`
```js
// 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
```
In Panda, multi-variant styles are referred to as recipes. Recipes are a collection of styles that can be applied to any
component.
There are two ways to define recipes in Panda. The first approach is to use the `cva` function which produces atomic
classnames.
```js
import { cva } from 'styled-system/css'
const buttonStyles = cva({
base: {
display: 'inline-flex'
},
variants: {
variant: {
primary: {
color: 'white',
bg: 'primary'
},
secondary: {
color: 'white',
bg: 'secondary'
},
accent: {
color: 'white',
bg: 'accent'
}
}
}
})
const Demo = () => (
)
```
The second approach is to define the recipe in the `theme.recipes` property of the panda config. This is referred to as
'Config recipes' in Panda and allows for sharing recipes across components and projects.
```js
// panda.config.js
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
recipes: {
button: {
className: 'button',
base: { display: 'inline-flex' },
variants: {
variant: {
primary: { color: 'white', bg: 'primary' },
secondary: { color: 'white', bg: 'secondary' },
accent: { color: 'white', bg: 'accent' }
}
}
}
}
}
}
})
// Button.js
import { button } from 'styled-system/recipes'
const Demo = () =>
```
## 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.
```jsx
// theme.js
const theme = {
colors: {
primary: '#07c',
modes: {
dark: {
primary: '#0cf'
}
}
}
}
// Button.js
const Demo = () =>
```
In Panda, color modes related values are defined as `semanticTokens` in the theme. Semantic tokens are tokens that
change depending on the color mode.
```js
// panda.config.js
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
semanticTokens: {
colors: {
primary: { value: { base: '#07c', _dark: '#0cf' } }
}
}
}
}
})
// Button.js
import { css } from 'styled-system/css'
const Demo = () => (
)
```
## Global Styles
Theme UI offers a Global component (that wraps Emotionβs) for adding global CSS with theme-based values.
```jsx
import { Global } from 'theme-ui'
export default props => (
)
```
In Panda, global styles are defined in the `theme.global` property of the panda config.
```js
// panda.config.js
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
globalCss: {
button: {
m: 0,
bg: 'primary',
color: 'background',
border: 0
}
}
})
```
## Component Styles
Theme UI offers pre-defined layout components like `Box`, `Stack`, `Grid`, `Flex`
```jsx
import { Box, Grid } from 'theme-ui'
const Demo = () => (
BoxBoxBoxBox
)
```
In Panda, these are called "layout patterns", or "patterns" for short. Panda provides similar patterns that can be used
as a function or JSX element just like Theme UI.
```jsx
import { Box, Grid } from 'styled-system/jsx'
const Demo = () => (
BoxBox
)
```
The function approach can be handy as well
```jsx
import { css } from 'styled-system/css'
import { grid } from 'styled-system/patterns'
const Demo = () => (
Box
Box
)
```
---
## Thinking in Panda
Which Panda API to reach for next, from css() to recipes, slots, and config.
Panda has one mental model, and everything below is that model at different sizes. Four ideas carry it:
- **Styles live next to your markup.** You write them as objects in the same file as the component, not in a separate
stylesheet to keep in sync.
- **Tokens are the vocabulary.** Name intent once (`blue.500`, `md`, `semibold`) and every style refers to the same
names.
- **Atomic CSS that scales.** Each property and value becomes one shared class, so your CSS grows with the number of
distinct styles, not the number of components.
- **Zero runtime.** It all compiles to plain CSS at build time. Nothing computes styles in the browser.
The only question day to day is which API to reach for as a component grows. Start local. Promote only when the shape of
the styles forces it. This walk builds a Button, then a Card, and climbs the ladder: `css` β `cva` β `sva` β config.
Want the case for build-time styles first? Read [Welcome to Panda](/docs/styling/getting-started).
## Step 1: Start with `css()`
Say you need a button, one look, no variants yet. Use `css()` to put the styles right on the element:
```tsx
import { css } from '../styled-system/css'
export function Button({ children }: { children: React.ReactNode }) {
return (
)
}
```
A few habits show up here and stay true for every later step.
**Tokens name intent.** `blue.500`, `md`, `semibold` come from your theme. Panda resolves them to CSS variables.
**Conditions live in the object.** `_hover` and `md` are keys next to the rest of the styles. No separate media file.
**Keep styles static.** Panda reads your source at build time, so literals, local constants, and ternaries are all fine.
The moment a value only exists once the app is running, Panda can't see it. Map runtime choices to styles Panda can see:
```tsx
// β Panda can't see `shade` at build time
function Text({ shade }: { shade: number }) {
return
}
// β every class exists at build time
const byShade = {
300: css({ color: 'red.300' }),
500: css({ color: 'red.500' })
}
function Text({ shade }: { shade: keyof typeof byShade }) {
return
}
```
> `byShade` is really just a variant map without the name. Catch yourself hand-rolling one like this and take it as a
> sign: that's `cva`, coming up in Step 2. For values that are truly dynamic, a user-picked color, a computed length,
> skip the lookup map too: use [CSS variables](/docs/styling/dynamic-styling) instead.
Each property and value becomes a shared atomic class. Write `css({ color: 'white' })` in two different files, and Panda
still only generates it once. Your CSS scales with how many distinct styles you use, not with how many components you
have.
More on the object syntax: [Writing styles](/docs/styling/writing-styles).
## Step 2: Promote to a recipe when variants appear
The button now needs `size` and `visual`. Duplicating `css()` calls or nesting ternaries isn't an effective approach:
```tsx
// works, but every new variant makes this harder to read
function Button({ size, visual, children }) {
return (
)
}
```
That's a variant matrix. Reach for `cva`:
```tsx
import { cva } from '../styled-system/css'
const button = cva({
base: {
display: 'inline-flex',
alignItems: 'center',
rounded: 'md',
fontWeight: 'semibold'
},
variants: {
size: {
sm: { px: '3', py: '1.5', fontSize: 'sm' },
lg: { px: '5', py: '3', fontSize: 'md' }
},
visual: {
solid: { bg: 'blue.500', color: 'white', _hover: { bg: 'blue.600' } },
outline: { borderWidth: '1px', borderColor: 'blue.500', color: 'blue.500' }
}
},
defaultVariants: {
size: 'sm',
visual: 'solid'
}
})
export function Button({ size, visual, children }) {
return
}
```
Promote to a recipe when you have variants to name, not because the component file got long. One element, many looks:
`cva` is enough.
> That "before" example still compiles, unlike Step 1's `` `red.${shade}` ``. A ternary's branches are both literal, so
> Panda can see them and generates CSS for both. A template literal's interpolation isn't a fixed set of options, so
> there's nothing for Panda to generate. Runtime _choice_ between literals is fine. Runtime _computation_ of a new one
> is not.
Full API: [Recipes](/docs/styling/recipes).
## Step 3: Split into slots when markup has parts
Now let's build a Card. A Card has a `root`, `title`, and a `body`. One `size` prop should style all three together.
`sva` gives you one recipe and a class bag per part:
```tsx
import { sva } from '../styled-system/css'
const card = sva({
slots: ['root', 'title', 'body'],
base: {
root: {
rounded: 'lg',
borderWidth: '1px',
borderColor: 'gray.200',
bg: 'white',
p: '4'
},
title: { fontWeight: 'semibold', mb: '2' },
body: { color: 'gray.600', fontSize: 'sm' }
},
variants: {
size: {
sm: {
root: { p: '3' },
title: { fontSize: 'md' },
body: { fontSize: 'xs' }
},
lg: {
root: { p: '6' },
title: { fontSize: 'xl' },
body: { fontSize: 'md' }
}
}
},
defaultVariants: { size: 'sm' }
})
export function Card({ size, title, children }) {
const classes = card({ size })
return (
{title}
{children}
)
}
```
Use slots when one variant API owns multiple elements. Stay on `cva` when a single element is enough.
Full API: [Slot recipes](/docs/styling/slot-recipes).
## Step 4: Move to config when it's a system primitive
`cva` and `sva` are fine as long as the recipe stays colocated: it only matters inside this one component's file. Move
the same `base` / `variants` / `defaultVariants` shape into `defineRecipe` or `defineSlotRecipe` once any of this gets
true: other apps need to import the component, it ships inside a preset, or you want Panda to generate CSS only for
the variants your code actually calls, not the full matrix.
Button becomes a config recipe:
```ts filename="button.recipe.ts"
import { defineRecipe } from '@pandacss/dev'
export const buttonRecipe = defineRecipe({
className: 'button',
base: {
display: 'inline-flex',
alignItems: 'center',
rounded: 'md',
fontWeight: 'semibold'
},
variants: {
size: {
sm: { px: '3', py: '1.5', fontSize: 'sm' },
lg: { px: '5', py: '3', fontSize: 'md' }
},
visual: {
solid: { bg: 'blue.500', color: 'white', _hover: { bg: 'blue.600' } },
outline: { borderWidth: '1px', borderColor: 'blue.500', color: 'blue.500' }
}
},
defaultVariants: {
size: 'sm',
visual: 'solid'
}
})
```
Card becomes a config slot recipe:
```ts filename="card.recipe.ts"
import { defineSlotRecipe } from '@pandacss/dev'
export const cardRecipe = defineSlotRecipe({
className: 'card',
slots: ['root', 'title', 'body'],
base: {
root: {
rounded: 'lg',
borderWidth: '1px',
borderColor: 'gray.200',
bg: 'white',
p: '4'
},
title: { fontWeight: 'semibold', mb: '2' },
body: { color: 'gray.600', fontSize: 'sm' }
},
variants: {
size: {
sm: {
root: { p: '3' },
title: { fontSize: 'md' },
body: { fontSize: 'xs' }
},
lg: {
root: { p: '6' },
title: { fontSize: 'xl' },
body: { fontSize: 'md' }
}
}
},
defaultVariants: { size: 'sm' }
})
```
Register them on the theme, then import the generated recipes from `styled-system`:
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
import { buttonRecipe } from './button.recipe'
import { cardRecipe } from './card.recipe'
export default defineConfig({
// ...
theme: {
extend: {
recipes: { button: buttonRecipe },
slotRecipes: { card: cardRecipe }
}
}
})
```
The call signature doesn't change. Only the import path does, from a local file to `styled-system`:
```tsx
import { button, card } from '../styled-system/recipes'
const classes = card({ size: 'lg' })
Plan
```
**Decision rule:**
- Stay on `cva` / `sva` for colocated, app-local components.
- Move to `defineRecipe` / `defineSlotRecipe` for design-system components. You get leaner JIT CSS, and you can share
the recipe through a preset or `panda lib`.
The full comparison table lives here:
[Should I use atomic or config recipes?](/docs/styling/recipes#should-i-use-atomic-or-config-recipes-). The same split
applies to `sva` vs config slot recipes.
## Step 5: Trust the system
A few things don't change as you move between `css`, `cva`, `sva`, and config: tokens, cascade layers, and types.
**Tokens are the vocabulary.** Name intent once: `colors.text`, `spacing.4`. Every `css`, recipe, and slot refers to the
same names. See [tokens](/docs/theming/tokens).
**Cascade layers keep overrides predictable.** Panda emits into fixed layers, reset, base, tokens, recipes, utilities,
and a utility always beats a recipe no matter what order you import things in. That's decided at the CSS layer, not in
your code. Merge style objects yourself, though, and a different rule kicks in: the last value wins. See
[cascade layers](/docs/styling/cascade-layers) and [merging styles](/docs/styling/merging-styles).
**Types come from your config.** Autocomplete offers your tokens and recipe variants. A typo is a type error while you
type. Turn on [`strictTokens`](/docs/styling/writing-styles#stricttokens) when you want raw values rejected too.
## Where to go next
Know which step a style belongs on? You already think in Panda.
- [Welcome to Panda](/docs/styling/getting-started): install Panda and generate your first styles.
- [Writing styles](/docs/styling/writing-styles): the `css` function and object syntax in full.
- [Recipes](/docs/styling/recipes): `cva` and config recipes.
- [Slot recipes](/docs/styling/slot-recipes): `sva` and config slot recipes.
---
## Upgrading to v2
What changed in Panda v2, how to install the beta, and the breaking changes to fix as you migrate.
v2 is in beta (`2.0.0-beta`). The authoring API is stable, but the package layout and a few CLI surfaces are still
moving. Install it with the `@beta` tag; a plain install stays on v1.
v2 keeps the framework you already know and rewrites the compiler underneath it. You write the same `css()`, recipes,
patterns, tokens, conditions, and JSX props. What changed is how Panda turns them into CSS.
## What v2 is
v1 ran extraction and evaluation through `ts-morph` and `ts-evaluator` in Node. v2 replaces that hot path with a native
engine built on [Oxc](https://oxc.rs), shipped two ways:
- **`@pandacss/compiler`** β a native binding. The CLI and bundler plugins use it.
- **`@pandacss/compiler-wasm`** β the same engine compiled to WASM for the browser. The playground runs on it.
Both wrap the same Rust crates, so Node and browser builds produce the same CSS. You get faster extraction β one parse
per file, no TypeScript program in the hot path β and a smaller install, since the `ts-morph` dependency tree is gone.
Output stays in parity with v1 except for the deliberate changes below.
See [The compiler engine](/docs/styling/compiler-engine) for the pipeline, and
[How Panda works](/docs/styling/how-panda-works) for the model it keeps.
## Try the beta
### Release channels
v1 and v2 ship side by side on npm. Install without a tag and you stay on stable v1.
| Channel | Version | Install |
| -------- | ----------------- | -------------------- |
| `latest` | v1 (`1.x`) | `@pandacss/dev` |
| `beta` | v2 (`2.0.0-beta`) | `@pandacss/dev@beta` |
All `@pandacss/*` packages move on one version. Don't mix a v1 package with a v2 one.
### Install
Most projects only need `@pandacss/dev`:
```bash
pnpm add -D @pandacss/dev@beta
```
Add integrations on the same tag when you need them: `@pandacss/postcss@beta`, `@pandacss/vite@beta`,
`@pandacss/webpack@beta`, or `@pandacss/rollup@beta`.
v2 is ESM only and needs Node 22 or newer. Set `"type": "module"` (or use `.mjs`) so your `panda.config.ts` loads as ESM.
Want reproducible installs? Pin an exact version like `@pandacss/dev@2.0.0-beta.14`. `@beta` always resolves to the
newest pre-release.
### Build
Your v1 `panda.config.ts` carries over. Regenerate:
```bash
panda build # codegen + cssgen in one pass
panda dev # rebuild on change
```
The `panda` and `pandacss` binaries are the same as v1.
## Breaking changes to fix
These are the changes you have to act on when you move a project from v1.
### ESM only
There's no CommonJS build. If your config or tooling used `require()`:
```js
// β v1
const { defineConfig } = require('@pandacss/dev')
// β v2
import { defineConfig } from '@pandacss/dev'
```
Set `"type": "module"`, use `.mjs`, or run through an ESM-aware bundler. The `postcss.config.cjs` that
`panda init --postcss` writes is CommonJS on purpose and still works.
### Hooks moved to plugins
Hooks still exist, but they live on named plugins now, not a root `hooks` object:
```ts
// β v1
export default defineConfig({
hooks: {
'cssgen:done': ({ content }) => content,
},
})
// β v2
export default defineConfig({
plugins: [
{
name: 'local',
hooks: {
'parser:before': {
filter: { id: '**/*.{jsx,tsx}' },
handler: ({ content }) => content,
},
},
},
],
})
```
Supported hooks: `config:resolved`, `preset:resolved`, `parser:before`, `codegen:prepare`, `codegen:done`, and
`cssgen:done`. `cssgen:done` is observe-only now β it runs after the final CSS with `{ artifact, content, path? }` and
can't rewrite the string. Used it to strip unused tokens or keyframes? Reach for `optimize.removeUnusedTokens` /
`removeUnusedKeyframes` instead. The v1 engine hooks (`context:created`, `parser:after`, `tokens:created`,
`utility:created`, `parser:before.configure(...)`, β¦) are gone. See [Hooks](/docs/design-systems/hooks).
### createStyleContext is now two helpers
`createStyleContext` is gone from `styled-system/jsx`. Use one helper per recipe kind:
```tsx
// β v1 β one helper for both
import { createStyleContext } from 'styled-system/jsx'
// β v2 β slot recipe (sva)
import { createSlotRecipeContext } from 'styled-system/jsx'
const { withRootProvider, withProvider, withContext } = createSlotRecipeContext(card)
// β v2 β config recipe (cva)
import { createRecipeContext } from 'styled-system/jsx'
const { withContext } = createRecipeContext(button)
```
`withRootProvider` is new β use it for a slot recipe's root when the root doesn't render a slot of its own. See
[JSX style context](/docs/styling/jsx-style-context).
### MCP moved to its own package
The MCP server left the CLI. Run it from `@pandacss/mcp` with the `panda-mcp` binary:
```bash
# β v1
panda mcp
panda init-mcp
# β v2 β run it directly, nothing to install
npx -y @pandacss/mcp
```
See [MCP server](/docs/styling/mcp-server).
### Packages folded into the compiler
These v1 internals are gone; their work lives in `@pandacss/compiler` now. Drop direct imports of `@pandacss/core`,
`@pandacss/extractor`, `@pandacss/generator`, `@pandacss/node`, `@pandacss/parser`, `@pandacss/token-dictionary`,
`@pandacss/is-valid-prop`, `@pandacss/logger`, `@pandacss/reporter`, and the Astro `@pandacss/studio`. If you only use
`@pandacss/dev` plus Vite or PostCSS, you're fine.
### Config options removed
Ten options are gone: `studio` (and its sub-options), `eject`, `emitTokensOnly`, `gitignore`, `clean`, `watch`, `poll`,
`lightningcss`, and `browserslist`. `forceConsistentTypeExtension` is replaced by `forceImportExtension` β different
semantics, not a rename. `outExtension` gains a `'ts'` value. See [Config](/docs/reference/config).
### CLI commands and flags
`panda inspect`, `panda validate`, and `panda info` are removed. Use `panda doctor` (add `--json` for scripts).
Logging flags are consolidated: `--log-level silent|error|warn|info|debug` replaces `--silent`, `--quiet`, and
`--verbose`. `--profile` replaces `--cpu-prof` and covers time in the Rust engine, not just the Node side. Shared flags
are kebab-case (`--max-warnings`, `--watch-debounce`, β¦). See [CLI](/docs/reference/cli).
### Border overrides sort by property, not source order
v2 orders atomic rules by property breadth, deterministically. All the border shorthands sit in one tier, so an
all-sides shorthand always wins over a per-side one, no matter the merge order. Composing an all-sides border with a
per-side override no longer opens that side:
```tsx
// v1: renders an open bracket β the override was declared last
// v2: renders a closed box β borderWidth re-applies the inline-end side
cx(css({ borderWidth: '1px', borderStyle: 'solid' }), css({ borderInlineEnd: '0' }))
```
Reach for the longhand when the override has to win β longhands rank above every shorthand, so they always land last:
```tsx
cx(css({ borderWidth: '1px', borderStyle: 'solid' }), css({ borderInlineEndWidth: '0' }))
```
Padding, margin, and every other property group sort the same way. See [Border](/docs/reference/border).
### scrollbarWidth takes keywords, not tokens
v1 mapped `scrollbarWidth` to `sizes` tokens, so `scrollbarWidth: '4'` emitted `var(--sizes-4)` and browsers dropped it.
It's now `auto | thin | none`:
```ts
// β v1 β type-checked, invalid CSS
css({ scrollbarWidth: '4' })
// β v2
css({ scrollbarWidth: 'thin' })
```
If you passed a single color to `scrollbarColor`, move it to `scrollbarThumb`. `scrollbarColor` is now a raw two-value
string (`'red transparent'`).
### No universal variable reset
v1 seeded `--translate-x`, `--blur`, `--gradient-from-position` and friends through a `*, ::before, ::after, ::backdrop`
rule β 34 declarations on every element, used or not. v2 registers those variables with `@property` instead, so they
carry their own defaults and only ship when you use the utility. A page that uses none of them gets an empty base layer.
This needs `@property` (Chrome 85+, Safari 16.4+, Firefox 128+); older browsers drop the affected utilities rather than
mis-render them. Set `optimize.propertyFallback: true` to also seed the defaults as plain declarations for the variables
your project uses.
## What's new you'll want
Beyond parity, v2 adds features worth turning on:
- **The `optimize` block.** Opt-in CSS cleanup: `removeUnusedTokens`, `removeUnusedKeyframes`, `smartCompoundVariants`,
`treeshakeDesignSystem`, and `propertyFallback`. It replaces the common v1 `cssgen:done` cleanup.
- **New utilities and conditions.** Mask helpers ([Masks](/docs/reference/masks)), scrollbar utilities, pointer and
validity conditions (`_pointerFine`, `_userValid`, `_inert`), and raw CSS keywords like `textWrap: 'pretty'` and
`justifyContent: 'safe center'`.
- **`viewTransition()`.** Style the View Transitions API and get a stable class back. See
[View transitions](/docs/styling/view-transition).
- **Cross-file composition and source transforms.** Compose `css.raw()` styles across files, and let bundler plugins
rewrite static `css()` calls with `transform: true`.
- **Smaller `.d.ts`.** `cva` / `sva` return types key on a clean props type, which unblocks `isolatedDeclarations`. See
[Isolated declarations](/docs/design-systems/isolated-declarations).
- **Design systems.** `panda lib` publishes a component library; apps consume it with the `designSystem` config field.
It replaces `panda ship`. See [Building a design system](/docs/design-systems/building-a-design-system) and
[Consuming a design system](/docs/design-systems/consuming-a-design-system).
- **Linting on the v2 engine.** The [ESLint & oxlint plugin](/docs/reference/eslint-oxlint-plugin) lints against the
same extraction the build uses.
## Still being finalized
Honest gaps in the beta. Expect them to change before stable:
- **Studio.** The Astro-based `@pandacss/studio` is gone. A lighter, CLI-generated studio is planned. See
[Studio in v2](/docs/theming/studio-v2).
- **PostCSS plugin.** `@pandacss/postcss` v2 is experimental. If it misbehaves, use the Vite plugin or `panda build`.
- **CSS minification.** `minify: true` works in the native emitter; full parity with the v1 LightningCSS path is still
open.
- **Some presets and plugins.** A few v1 community presets aren't ported yet. Check engine coverage before you rely on
them.
## See also
- [The compiler engine](/docs/styling/compiler-engine) β the Rust pipeline underneath v2.
- [Config](/docs/reference/config) β the full v2 config reference.
- [CLI](/docs/reference/cli) β every command and flag.
- [How Panda works](/docs/styling/how-panda-works) β the mental model v2 keeps.
---
## viewTransition()
Style shared View Transitions API animations with a hashed bag class. You still set unique view-transition-name values at runtime.
The View Transitions API styles its animation with `::view-transition-old(name)` and `::view-transition-new(name)`,
matched against a `view-transition-name` you set on the element. That name has to be unique per element. Two elements
with the same name break the transition.
Panda extracts and shares styles across every place you call a style function. Those two rules don't mix: if the CSS
were keyed on `view-transition-name`, Panda couldn't dedupe it, since the name is different every time. So Panda
doesn't touch `view-transition-name` at all. Instead, `viewTransition()` gives you a class, generated from a real
CSS property built for exactly this, `view-transition-class`. Panda can extract and share a class like any other
atomic style. You still set the unique name yourself, however your framework wants it.
Import `viewTransition` from `styled-system/css`.
## Basic example
```ts
import { viewTransition } from 'styled-system/css'
const slide = viewTransition({
group: {
animationDuration: '0.4s',
animationTimingFunction: 'ease-in-out'
},
imagePair: { isolation: 'isolate' },
old: { opacity: 0 },
new: { opacity: 1 }
})
// β "vt_xxx"
```
`slide` is a class name, `"vt_xxx"`. Panda emits the CSS behind it:
```css
@layer utilities {
.vt_xxx {
view-transition-class: vt_xxx;
}
::view-transition-group(.vt_xxx) {
animation-duration: 0.4s;
animation-timing-function: ease-in-out;
}
::view-transition-image-pair(.vt_xxx) {
isolation: isolate;
}
::view-transition-old(.vt_xxx) {
opacity: 0;
}
::view-transition-new(.vt_xxx) {
opacity: 1;
}
}
```
Call `viewTransition()` again somewhere else with the exact same options and you get the exact same class back, no
duplicate CSS. That's the whole point: one shared bag class, however many elements use it.
## Wiring it into your framework
`viewTransition()` only gets you the class. Every framework has its own way of setting `view-transition-name`, so
that part is still on you.
### React
Pass the class into React's View Transition Class props, `enter`, `exit`, `share`, `update`, `default`. Reserve
`name` for elements that need a shared-element transition:
```tsx
import { ViewTransition } from 'react'
import { viewTransition } from 'styled-system/css'
const slide = viewTransition({
group: { animationDuration: '0.4s' },
old: { opacity: 0 },
new: { opacity: 1 }
})
export function HeroImage() {
return (
)
}
```
`` needs a React build that exports it, Canary or Experimental as of this writing. Next.js can opt in
with `experimental.viewTransition: true`.
### Astro
Astro owns the name through `transition:name`. Put the bag class on the element next to it:
```astro
---
import { viewTransition } from '../styled-system/css'
const slide = viewTransition({
group: { animationDuration: '0.4s' },
old: { opacity: 0 },
new: { opacity: 1 }
})
---
```
Enable client routing with `` from `astro:transitions` in your layout, or the browser never starts a
transition to animate.
### Solid or Nuxt
Neither framework wraps this for you, so you set both the class and the name yourself:
```tsx
import { viewTransition } from 'styled-system/css'
const slide = viewTransition({
group: { animationDuration: '0.4s' },
old: { opacity: 0 },
new: { opacity: 1 }
})
```
## Slots
Four options, each one a slot in the View Transitions pseudo-element tree:
| Option | CSS selector |
| ----------- | -------------------------------------- |
| `group` | `::view-transition-group(.vt_*)` |
| `imagePair` | `::view-transition-image-pair(.vt_*)` |
| `old` | `::view-transition-old(.vt_*)` |
| `new` | `::view-transition-new(.vt_*)` |
Each one takes a style object, same shape as `css()`. For animations, set `animationName` to a theme keyframe or a
name you defined yourself.
## Shipping a shared transition from a design system
Export the bag as a constant from your library source, the same way you'd export a shared `cva`:
```ts
export const slide = viewTransition({
old: { opacity: 0 },
new: { opacity: 1 }
})
```
`panda lib` serializes it into build info, so apps consuming your library get the CSS without re-scanning your
source. It tree-shakes the same way recipes do, only the bags an app actually imports ship. There's no
`theme.viewTransitions` config registry to register this in. If you've shared a `cva` or a plain `css()` helper from
a design system before, this works the same way.
---
## 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.
```js
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 opacity modifier
See [using a custom property](/docs/styling/color-opacity-modifier#using-a-custom-property) for the
`{colors.colorPaletteβ¦/opacity}` syntax on `--*` variables:
```js
import { css } from '../styled-system/css'
const className = css({
colorPalette: 'blue',
'--tint': '{colors.colorPalette.500/50}',
bg: 'var(--tint)',
color: 'colorPalette.700'
})
```
## Using with recipes
You can also use virtual colors with recipes.
```js
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.
```js
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 (
<>
>
)
}
```
## 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.
```js
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:
```tsx
import { css } from '../styled-system/css'
export const App = () => {
return (
)
}
```
Or you can use any deeply nested property (e.g. `button.light.accent`) as a root color palette:
```tsx
import { css } from '../styled-system/css'
export const App = () => {
return (
)
}
```
> **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).
> Learn more about [static css generation](/docs/design-systems/static).
```tsx
export default defineConfig({
staticCss: {
css: [
{
properties: { colorPalette: ['red', 'blue'] }
}
]
}
})
```
Then in your code, you can design components that use the `colorPalette` property:
```tsx
import { css } from '../styled-system/css'
function ButtonShowcase() {
const [colorPalette, setColorPalette] = useState('red')
return (
)
}
```
## Configuration
By default, color palette generation is enabled and includes all colors defined in your theme.
You can control which colors are used to generate color palettes by configuring the `colorPalette` property in your
theme.
### Disable Color Palette
To completely disable color palette generation, set `enabled` to `false`:
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
colorPalette: {
enabled: false
}
}
})
```
### Include Specific Colors
To generate color palettes for only specific colors, use the `include` option:
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
colorPalette: {
include: ['gray', 'blue', 'red']
}
}
})
```
This will only generate color palettes for `gray`, `blue`, and `red` colors, even if you have other colors defined in
your theme.
**Glob patterns** are supported for nested tokens:
```ts filename="panda.config.ts"
colorPalette: {
include: ['gray.*', 'blue.*'] // Includes all nested tokens
}
```
### Exclude Specific Colors
To exclude certain colors from color palette generation, use the `exclude` option:
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
colorPalette: {
exclude: ['yellow', 'orange']
}
}
})
```
This will generate color palettes for all colors except `yellow` and `orange`.
### Combination of Options
You can combine the `enabled`, `include`, and `exclude` options as needed:
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
colorPalette: {
enabled: true,
include: ['gray', 'blue', 'red', 'green'],
exclude: ['red'] // This will override the include for 'red'
}
}
})
```
In this example, color palettes will be generated for `gray`, `blue`, and `green`, but not for `red` (since it's
excluded).
## Nested Semantic Tokens
If nested tokens show as raw paths (e.g., `colors.base.accent`) instead of CSS variables, use glob patterns:
```ts filename="panda.config.ts"
semanticTokens: {
colors: {
button: {
primary: {
DEFAULT: { value: '{colors.blue.500}' },
hover: { value: '{colors.blue.600}' }
}
}
}
},
colorPalette: {
include: ['button.*'] // Include all nested paths
}
```
Usage:
```tsx
className={css({
colorPalette: 'button.primary',
bg: 'colorPalette',
_hover: { bg: 'colorPalette.hover' }
})}
```
---
## Using Vite
Easily use Panda with Vite, React and Typescript with our dedicated integration.
This guide will show you how to set up Panda CSS in a Vite project using PostCSS.
## Start a new project
### Create Vite project
To get started, we will need to create a new Vite project using `react-ts` template.
{/* */}
```bash
pnpm create vite test-app --template react-ts
cd test-app
pnpm install
```
```bash
npm create vite@latest test-app -- --template react-ts
cd test-app
npm install
```
```bash
yarn create vite test-app --template react-ts
cd test-app
yarn
```
```bash
bun create vite test-app --template react-ts
cd test-app
bun install
```
{/* */}
### Install Panda
Install panda and create your `panda.config.ts` file.
{/* */}
```bash
pnpm install -D @pandacss/dev
pnpm panda init --postcss
```
```bash
npm install -D @pandacss/dev
npx panda init --postcss
```
```bash
yarn add -D @pandacss/dev
yarn panda init --postcss
```
```bash
bun add -D @pandacss/dev
bun panda init --postcss
```
{/* */}
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3} filename="package.json"
{
"scripts": {
+ "prepare": "panda codegen",
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
}
}
```
- `"prepare"` - script that will run Panda CSS CLI codegen before each build. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the content
Make sure that all of the paths of your React components are included in the `include` section of the `panda.config.ts`
file.
```js {8} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ['./src/**/*.{js,jsx,ts,tsx}', './pages/**/*.{js,jsx,ts,tsx}'],
// Files to exclude
exclude: [],
// Generates JSX utilities with options of React, Preact, Qwik, Solid, Vue
jsxFramework: 'react',
// The output directory for your css system
outdir: 'styled-system'
})
```
### Configure the entry CSS with layers
Add this code to an `src/index.css` file imported in the root component of your project.
```css filename="src/index.css"
@layer reset, base, tokens, recipes, utilities;
```
> **Note:** Feel free to remove `src/App.css` file as we don't need it anymore, and make sure to remove the import from
> the `src/App.tsx` file.
## Start your build process
Run the following command to start your development server.
{/* */}
```bash
pnpm dev
```
```bash
npm run dev
```
```bash
yarn dev
```
```bash
bun dev
```
{/* */}
### Start using Panda
Now you can start using Panda CSS in your project. Here is the snippet of code that you can use in your `src/App.tsx`
file.
```tsx filename="src/App.tsx"
import { css } from '../styled-system/css'
function App() {
return
Hello πΌ!
}
export default App
```
## Troubleshooting
If you're not getting import autocomplete in your IDE, you may need to include the `styled-system` directory in your
`tsconfig.json` file:
```json filename="tsconfig.json"
{
// ...
"include": ["src", "styled-system"]
}
```
---
## Using Vue
Easily use Panda with Vue with our dedicated integration.
Learn how to set up Panda CSS in a Vue project using PostCSS.
## Start a new project
### Create Vite project
To get started, we will need to create a new Vue project using the official
[scaffolding tool](https://github.com/vuejs/create-vue).
If you don't enter any parameter, the CLI will guide you through the process of creating a new Vue app.
{/* */}
```bash
pnpm create vue@latest
```
```bash
npm create vue@latest
```
```bash
yarn create vue@latest
```
```bash
bun create vue@latest
```
{/* */}
You will be asked a few questions, answer them as follows:
```bash
Vue.js - The Progressive JavaScript Framework
β Project name: β¦ test-app
β Add TypeScript? β¦ Yes
β Add JSX Support? β¦ Yes
β Add Vue Router for Single Page Application development? β¦ No / Yes
β Add Pinia for state management? β¦ No / Yes
β Add Vitest for Unit Testing? β¦ No / Yes
β Add an End-to-End Testing Solution? βΊ No
β Add ESLint for code quality? β¦ No / Yes
β Add Prettier for code formatting? β¦ No / Yes
```
Enter the newly created directory and install the dependencies.
{/* */}
```bash
cd test-app
pnpm install
```
```bash
cd test-app
npm install
```
```bash
cd test-app
yarn
```
```bash
cd test-app
bun install
```
{/* */}
### Install Panda
Install panda and create your `panda.config.ts` file.
{/* */}
```bash
pnpm install -D @pandacss/dev
pnpm panda init --postcss
```
```bash
npm install -D @pandacss/dev
npx panda init --postcss
```
```bash
yarn add -D @pandacss/dev
yarn panda init --postcss
```
```bash
bun add -D @pandacss/dev
bun panda init --postcss
```
{/* */}
### Update package.json scripts
Open your `package.json` file and update the `scripts` section as follows:
```diff {3} filename="package.json"
{
"scripts": {
+ "prepare": "panda codegen",
"dev": "vite",
"build": "run-p type-check build-only",
"preview": "vite preview",
"build-only": "vite build"
}
}
```
- `"prepare"` - script that will run Panda CSS CLI codegen before each build. Read more about
[codegen](/docs/reference/cli#codegen) in the CLI section.
> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.
### Configure the content
Make sure that all of the paths of your Vue components are included in the `include` section of the `panda.config.ts`
file.
```js {8, 17} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// Whether to use css reset
preflight: true,
// Where to look for your css declarations
include: ['./src/**/*.{js,jsx,ts,tsx,vue}'],
// Files to exclude
exclude: [],
// The output directory for your css system
outdir: 'styled-system'
})
```
### Configure the entry CSS with layers
Add this code to an `src/index.css` file and import it in the root component of your project.
```css filename="src/index.css"
@layer reset, base, tokens, recipes, utilities;
```
## Start your build process
Run the following command to start your development server.
{/* */}
```bash
pnpm dev
```
```bash
npm run dev
```
```bash
yarn dev
```
```bash
bun dev
```
{/* */}
### Start using Panda
Now you can start using Panda CSS in your project. Here is the snippet of code that you can use in your `src/App.vue`
file.
```vue-html filename="src/App.vue"
Hello πΌ!
```
---
## Writing Styles
Panda generates the utilities you need to style your components with confidence.
Using the object syntax is a fundamental approach to writing styles in Panda. It not only provides a type-safe style
authoring experience, but also improves readability and ensures a consistent experience with style overrides.
## Atomic Styles
When you write styles in Panda, it generates a modern atomic stylesheet that is automatically scoped to the
`@layer utilities` cascade layer.
The atomic stylesheets approach offers several advantages, such as improved code maintainability and reusability, as
well as a smaller overall CSS footprint.
Panda exposes a `css` function that can be used to author styles. It accepts a style object and returns a className
string.
```jsx
import { css } from '../styled-system/css'
const styles = css({
backgroundColor: 'gainsboro',
borderRadius: '9999px',
fontSize: '13px',
padding: '10px 15px'
})
// Generated className:
// --> bg_gainsboro rounded_9999px fs_13px p_10px_15px
Hello World
```
The styles generated at build time end up like this:
```css
@layer utilities {
.bg_gainsboro {
background-color: gainsboro;
}
.rounded_9999px {
border-radius: 9999px;
}
.fs_13px {
font-size: 13px;
}
.p_10px_15px {
padding: 10px 15px;
}
}
```
### Shorthand Properties
Panda provides shorthands for common css properties to help improve the speed of development and reduce the visual
density of your style declarations.
Properties like `borderRadius`, `backgroundColor`, and `padding` can be swapped to their shorthand equivalent `rounded`,
`bg`, and `p`.
```jsx
import { css } from '../styled-system/css'
// BEFORE - Good
const styles = css({
backgroundColor: 'gainsboro',
borderRadius: '9999px',
fontSize: '13px',
padding: '10px 15px'
})
// AFTER - Better
const styles = css({
bg: 'gainsboro',
rounded: '9999px',
fontSize: '13px',
p: '10px 15px'
})
```
> Shorthands are documented alongside their respective properties in the [utilities](/docs/reference/background)
> section.
### Type safety
Panda is built with TypeScript and provides type safety for all style properties and shorthands. Most of the style
properties are connected to either the native CSS properties or their respective token value defined as defined in the
`theme` object.
```ts
import { css } from '../styled-system/css'
// ‡ you'll get autocomplete for colors
const styles = css({ bg: '|' })
```
> You can also enable the `strictTokens: true` setting in the Panda configuration. This allows only token values and
> prevents the use of custom or raw CSS values.
- `config.strictTokens` will only affect properties that have config tokens, such as `color`, `bg`, `borderColor`, etc.
- `config.strictPropertyValues` will throw for properties that do not have config tokens, such as `display`, `content`,
`willChange`, etc. when the value is not a predefined CSS value.
> In both cases, you can use the `[xxx]` escape-hatch syntax to use custom or raw CSS values without TypeScript errors.
#### strictTokens
With `config.strictTokens` enabled, you can only use token values in your styles. This prevents the use of custom or raw
CSS values.
```ts filename="panda.config.ts"
import { css } from '../styled-system/css'
css({ bg: 'red' }) // β Error: "red" is not a valid token value
css({ fontSize: '123px' }) // β Error: "123px" is not a valid token value
css({ bg: 'red.400' }) // β Valid
css({ fontSize: '[123px]' }) // β Valid, since `[123px]` is using the escape-hatch syntax
css({ content: 'abc' }) // β Valid, since `content` isn't bound to a config token
```
For one-off styles, you can always use the escape-hatch syntax `[xxx]` to use custom or raw CSS values without
TypeScript errors.
```ts filename="panda.config.ts"
import { css } from '../styled-system/css'
css({ bg: '[red]' }) // β Valid, since `[red]` is using the escape-hatch syntax
css({ fontSize: '[123px]' }) // β Valid, since `[123px]` is using the escape-hatch syntax
```
#### strictPropertyValues
With `config.strictPropertyValues` enabled, you can only use valid CSS values for properties that do have a predefined
list of values in your styles. This prevents the use of custom or raw CSS values.
```ts filename="panda.config.ts"
css({ display: 'flex' }) // β Valid
css({ display: 'block' }) // β Valid
css({ display: 'abc' }) // β will throw since 'abc' is not part of predefined values of 'display'
css({ pos: 'absolute123' }) // β will throw since 'absolute123' is not part of predefined values of 'position'
css({ display: '[var(--btn-display)]' }) // β Valid, since `[var(--btn-display)]` is using the escape-hatch syntax
css({ content: '""' }) // β Valid, since `content` does not have a predefined list of values
css({ flex: '0 1' }) // β Valid, since `flex` does not have a predefined list of values
```
The `config.strictPropertyValues` option will only be applied to this exhaustive list of properties:
```ts
type StrictableProps =
| 'alignContent'
| 'alignItems'
| 'alignSelf'
| 'all'
| 'animationComposition'
| 'animationDirection'
| 'animationFillMode'
| 'appearance'
| 'backfaceVisibility'
| 'backgroundAttachment'
| 'backgroundClip'
| 'borderCollapse'
| 'border'
| 'borderBlock'
| 'borderBlockEnd'
| 'borderBlockStart'
| 'borderBottom'
| 'borderInline'
| 'borderInlineEnd'
| 'borderInlineStart'
| 'borderLeft'
| 'borderRight'
| 'borderTop'
| 'borderBlockEndStyle'
| 'borderBlockStartStyle'
| 'borderBlockStyle'
| 'borderBottomStyle'
| 'borderInlineEndStyle'
| 'borderInlineStartStyle'
| 'borderInlineStyle'
| 'borderLeftStyle'
| 'borderRightStyle'
| 'borderTopStyle'
| 'boxDecorationBreak'
| 'boxSizing'
| 'breakAfter'
| 'breakBefore'
| 'breakInside'
| 'captionSide'
| 'clear'
| 'columnFill'
| 'columnRuleStyle'
| 'contentVisibility'
| 'direction'
| 'display'
| 'emptyCells'
| 'flexDirection'
| 'flexWrap'
| 'float'
| 'fontKerning'
| 'forcedColorAdjust'
| 'isolation'
| 'lineBreak'
| 'mixBlendMode'
| 'objectFit'
| 'outlineStyle'
| 'overflow'
| 'overflowX'
| 'overflowY'
| 'overflowBlock'
| 'overflowInline'
| 'overflowWrap'
| 'pointerEvents'
| 'position'
| 'resize'
| 'scrollBehavior'
| 'touchAction'
| 'transformBox'
| 'transformStyle'
| 'userSelect'
| 'visibility'
| 'wordBreak'
| 'writingMode'
```
## Nested Styles
Panda provides different ways of nesting style declarations. You can use the native css nesting syntax, or the built-in
pseudo props like `_hover` and `_focus`. Pseudo props are covered more in-depth in the next section.
### Native CSS Nesting
Panda supports the native css nesting syntax. You can use the `&` selector to create nested styles.
> **Important:** It is required to use the "&" character when nesting styles.
```jsx
```
You can also target children and siblings using the `&` syntax.
```jsx
```
We recommend not using descendant selectors as they can lead to specificity issues when managing style overrides.
Colocating styles directly on the element is the preferred way of writing styles in Panda.
### Using Pseudo Props
Panda provides a set of pseudo props that can be used to create nested styles. The pseudo props are prefixed with an
underscore `_` to avoid conflicts with the native pseudo selectors.
For example, to create a hover style, you can use the `_hover` pseudo prop.
```jsx
```
> See the [pseudo props](/docs/styling/conditional-styles#reference) section for a list of all available pseudo props.
## Global styles
Global styles are useful for applying additional global resets or font faces. Use the `globalCss` property in the
`panda.config.ts` file to define global styles.
Global styles are inserted at the top of the stylesheet and are scoped to the `@layer base` cascade layer.
> For resets, global variables, theming patterns, and more examples, see [Global styles](/docs/styling/global-styles).
```js filename="panda.config.ts"
import { defineConfig, defineGlobalStyles } from '@pandacss/dev'
const globalCss = defineGlobalStyles({
'html, body': {
color: 'gray.900',
lineHeight: '1.5'
}
})
export default defineConfig({
// ...
globalCss
})
```
The styles generated at build time will look like this:
```css
@layer base {
html,
body {
color: var(--colors-gray-900);
line-height: 1.5;
}
}
```
## Style Composition
### Merging styles
Passing multiple styles to the `css` function will deeply merge the styles, allowing you to override styles in a
predictable way.
```jsx
import { css } from '../styled-system/css'
const result = css({ mx: '3', paddingTop: '4' }, { mx: '10', pt: '6' })
// ^? result = "mx_10 pt_6"
```
To design a component that supports style overrides, you can provide the `css` prop as a style object, and it'll be
merged correctly.
```tsx filename="src/components/Button.tsx"
import { css } from '../styled-system/css'
export const Button = ({ css: cssProp = {}, children }) => {
const className = css({ display: 'flex', alignItems: 'center', color: 'black' }, cssProp)
return
}
```
Then you can use the `Button` component like this:
```tsx filename="src/app/page.tsx"
import { Button } from './Button'
export default function Page() {
return (
)
}
```
---
You can use this approach as well with the `{cvaFn}.raw`, `{svaFn.raw}` and `{patternFn}.raw` functions, allowing style
objects to be merged as expected in any situation.
**Pattern Example:**
```tsx filename="src/components/Button.tsx"
import { hstack } from '../styled-system/patterns'
import { css } from '../styled-system/css'
export const Button = ({ css: cssProp = {}, children }) => {
// using the flex pattern
const hstackProps = hstack.raw({
border: '1px solid',
_hover: { color: 'blue.400' }
})
// merging the styles
const className = css(hstackProps, cssProp)
return
}
```
**CVA Example:**
```tsx filename="src/components/Button.tsx"
import { css, cva } from '../styled-system/css'
const buttonRecipe = cva({
base: { display: 'flex', fontSize: 'lg' },
variants: {
variant: {
primary: { color: 'white', backgroundColor: 'blue.500' }
}
}
})
export const Button = ({ css: cssProp = {}, children }) => {
const className = css(
// using the button recipe
buttonRecipe.raw({ variant: 'primary' }),
// adding style overrides (internal)
{ _hover: { color: 'blue.400' } },
// adding style overrides (external)
cssProp
)
return
}
```
**SVA Example:**
```tsx filename="src/components/Button.tsx"
import { css, sva } from '../styled-system/css'
const checkbox = sva({
slots: ['root', 'control', 'label'],
base: {
root: { display: 'flex', alignItems: 'center', gap: '2' },
control: { borderWidth: '1px', borderRadius: 'sm' },
label: { marginStart: '2' }
},
variants: {
size: {
sm: {
control: { width: '8', height: '8' },
label: { fontSize: 'sm' }
},
md: {
control: { width: '10', height: '10' },
label: { fontSize: 'md' }
}
}
},
defaultVariants: {
size: 'sm'
}
})
export const Checkbox = ({ rootProps, controlProps, labelProps }) => {
// using the checkbox recipe
const slotStyles = checkbox.raw({ size: 'md' })
return (
)
}
// Usage
const App = () => {
return (
)
}
```
### Classname concatenation
Panda provides a simple `cx` function to join classnames. It accepts a list of classnames and returns a string.
```jsx
import { css, cx } from '../styled-system/css'
const styles = css({
borderWidth: '1px',
borderRadius: '8px',
paddingX: '12px',
paddingY: '24px'
})
const Card = ({ className, ...props }) => {
const rootClassName = cx('group', styles, className)
return
}
```
### Hashing
When debugging or previewing DOM elements in the browser, the length of the generated atomic `className` can get quite
long, and a bit annoying. If you prefer to have terser classnames, use the `hash` option to enable className and css
variable name hashing.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// ...
hash: true
})
```
> You might need to generate a new code artifact by running `panda codegen --clean`
When you write a style like this:
```jsx
import { css } from '../styled-system/css'
const styles = css({
display: 'flex',
flexDirection: 'row',
_hover: {
bg: 'red.50'
}
})
```
The hash generated css will look like:
```css
.fPSBzf {
display: flex;
}
.ksWBqx {
flex-direction: row;
}
.btpEVp:is(:hover, [data-hover]) {
background: var(--bINrJX);
}
```
> Use [`hash`](/docs/reference/config#hash) in production only. It makes debugging harder. See
> [Environment-specific config](/docs/design-systems/environment-specific-config) for branching on `process.env`.
## Important styles
Applying important styles works just like CSS
```js
css({
color: 'red !important'
})
```
You can also apply important using just the exclamation syntax `!`
```js
css({
color: 'red!'
})
```
## TypeScript
Use the `SystemStyleObject` type if you want to type your styles.
```ts {2}
import { css } from '../styled-system/css'
import type { SystemStyleObject } from '../styled-system/types'
const styles: SystemStyleObject = {
color: 'red'
}
```
## Property conflicts
When you combine shorthand and longhand properties, Panda resolves them predictably: the longhand wins, whatever order
you write them in. Panda sorts atomic rules by how broad each property is, so the narrower longhand emits last and
overrides the shorthand.
```jsx
import { css } from '../styled-system/css'
const styles = css({
paddingTop: '20px',
padding: '10px'
})
```
The styles generated at build time will look like this:
```css
@layer utilities {
.p_10px {
padding: 10px;
}
.pt_20px {
padding-top: 20px;
}
}
```
`.pt_20px` emits after `.p_10px`, so the element gets `10px` padding on three sides and `20px` on top.
## Global vars
You can use the `globalVars` property to define global
[CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/--*) or custom CSS
[`@property`](https://developer.mozilla.org/en-US/docs/Web/CSS/@property) definitions.
Panda will automatically generate the corresponding CSS variables and suggest them in your style objects.
> They will be generated in the [`cssVarRoot`](/docs/reference/config#cssvarroot) near your tokens.
This can be especially useful when using a 3rd party library that provides custom CSS variables, like a popper library
that exposes a `--popper-reference-width`.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// ...
globalVars: {
'--popper-reference-width': '4px',
// you can also generate a CSS @property
'--button-color': {
syntax: '',
inherits: false,
initialValue: 'blue'
}
}
})
```
> Note: Keys defined in `globalVars` will be available as a value for _every_ utilities, as they're not bound to token
> categories.
```ts
import { css } from '../styled-system/css'
const className = css({
'--button-color': 'colors.red.300',
// ^^^^^^^^^^^^ will be suggested
backgroundColor: 'var(--button-color)'
// ^^^^^^^^^^^^^^^^^^ will be suggested
})
```
---
# Theming
## Animation Styles
Define reusable animation css properties.
Animation styles focus solely on animations, allowing you to orchestrate animation properties.
## Defining Animation Styles
Animation styles are defined in the `animationStyles` property of the theme.
Here's an example of an animation style:
```js filename="animation-styles.ts"
import { defineAnimationStyles } from '@pandacss/dev'
export const animationStyles = defineAnimationStyles({
'slide-fade-in': {
value: {
transformOrigin: 'var(--transform-origin)',
animationDuration: 'fast',
'&[data-placement^=top]': {
animationName: 'slide-from-top, fade-in'
},
'&[data-placement^=bottom]': {
animationName: 'slide-from-bottom, fade-in'
},
'&[data-placement^=left]': {
animationName: 'slide-from-left, fade-in'
},
'&[data-placement^=right]': {
animationName: 'slide-from-right, fade-in'
}
}
}
})
```
> **Good to know:** The `value` property maps to style objects that will be applied to the element.
## Update the Config
To use the animation styles, we need to update the `config` object in the `panda.config.ts` file.
```js filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
import { animationStyles } from './animation-styles'
export default defineConfig({
theme: {
extend: {
animationStyles
}
}
})
```
This should automatically update the generated theme with the specified `animationStyles`. If this doesn't happen, you
can run the `panda codegen` command.
## Using Animation Styles
Now we can use the `animationStyle` property in our components.
```jsx
import { css } from '../styled-system/css'
function App() {
return (
This is an element with slide-fade-in animation style.
)
}
```
Take advantage of it in your conditions:
```ts
export const popoverSlotRecipe = defineSlotRecipe({
slots: anatomy.keys(),
base: {
content: {
_open: {
animationStyle: 'scale-fade-in'
},
_closed: {
animationStyle: 'scale-fade-out'
}
}
}
})
```
## Nesting animation styles
Animation styles support nested structures with a special `DEFAULT` key. This allows you to create variants of an
animation style while having a default fallback.
When you define a `DEFAULT` key within a nested animation style, you can reference the parent key directly to use the
default value.
```js filename="panda.config.ts"
export default defineConfig({
theme: {
extend: {
animationStyles: {
fade: {
DEFAULT: {
value: {
animationName: 'fade-in',
animationDuration: '300ms',
animationTimingFunction: 'ease-in-out'
}
},
slow: {
value: {
animationName: 'fade-in',
animationDuration: '600ms',
animationTimingFunction: 'ease-in-out'
}
},
fast: {
value: {
animationName: 'fade-in',
animationDuration: '150ms',
animationTimingFunction: 'ease-in-out'
}
}
}
}
}
}
})
```
Now you can use the default fade animation or specific speed variants:
```jsx
import { css } from '../styled-system/css'
function App() {
return (
Default fade speed
Slow fade
Fast fade
)
}
```
## Best Practices
### Avoid Overuse
To ensure the performance and readability of your design system, avoid overusing animations. Use them sparingly to
enhance user experience without overwhelming the user.
### Consistent Naming Conventions
We recommend using consistent naming conventions for animation styles. Here are common ideas on how to name animation
styles:
- Based on the type of animation (`slide`, `fade`, `bounce`)
- Based on the direction or trigger (`slide-from-top`, `fade-in`, `bounce-on-click`)
- Descriptive or functional names that explain the style's intended use (`modal-open`, `button-hover`, `alert-show`)
By following these guidelines, you can create a clear and maintainable animation system in your design.
---
## Custom Font
How to use custom fonts in your project.
Adding custom fonts to your application or website is a typical requirement for projects. Panda recommends using custom
fonts through CSS variables for consistency.
## Setup
### Next.js
Next.js provides a built-in automatic self-hosting for any font file by using the `next/font` module. It allows you to
conveniently use all Google Fonts and any local font with performance and privacy in mind.
Here's an example of how to load a local "Mona Sans" font and a Google Font "Fira Code" in your Next.js project.
```js filename="styles/font.ts"
import { Fira_Code } from 'next/font/google'
import localFont from 'next/font/local'
export const MonaSans = localFont({
src: '../fonts/Mona-Sans.woff2',
display: 'swap',
variable: '--font-mona-sans'
})
export const FiraCode = Fira_Code({
weight: ['400', '500', '700'],
display: 'swap',
subsets: ['latin'],
variable: '--font-fira-code'
})
```
> Ideally, you should load the font in the layout file.
Next, you need to add the font variables to your HTML document. You can do this using either the App Router or the Pages
Router.
#### App Router
```jsx filename="app/layout.tsx"
import { FiraCode, MonaSans } from '../styles/font'
export default function Layout(props) {
const { children } = props
return (
{children}
)
}
```
> **Note π¨:** By default, Next.js attaches the className for the fonts to the `` element, for panda to
> appropriately load fonts, update the code to attach the `className` to the `` element.
#### Pages Router
```jsx filename="pages/_app.tsx"
import { FiraCode, MonaSans } from '../styles/font'
export default function App({ Component, pageProps }) {
return (
<>
>
)
}
```
### Fontsource
[Fontsource](https://fontsource.org/) streamlines the process of integrating fonts into your web application.
To begin, install your desired font package:
```bash
pnpm add @fontsource-variable/fira-code
```
Next, import the font into your project:
```jsx
import '@fontsource-variable/fira-code'
```
Lastly, create a variable to use it as a token in the panda config
```css filename="styles/font.css"
:root {
--font-fira-code: 'Fira Code Variable', monospace;
}
```
### Vanilla CSS
You can leverage the native font-face CSS property to load custom fonts in your project.
```css
@font-face {
font-family: 'Mona Sans';
src: url('../fonts/Mona-Sans.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
```
Then alias the font names to css variables.
```css
:root {
--font-mona-sans: 'Mona Sans', sans-serif;
}
```
### Global Font Face
You can also define global font face in your panda config.
```js
export default defineConfig({
globalFontface: {
Fira: {
src: 'url(/fonts/fira.woff2) format("woff2")',
fontWeight: 400,
fontStyle: 'normal',
fontDisplay: 'swap'
}
}
})
```
You can also define multiple font sources for the same weight.
```js
export default defineConfig({
globalFontface: {
Fira: {
src: ['url(/fonts/fira.woff2) format("woff2")', 'url(/fonts/fira.woff) format("woff")'],
fontWeight: 400,
fontStyle: 'normal',
fontDisplay: 'swap'
}
}
})
```
You can also define multiple font weights.
```js
export default defineConfig({
globalFontface: {
Fira: [
{
src: 'url(/fonts/fira.woff2) format("woff2")',
fontWeight: 400,
fontStyle: 'normal',
fontDisplay: 'swap'
},
{
src: 'url(/fonts/fira-bold.woff2) format("woff2")',
fontWeight: 700,
fontStyle: 'normal',
fontDisplay: 'swap'
}
]
}
})
```
Then expose the font names to css variables.
```css
:root {
--font-fira-code: 'Fira Code Variable', monospace;
}
```
You can also use [globalVars](/docs/styling/writing-styles#global-vars) in your panda config to define the variables.
```js
export default defineConfig({
globalVars: {
'--font-fira-code': 'Fira Code Variable, monospace'
}
})
```
## Update Panda Config
```js
export default defineConfig({
theme: {
extend: {
tokens: {
fonts: {
fira: { value: 'var(--font-fira-code), Menlo, monospace' },
mona: { value: 'var(--font-mona-sans), sans-serif' }
}
}
}
}
})
```
## Use the custom fonts
```jsx
import { css } from '../styled-system/css'
function Page() {
return (
Mona Sans
Fira Code
)
}
```
---
## Layer Styles
Define reusable container styles properties.
Layer styles provide a way to create consistent and visually appealing elements.
- Color or text color
- Background color
- Border width and border color
- Box shadow
- Opacity
## Defining layer styles
Layer styles are defined in the `layerStyles` property of the theme.
Here's an example of a layer style:
```js filename="layer-styles.ts"
import { defineLayerStyles } from '@pandacss/dev'
const layerStyles = defineLayerStyles({
container: {
description: 'container styles',
value: {
background: 'gray.50',
border: '2px solid',
borderColor: 'gray.500'
}
}
})
```
> **Good to know:** The `value` property maps to style objects that will be applied to the element.
## Update the config
To use the layer styles, we need to update the `config` object in the `panda.config.ts` file.
```js filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
import { layerStyles } from './layer-styles'
export default defineConfig({
theme: {
extend: {
layerStyles
}
}
})
```
This should automatically update the generated theme the specified `layerStyles`. If this doesn't happen, you can run
the `panda codegen` command.
## Using layer styles
Now we can use `layerStyle` property in our components.
```jsx
import { css } from '../styled-system/css'
function App() {
return
This is inside a container style
}
```
## Nesting layer styles
Layer styles support nested structures with a special `DEFAULT` key. This allows you to create variants of a layer style
while having a default fallback.
When you define a `DEFAULT` key within a nested layer style, you can reference the parent key directly to use the
default value.
```js filename="panda.config.ts"
export default defineConfig({
theme: {
extend: {
layerStyles: {
card: {
DEFAULT: {
value: {
background: 'white',
border: '1px solid',
borderColor: 'gray.200',
borderRadius: 'md',
boxShadow: 'sm'
}
},
elevated: {
value: {
background: 'white',
border: 'none',
borderRadius: 'lg',
boxShadow: 'lg'
}
},
outlined: {
value: {
background: 'transparent',
border: '2px solid',
borderColor: 'gray.300',
borderRadius: 'md',
boxShadow: 'none'
}
}
}
}
}
}
})
```
Now you can use the default card style or specific variants:
```jsx
import { css } from '../styled-system/css'
function App() {
return (
Default card style
Elevated card
Outlined card
)
}
```
---
## Multi-Theme Tokens
Panda supports advance token definition beyond just light/dark mode; theming beyond just dark mode. You can define multi-theme tokens using nested conditions.
## Multi-Theme Tokens
Panda supports advance token definition beyond just light/dark mode; theming beyond just dark mode. You can define
multi-theme tokens using nested conditions.
Let's say your application supports a pink and blue theme, and each theme can have a light and dark mode. Let's see how
to model this in Panda.
We'll start by defining the following conditions for these theme and color modes:
```js
// panda.config.ts
const config = {
conditions: {
light: '[data-color-mode=light] &',
dark: '[data-color-mode=dark] &',
pinkTheme: '[data-theme=pink] &',
blueTheme: '[data-theme=blue] &'
}
}
```
> Conditions are a way to provide preset css selectors or media queries for use in your Panda project
Next, we'll define a `colors.text` semantic token for the pink and blue theme.
```js
// panda.config.ts
const theme = {
// ...
semanticTokens: {
colors: {
text: {
value: {
_pinkTheme: '{colors.pink.500}',
_blueTheme: '{colors.blue.500}'
}
}
}
}
}
```
Next, we'll modify `colors.text` to support light and dark color modes for each theme.
```js
// panda.config.ts
const theme = {
// ...
semanticTokens: {
colors: {
text: {
value: {
_pinkTheme: { base: '{colors.pink.500}', _dark: '{colors.pink.300}' },
_blueTheme: { base: '{colors.blue.500}', _dark: '{colors.blue.300}' }
}
}
}
}
}
```
Now, you can use the `text` token in your styles, and it will automatically change based on the theme and the color
scheme.
```jsx
// use pink and dark mode theme
Hello World
// use pink and light mode theme
Hello World
```
## Multi-Themes
The above example shows you how to define multi-theme tokens using nested conditions but you can also define clearly separated themes using the `themes` property in the config.
This allows you to apply a `theme` on multiple tokens at once, using data attributes and CSS variables.
> Theme variants can be applied using the `data-panda-theme` attribute with the theme key as the value.
```ts
// panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// ...
// main theme
theme: {
extend: {
tokens: {
colors: {
text: { value: 'green' }
}
},
semanticTokens: {
colors: {
body: {
value: {
base: '{colors.green.600}',
_osDark: '{colors.green.400}'
}
}
}
}
}
},
// alternative theme variants
themes: {
primary: {
tokens: {
colors: {
text: { value: 'red' }
}
},
semanticTokens: {
colors: {
muted: { value: '{colors.red.200}' },
body: {
value: {
base: '{colors.red.600}',
_osDark: '{colors.red.400}'
}
}
}
}
},
secondary: {
tokens: {
colors: {
text: { value: 'blue' }
}
},
semanticTokens: {
colors: {
muted: { value: '{colors.blue.200}' },
body: {
value: {
base: '{colors.blue.600}',
_osDark: '{colors.blue.400}'
}
}
}
}
}
}
})
```
### Pregenerating themes
By default, no additional theme variant is generated, you need to specify the specific themes you want to generate in
`staticCss.themes` to include them in the CSS output.
```ts
// panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// ...
staticCss: {
themes: ['primary', 'secondary']
}
})
```
This will generate the following CSS:
```css
@layer tokens {
:where(:root, :host) {
--colors-text: blue;
--colors-body: var(--colors-blue-600);
}
[data-panda-theme='primary'] {
--colors-text: red;
--colors-muted: var(--colors-red-200);
--colors-body: var(--colors-red-600);
}
@media (prefers-color-scheme: dark) {
:where(:root, :host) {
--colors-body: var(--colors-blue-400);
}
[data-panda-theme='primary'] {
--colors-body: var(--colors-red-400);
}
}
}
```
### Dynamically importing themes
An alternative way of applying a theme is by using the new `styled-system/themes` entrypoint where you can import the
themes expected CSS and apply them in your app.
> βΉοΈ The `styled-system/themes` will always contain every themes (tree-shaken if not used), whereas `staticCss.themes` only
> applies to the CSS output.
Each theme has a corresponding JSON file with a similar structure:
```json
{
"name": "primary",
"id": "panda-themes-primary",
"css": "[data-panda-theme=primary] { ... }"
}
```
#### Dynamically import a theme using its name
```ts
import { getTheme } from '../styled-system/themes'
const theme = await getTheme('red')
// ^? {
// name: "red";
// id: string;
// css: string;
// }
```
#### Inject the theme styles into the DOM:
```ts
import { injectTheme } from '../styled-system/themes'
const theme = await getTheme('red')
injectTheme(document.documentElement, theme) // this returns the injected style element
```
#### SSR example with NextJS:
```tsx
// app/layout.tsx
import { cookies } from 'next/headers'
import './globals.css'
import { ThemeName, getTheme } from '../../styled-system/themes'
export default async function RootLayout({
children
}: {
children: React.ReactNode
}) {
const store = cookies()
const themeName = store.get('theme')?.value as ThemeName
const theme = themeName && (await getTheme(themeName))
return (
{themeName && (
)}
{children}
)
}
```
```tsx
// app/page.tsx
'use client'
import { getTheme, injectTheme } from '../../styled-system/themes'
export default function Home() {
return (
<>
>
)
}
// Set a Cookie
function setCookie(cName: string, cValue: any, expDays: number) {
let date = new Date()
date.setTime(date.getTime() + expDays * 24 * 60 * 60 * 1000)
const expires = 'expires=' + date.toUTCString()
document.cookie = cName + '=' + cValue + '; ' + expires + '; path=/'
}
```
### Theme contract
Finally, you can create a theme contract to ensure that all themes have the same structure:
```ts
import { defineThemeContract } from '@pandacss/dev'
const defineTheme = defineThemeContract({
tokens: {
colors: {
red: { value: '' } // theme implementations must have a red color
}
}
})
defineTheme({
tokens: {
colors: {
// ^^^^ β Property 'red' is missing in type '{}' but required in type '{ red: { value: string; }; }'
//
// β fixed with
// red: { value: 'red' },
}
}
})
```
---
## Spec
Document your design system in Panda.
A spec is a readable JSON representation of the theme structure in your panda config. They're designed to be used for
**documentation purposes** in your own docs websites and Storybook. Some common use cases:
- Document tokens in your theme (colors, spacing, fonts, etc.)
- Showcase the recipes and slot recipes (include their variants and default variants)
- Document the typography styles and layer styles
## Spec Generation
Spec generation is not part of the current v2 CLI surface yet. The previous `panda spec` command is not ported.
## Spec Output
The spec command generates a set of JSON files that represent your entire design system. Here's an example of the output
structure:
```sh
styled-system/
βββ specs/ # Generated documentation-ready spec files
βββ animation-styles.json # Animation style presets (empty if none defined)
βββ color-palette.json # List of palette names (blue, teal, etc.)
βββ conditions.json # Condition selectors (_hover, _focus, ...)
βββ keyframes.json # Keyframe definitions (spin, ping, ...)
βββ layer-styles.json # Layer style definitions (card, overlay, ...)
βββ patterns.json # Pattern definitions + their properties
βββ recipes.json # Component recipes + variants + defaults
βββ semantic-tokens.json # Semantic tokens with conditions (base, dark, etc.)
βββ text-styles.json # Text style definitions (xs, sm, md, ...)
βββ tokens.json # Raw design tokens grouped by category
```
Below is a breakdown of what each file contains and how it can be used.
### `tokens.json`
#### Structure
This file contains an array of raw design tokens grouped by category
```json
{
"type": "tokens",
"data": [
{
"type": "colors",
"values": [
{
"name": "purple.800",
"value": "#6b21a8",
"cssVar": "var(--colors-purple-800)"
}
],
"tokenFunctionExamples": ["token('colors.purple.800')", "token.var('colors.purple.800')"],
"functionExamples": ["css({ color: 'purple.800' })"],
"jsxExamples": [""]
}
]
}
```
Each token in the `values` array includes:
- **name**: token key (e.g., 2xs, md, primary)
- **value**: resolved CSS value (e.g., "1rem", "#F6E458")
- **cssVar**: the generated CSS custom property
- **tokenFunctionExamples**: examples of how to use the token in the `token` function
- **functionExamples**: examples of how to use the token in the `css` function
- **jsxExamples**: examples of how to use the token in JSX
#### Usage
Here's an example of how to document color tokens:
```tsx
import { grid } from 'styled-system/patterns'
import tokens from 'styled-system/specs/tokens.json'
const Demo = () => {
const colors = tokens.data.find(token => token.type === 'colors')
return (
{colors?.values.map(color => (
{color.name}
{color.value}
))}
)
}
```
Your color token documentation should look similar to this:

### `semantic-tokens.json`
#### Structure
This file contains an array of semantic token definitions grouped by category, with conditional values for different
modes (e.g., light/dark).
```json
{
"type": "semantic-tokens",
"data": [
{
"type": "colors",
"values": [
{
"name": "bg",
"values": [
{ "value": "{colors.white}", "condition": "base" },
{ "value": "{colors.dark}", "condition": "dark" }
],
"cssVar": "var(--colors-bg)"
}
],
"tokenFunctionExamples": ["token('colors.bg')", "token.var('colors.bg')"],
"functionExamples": ["css({ color: 'bg' })"],
"jsxExamples": [""]
}
]
}
```
Each semantic token in the `values` array includes:
- **name**: the semantic token key (e.g., `bg`, `fg.muted`, `accent`)
- **values**: an array of conditional mappings, each with:
- **value**: the resolved token reference (e.g., `{colors.white}`)
- **condition**: the condition name (e.g., `base`, `dark`)
- **cssVar**: the generated CSS custom property
- **tokenFunctionExamples**: examples of how to use the token in the `token` function
- **functionExamples**: examples of how to use the token in the `css` function
- **jsxExamples**: examples of how to use the token in JSX
#### Usage
Here's an example of how to document semantic color tokens with their conditional values:
```tsx
import semanticTokens from 'styled-system/specs/semantic-tokens.json'
const Demo = () => {
const colors = semanticTokens.data.find(token => token.type === 'colors')
return (
{colors?.values.map(token => (
{token.name}
{token.cssVar}
{token.values.map(({ condition, value }) => (
{condition}
{value}
))}
))}
)
}
```
### `recipes.json`
#### Structure
This file contains an array of recipe definitions for styling components with variant support.
```json
{
"type": "recipes",
"data": [
{
"name": "button",
"description": "A button style",
"variants": {
"shape": ["square", "circle"],
"color": ["main", "black", "white"],
"size": ["sm", "md", "lg"]
},
"defaultVariants": {
"shape": "square",
"color": "main",
"size": "md"
},
"functionExamples": ["button({ shape: 'square' })", "button({ color: 'main' })", "button({ size: 'sm' })"],
"jsxExamples": ["", "", ""]
}
]
}
```
Each recipe in the `data` array includes:
- **name**: the recipe name (e.g., `button`, `card`)
- **description**: optional description of what the recipe does
- **variants**: an object where each key is a variant name and each value is an array of allowed options
- **defaultVariants**: an object defining the default option for each variant
- **functionExamples**: examples of how to use the recipe function
- **jsxExamples**: examples of how to use the recipe in JSX
#### Usage
Here's an example of how to document a button recipe within a table:
```tsx
import recipes from 'styled-system/specs/recipes.json'
const Demo = () => {
const buttonRecipe = recipes.data.find(recipe => recipe.name === 'button')
const defaultVariants = buttonRecipe?.defaultVariants || {}
return (
{(options as string[]).map(option => (
{option}
))}
{defaultVariants[key as keyof typeof defaultVariants] || 'none'}
))}
)
}
```
Your button recipe documentation should look similar to this:

### `color-palette.json`
Contains a list of all color names defined in your palette.
- `values` β an array of color keys (e.g., `"blue"`, `"teal"`, `"pink"` and more).
- Does not contain the color scales (e.g., 500, 600); it only lists the available palette names.
- Optional examples: `functionExamples` and `jsxExamples`
### `text-styles.json`
#### Structure
This file contains an array of text style definitions for typography presets.
```json
{
"type": "text-styles",
"data": [
{
"name": "xl",
"functionExamples": ["css({ textStyle: 'xl' })"],
"jsxExamples": [""]
}
]
}
```
Each text style in the `data` array includes:
- **name**: the text style name (e.g., `xs`, `sm`, `md`, `lg`, `xl`, `2xl`)
- **functionExamples**: examples of how to use the text style in the `css` function
- **jsxExamples**: examples of how to use the text style in JSX
#### Usage
Here's an example of how to document text styles with a visual preview:
```tsx
import textStyles from 'styled-system/specs/text-styles.json'
const Demo = () => {
return (
{textStyles.data.map(style => (
{style.name}
The quick brown fox jumps over the lazy dog
))}
)
}
```
### `layer-styles.json`
#### Structure
This file contains an array of layer style definitions for visual presets (backgrounds, shadows, borders, etc.).
```json
{
"type": "layer-styles",
"data": [
{
"name": "offShadow",
"functionExamples": ["css({ layerStyle: 'offShadow' })"],
"jsxExamples": [""]
}
]
}
```
Each layer style in the `data` array includes:
- **name**: the layer style name (e.g., `card`, `overlay`, `offShadow`)
- **functionExamples**: examples of how to use the layer style in the `css` function
- **jsxExamples**: examples of how to use the layer style in JSX
#### Usage
Here's an example of how to document layer styles with a visual preview:
```tsx
import layerStyles from 'styled-system/specs/layer-styles.json'
const Demo = () => {
return (
{layerStyles.data.map(style => (
{style.name}
))}
)
}
```
### `animation-styles.json`
Contains an array of animation style entries. Each entry includes:
- `name` β the animation preset name
- Animation properties such as `duration`, `timingFunction`, etc.
- Optional examples: `functionExamples` and `jsxExamples`
If no animation styles are configured in your `panda.config.ts` file , this file will contain an empty data array.
### `keyframes.json`
#### Structure
This file contains an array of keyframe definitions for CSS animations.
```json
{
"type": "keyframes",
"data": [
{
"name": "spin",
"functionExamples": ["css({ animationName: 'spin' })", "css({ animation: 'spin 1s ease-in-out infinite' })"],
"jsxExamples": ["", ""]
}
]
}
```
Each keyframe in the `data` array includes:
- **name**: the keyframe name (e.g., `spin`, `ping`, `pulse`, `bounce`, `fade-in`)
- **functionExamples**: examples of how to use the keyframe in the `css` function
- **jsxExamples**: examples of how to use the keyframe in JSX
#### Usage
Here's an example of how to document keyframes with animated previews:
```tsx
import keyframes from 'styled-system/specs/keyframes.json'
const Demo = () => {
return (
{keyframes.data.map(keyframe => (
{keyframe.name}
))}
)
}
```
### `patterns.json`
#### Structure
This file contains an array of pattern definitions for layout utilities.
```json
{
"type": "patterns",
"data": [
{
"name": "flex",
"jsx": "Flex",
"properties": [
{ "name": "align", "type": "SystemProperties['alignItems']" },
{ "name": "justify", "type": "SystemProperties['justifyContent']" },
{ "name": "direction", "type": "SystemProperties['flexDirection']" },
{ "name": "wrap", "type": "SystemProperties['flexWrap']" }
],
"functionExamples": ["flex({ align: 'center' })", "flex({ justify: 'space-between' })"],
"jsxExamples": ["", ""]
}
]
}
```
Each pattern in the `data` array includes:
- **name**: the pattern function name (e.g., `flex`, `grid`, `stack`, `center`)
- **jsx**: the JSX component name for the pattern (e.g., `Flex`, `Grid`, `Stack`)
- **properties**: an array of pattern-specific props, each with:
- **name**: the prop name (e.g., `align`, `justify`, `gap`)
- **type**: the TypeScript type for the prop
- **defaultValue**: optional default value for the prop
- **functionExamples**: examples of how to use the pattern function
- **jsxExamples**: examples of how to use the pattern in JSX
#### Usage
Here's an example of how to document patterns with their properties:
```tsx
import patterns from 'styled-system/specs/patterns.json'
const Demo = () => {
return (
)
}
```
### `conditions.json`
Contains an array of condition entries. Each entry includes:
- `name` β the condition key used in style objects (e.g.,`_hover`, `_focus`, `_focusWithin`)
- `value` β the CSS selector or media query that the condition maps to
- Optional examples: `functionExamples` and `jsxExamples`
## FAQs
### Can I edit the spec files directly?
**No.** The spec files are **generated** filesβyou should **not** edit your design tokens, recipes, or theme directly in
these files. All configuration changes must be made in your `panda.config.ts` file. The spec files exist purely for
documentation and visualization purposes.
---
## Using Panda Studio
Document your design system visually using Panda Studio.
The standalone `@pandacss/studio` Astro app is removed in v2. A lighter, CLI-generated studio is coming, see
[Studio in v2](/docs/theming/studio-v2). The page below is kept as a reference for the v1 app.
### Panda Studio
Panda Studio is a visual interface for exploring and understanding your entire design system. It provides a read-only
view of your tokens, semantic tokens, recipes, patterns, conditions, and more.

If you don't want to manually generate spec files or build a documentation website, Panda Studio is the easiest option.
- Studio does not require separate spec generation.
- Studio generates and visualizes your design system automatically.
- Studio acts as a fully-featured documentation tool without writing any documentation code.
> Spec files are primarily for custom documentation setups and Storybook integrations. Panda Studio is for teams who
> want instant documentation.
## Panda Studio Setup
To use panda studio, first install it:
```bash
pnpm i @pandacss/studio
```
Next, launch it locally using:
```bash
pnpm panda studio
```
This starts a local server and launches an interactive dashboard showing all your design system elements. Since Studio
reads your theme configuration directly, any changes to your `panda.config.ts` will appear automatically the next time
you run it.
You can also deploy the studio as a standalone design system portal for your team.
---
## Panda Studio in v2
A CLI-generated token viewer is coming to v2 β panda studio, plus getTokenJson, getTokenHtml, and getTokenCss. Not in the beta yet.
Coming soon. `panda studio` isn't in the v2 beta yet β it's still in progress. This page previews the shape so you can
plan around it. For the shipped version, see [Panda Studio](/docs/theming/studio).
## What's coming
The v1 [Panda Studio](/docs/theming/studio) is a separate Astro app β install a framework, boot a server, self-host to
share. The Rust migration removed that pipeline, so v2 has no built-in way to look at your tokens today.
`panda studio` brings token visualization back without the Astro weight. It writes `styled-system/studio` into your
project and boots a live viewer:
```sh
panda studio # emit styled-system/studio + boot the viewer
panda studio --port 4137
panda studio --css ./studio.css # render the viewer with your own stylesheet
```
`styled-system/studio` exports three framework-agnostic functions, modeled on [Tiptap](https://tiptap.dev)'s `getHTML` /
`getJSON` β small functions that each hand you one thing:
- **`getTokenJson({ category, query })`** β your tokens as data. Filter by category, search by query, or omit both for
everything. Each token is `{ category, path, name, value, conditions? }`, values fully resolved, with semantic tokens
carrying their per-condition and per-theme values.
- **`getTokenHtml({ tokens })`** β semantic, style-free HTML. Names and values as text, everything else on `data-`
attributes, all escaped. Drop it into any framework (`dangerouslySetInnerHTML`, `v-html`, `innerHTML`) or write it to
a file.
- **`getTokenCss(yourStylesheet)`** β takes your CSS and wires it up. It exposes each token's value as a `--pds-value`
variable on the matching `[data-value]` element, then appends your stylesheet, so `var(--pds-value)` resolves to real
token values.
Panda ships the content and the wiring, not the design. There's no default theme and no bundled stylesheet β the grid,
swatches, and type are yours. A bare `panda studio` is plain until you pass `--css`.
```tsx
import { getTokenJson, getTokenHtml, getTokenCss } from '../styled-system/studio'
import myCss from './studio.css?raw'
function Colors() {
const [tokens, setTokens] = useState(getTokenJson({ category: 'colors' }))
return (
)
}
```
The generated module is self-contained β the runtime is inlined with your tokens baked in, so it imports nothing at
runtime.
## See also
- [Panda Studio](/docs/theming/studio) for the current, shipped version.
- [Upgrading to v2](/docs/styling/upgrading-to-v2) for the broader rewrite this belongs to.
---
## Text Styles
Define reusable typography css properties.
Text styles allows you to define textual css properties. The common properties are:
- The font family, weight, size
- Line height
- Letter spacing
- Text Decoration (strikethrough and underline)
- Text Transform (uppercase, lowercase, and capitalization)
## Defining text styles
Text styles are defined in the `textStyles` property of the theme.
Here's an example of a text style:
```js filename="text-styles.ts"
import { defineTextStyles } from '@pandacss/dev'
export const textStyles = defineTextStyles({
body: {
description: 'The body text style - used in paragraphs',
value: {
fontFamily: 'Inter',
fontWeight: '500',
fontSize: '16px',
lineHeight: '24px',
letterSpacing: '0',
textDecoration: 'None',
textTransform: 'None'
}
}
})
```
> **Good to know:** The `value` property maps to style objects that will be applied to the text.
## Update the config
To use the text styles, we need to update the `config` object in the `panda.config.ts` file.
```js filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
import { textStyles } from './text-styles'
export default defineConfig({
theme: {
extend: {
textStyles
}
}
})
```
This should automatically update the generated theme the specified `textStyles`. If this doesn't happen, you can run the
`panda codegen` command.
## Using text styles
Now we can use `textStyle` property in our components.
```jsx
import { css } from '../styled-system/css'
function App() {
return
This is a paragraph from Panda with the body text style.
}
```
## Nesting text styles
Text styles support nested structures with a special `DEFAULT` key. This allows you to create variants of a text style
while having a default fallback.
When you define a `DEFAULT` key within a nested text style, you can reference the parent key directly to use the default
value.
```js filename="panda.config.ts"
export default defineConfig({
theme: {
extend: {
textStyles: {
heading: {
DEFAULT: {
value: {
fontFamily: 'Inter',
fontWeight: 'bold',
fontSize: '1.5rem',
lineHeight: '1.2'
}
},
h1: {
value: {
fontFamily: 'Inter',
fontWeight: 'bold',
fontSize: '2.5rem',
lineHeight: '1.1'
}
},
h2: {
value: {
fontFamily: 'Inter',
fontWeight: 'bold',
fontSize: '2rem',
lineHeight: '1.15'
}
}
}
}
}
}
})
```
Now you can use the default heading style or specific variants:
```jsx
import { css } from '../styled-system/css'
function App() {
return (
Main Title
Subtitle
Uses DEFAULT variant
)
}
```
## Best Practices
### Avoid layout properties
To ensure the consistency of your design system, avoid applying layout properties (like margin, padding, etc.) or color
properties (background, colors, etc.) to the text styles.
### Naming conventions
We recommend using the same text style names used by designers on your team. Here are common ideas on how to name text
styles:
- Sized-based naming system (`xs`, `sm`, `md`, `lg`, `xl`)
- Semantic naming system that corresponds to respective html tags in production (`caption`, `paragraph`, `h1`, `h2`)
- Descriptive or functional naming system that explains the style's intended use (`alert`, `modal-header`,
`button-label`)
---
## Theme
Customizing the default theme
Panda comes with a default theme that is used to generate the utilities for your project. You can customize this theme
to match your design requirements.
## Breakpoints
Use the `breakpoints` key in the `theme` section of your Panda config file to customize the default breakpoints.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
breakpoints: {
'3xl': '1800px'
}
}
}
})
```
Panda ships with the following breakpoints by default:
## Tokens
### Colors
Use the `colors` key in the `token` section of your Panda config file to customize the default color values.
> We recommend using numeric ranges from `50` to `900`
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
tokens: {
colors: {
brand: { value: '#EA8433' }
}
}
}
}
})
```
Panda comes with a handful of colors picked from the amazing Tailwind color palette
### Spacing
Use the `spacing` key in the theme section of your Panda config file to customize the default spacing values.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
tokens: {
spacing: {
gutter: { value: '32px' }
}
}
}
}
})
```
Panda ships with the following spacing tokens by default:
### Border Radius
Use the `radii` key in the theme section of your Panda config file to customize the default border radius values.
### Shadows
Use the `shadows` key in the theme section of your Panda config file to customize the default box shadows values.
Panda ships with the following shadows by default:
### Sizing
Use the `sizes` key in the theme section of your Panda config file to customize the default sizing values.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
tokens: {
sizes: {
icon: { value: '24px' }
}
}
}
}
})
```
Panda ships with the following sizing tokens by default, in addition with the values from the default Panda
[spacing](#spacing) tokens:
### Fonts
Use the `fonts` key in the theme object to customize the default font families.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
tokens: {
fonts: {
marketing: { value: 'Inter Variable' }
}
}
}
}
})
```
Panda ships with the following font families tokens by default:
### Font Sizes
Use the `fontSizes` key in the theme object to customize the default font sizes.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
tokens: {
fontSizes: {
big: { value: '80px' }
}
}
}
}
})
```
Panda ships with the following font size tokens by default:
## Keyframes
Use the `keyframes` key in the `theme` section of your Panda config file to customize the default keyframes.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
extend: {
keyframes: {
fadein: {
'0%': { opacity: '0' },
'100%': { opacity: '1' }
},
fadeout: {
'0%': { opacity: '1' },
'100%': { opacity: '0' }
}
}
}
}
})
```
Panda ships with the following keyframes by default:
## Minimal setup
If you want to use Panda with the bare minimum, without any of the defaults, you can read more about it
[here](/docs/design-systems/minimal-setup)
---
## Tokens
Design tokens are the platform-agnostic way to manage design decisions in your application or website.
Design tokens provide a platform-agnostic way to manage design decisions through key-value pairs that describe
fundamental visual styles.
> Design tokens in Panda are largely influenced by the [W3C Token Format](https://tr.designtokens.org/format/).
A design token consists of the following properties:
- `value`: The value of the token. This can be any valid CSS value.
- `description`: An optional description of what the token can be used for.
## Core Tokens
Tokens are defined in the `panda.config` file under the `theme` key
```js filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
// ππ» Define your tokens here
extend: {
tokens: {
colors: {
primary: { value: '#0FEE0F' },
secondary: { value: '#EE0F0F' }
},
fonts: {
body: { value: 'system-ui, sans-serif' }
}
}
}
}
})
```
> β οΈ Token values need to be nested in an object with a `value` key. This is to allow for additional properties like
> `description` and more in the future.
After defining tokens, you can use them in authoring components and styles.
```jsx
import { css } from '../styled-system/css'
function App() {
return (
Hello World
)
}
```
You can also add an optional description to your tokens. This will be used in the autogenerate token documentation.
```js {8}
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
tokens: {
colors: {
danger: {
value: '#EE0F0F',
description: 'Color for errors'
}
}
}
}
})
```
## Semantic Tokens
Semantic tokens are tokens that are designed to be used in a specific context. In most cases, the value of a semantic
token references to an existing token.
> To reference a value in a semantic token, use the `{}` syntax.
For example, assuming we've defined the following tokens:
- `red` and `green` are raw tokens that define the color red and green.
- `danger` and `success` are semantic tokens that reference the `red` and `green` tokens.
```js
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {
tokens: {
colors: {
red: { value: '#EE0F0F' },
green: { value: '#0FEE0F' }
}
},
semanticTokens: {
colors: {
danger: { value: '{colors.red}' },
success: { value: '{colors.green}' }
}
}
}
})
```
> β οΈ Semantic Token values need to be nested in an object with a `value` key. This is to allow for additional properties
> like `description` and more in the future.
Semantic tokens can also be changed based on the [conditions](/docs/styling/conditional-styles) like light and dark
modes.
For example, if you want a color to change automatically based on light or dark mode.
```js
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// ...
theme: {
semanticTokens: {
colors: {
danger: {
value: { base: '{colors.red}', _dark: '{colors.darkred}' }
},
success: {
value: { base: '{colors.green}', _dark: '{colors.darkgreen}' }
}
}
}
}
})
```
> NOTE π¨: The conditions used in semantic tokens must be an at-rule or parent selector
> [condition](/docs/styling/conditional-styles#reference).
## Token Nesting
Tokens can be nested to create a hierarchy of tokens. This is useful when you want to group tokens together.
> Tip: You can use the `DEFAULT` key to define the default value of a nested token.
```js
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// ...
theme: {
semanticTokens: {
colors: {
bg: {
DEFAULT: { value: '{colors.gray.100}' },
muted: { value: '{colors.gray.100}' }
}
}
}
}
})
```
This allows the use of the `bg` token in the following ways:
```jsx
import { css } from '../styled-system/css'
function App() {
return (
Hello World
)
}
```
## Token Types
Panda supports the following token types:
### Colors
Colors have meaning and support the purpose of the content, communicating things like hierarchy of information, and
states. It is mostly defined as a string value or reference to other tokens.
```jsx
const theme = {
tokens: {
colors: {
red: { 100: { value: '#fff1f0' } }
}
}
}
```
### Gradients
Gradient tokens represent a smooth transition between two or more colors. Its value can be defined as a string or a
composite value.
```ts
type Gradient =
| string
| {
type: 'linear' | 'radial'
placement: string | number
stops:
| Array<{
color: string
position: number
}>
| Array
}
```
`placement` is either a keyword (`to right`, `circle at center`) or a number, which is read as an angle in degrees. A
stop `position` is a percentage along the gradient, from `0` to `100`.
```jsx
const theme = {
tokens: {
gradients: {
// string value
simple: { value: 'linear-gradient(to right, red, blue)' },
// composite value
primary: {
value: {
type: 'linear',
placement: 'to right',
stops: ['red', 'blue']
}
},
// angled placement, with positioned stops
angled: {
value: {
type: 'linear',
placement: 45,
stops: [
{ color: 'red', position: 0 },
{ color: 'blue', position: 100 }
]
}
}
}
}
}
```
The `angled` token above emits `linear-gradient(45deg, red 0%, blue 100%)`.
### Sizes
Size tokens represent the width and height of an element. Its value is defined as a string.
```jsx
const theme = {
tokens: {
sizes: {
sm: { value: '12px' }
}
}
}
```
> Size tokens are typically used in `width`, `height`, `min-width`, `max-width`, `min-height`, `max-height` properties.
### Spacings
Spacing tokens represent the margin and padding of an element. Its value is defined as a string.
```jsx
const theme = {
tokens: {
spacing: {
sm: { value: '12px' }
}
}
}
```
> Spacing tokens are typically used in `margin`, `padding`, `gap`, and `{top|right|bottom|left}` properties.
### Fonts
Font tokens represent the font family of a text element. Its value is defined as a string or an array of strings.
```jsx
const theme = {
tokens: {
fonts: {
body: { value: 'Inter, sans-serif' },
heading: { value: ['Roboto Mono', 'sans-serif'] }
}
}
}
```
> Font tokens are typically used in `font-family` property.
### Font Sizes
Font size tokens represent the size of a text element. Its value is defined as a string.
```jsx
const theme = {
tokens: {
fontSizes: {
sm: { value: '12px' }
}
}
}
```
> Font size tokens are typically used in `font-size` property.
### Font Weights
Font weight tokens represent the weight of a text element. Its value is defined as a string.
```jsx
const theme = {
tokens: {
fontWeights: {
bold: { value: '700' }
}
}
}
```
> Font weight tokens are typically used in `font-weight` property.
### Letter Spacings
Letter spacing tokens represent the spacing between letters in a text element. Its value is defined as a string.
```jsx
const theme = {
tokens: {
letterSpacings: {
wide: { value: '0.1em' }
}
}
}
```
> Letter spacing tokens are typically used in `letter-spacing` property.
### Line Heights
Line height tokens represent the height of a line of text. Its value is defined as a string.
```jsx
const theme = {
tokens: {
lineHeights: {
normal: { value: '1.5' }
}
}
}
```
> Line height tokens are typically used in `line-height` property.
### Radii
Radii tokens represent the radius of a border. Its value is defined as a string.
```jsx
const theme = {
tokens: {
radii: {
sm: { value: '4px' }
}
}
}
```
> Radii tokens are typically used in `border-radius` property.
### Borders
A border is a line surrounding a UI element. You can define them as string values or as a composite value
```jsx
const theme = {
tokens: {
borders: {
// string value
subtle: { value: '1px solid red' },
// string value with reference to color token
danger: { value: '1px solid {colors.red.400}' },
// composite value
accent: { value: { width: '1px', color: 'red', style: 'solid' } }
}
}
}
```
> Border tokens are typically used in `border`, `border-top`, `border-right`, `border-bottom`, `border-left`, `outline`
> properties.
### Border Widths
Border width tokens represent the width of a border. Its value is defined as a string.
```jsx
const theme = {
tokens: {
borderWidths: {
thin: { value: '1px' },
thick: { value: '2px' },
medium: { value: '1.5px' }
}
}
}
```
### Shadows
Shadow tokens represent the shadow of an element. Its value is defined as single or multiple values containing a string
or a composite value.
```ts
type CompositeShadow = {
offsetX: number
offsetY: number
blur: number
spread: number
color: string
inset?: boolean
}
type Shadow = string | CompositeShadow | string[] | CompositeShadow[]
```
```jsx
const theme = {
tokens: {
shadows: {
// string value
subtle: { value: '0 1px 2px 0 rgba(0, 0, 0, 0.05)' },
// composite value
accent: {
value: {
offsetX: 0,
offsetY: 4,
blur: 4,
spread: 0,
color: 'rgba(0, 0, 0, 0.1)'
}
},
// multiple string values
realistic: {
value: ['0 1px 2px 0 rgba(0, 0, 0, 0.05)', '0 1px 4px 0 rgba(0, 0, 0, 0.1)']
}
}
}
}
```
> Shadow tokens are typically used in `box-shadow` property.
### Easings
Easing tokens represent the easing function of an animation or transition. Its value is defined as a string or an array
of values representing the cubic bezier.
```jsx
const theme = {
tokens: {
easings: {
// string value
easeIn: { value: 'cubic-bezier(0.4, 0, 0.2, 1)' },
// array value
easeOut: { value: [0.4, 0, 0.2, 1] }
}
}
}
```
> Ease tokens are typically used in `transition-timing-function` property.
### Opacity
Opacity tokens help you set the opacity of an element.
```js
const theme = {
tokens: {
opacity: {
50: { value: 0.5 }
}
}
}
```
> Opacity tokens are typically used in `opacity` property.
### Z-Index
This token type represents the depth of an element's position on the z-axis.
```jsx
const theme = {
tokens: {
zIndex: {
modal: { value: 1000 }
}
}
}
```
> Z-index tokens are typically used in `z-index` property.
### Assets
Asset tokens represent a url or svg string. Its value is defined as a string or a composite value.
```ts
type CompositeAsset = { type: 'url' | 'svg'; value: string }
type Asset = string | CompositeAsset
```
```js
const theme = {
tokens: {
assets: {
logo: {
value: { type: 'url', value: '/static/logo.png' }
},
checkmark: {
value: { type: 'svg', value: '' }
}
}
}
}
```
> Asset tokens are typically used in `background-image` property.
### Durations
Duration tokens represent the length of time in milliseconds an animation or animation cycle takes to complete. Its
value is defined as a string.
```jsx
const theme = {
tokens: {
durations: {
fast: { value: '100ms' }
}
}
}
```
> Duration tokens are typically used in `transition-duration` and `animation-duration` properties.
### Animations
Animation tokens represent a keyframe animation. Its value is defined as a string value.
```jsx
const theme = {
tokens: {
animations: {
spin: {
value: 'spin 1s linear infinite'
}
}
}
}
```
> Animation tokens are typically used in `animation` property.
### Aspect Ratios
Aspect ratio tokens represent the aspect ratio of an element. Its value is defined as a string.
```js
const theme = {
tokens: {
aspectRatios: {
'1:1': { value: '1 / 1' },
'16:9': { value: '16 / 9' }
}
}
}
```
### Cursor
Cursor tokens define the style of the mouse pointer when it hovers over a specific element or area. These tokens
represent the visual behavior of interactions, indicating actions such as clickable areas, draggable elements, or
loading states. Their value is defined as a string.
```js
const theme = {
tokens: {
cursor: {
click: { value: 'pointer' },
disabled: { value: 'not-allowed' },
// custom value
custom: { value: 'url(cursor.svg), auto' }
}
}
}
```
## Token Helpers
To help defining tokens in a type-safe way, you can use the tokens
[Config Functions](/docs/design-systems/config-functions#token-creators).
## CSS variables
The generated CSS variables will be scoped using the `cssVarRoot` selector defined in the config.
```js
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
cssVarRoot: ':where(:root, :host)'
// ...
})
```
This will generate a CSS file similar to the following:
```css
:where(:root, :host) {
--colors-primary: #0fee0f;
--colors-secondary: #ee0f0f;
/* ... */
}
```
You can also define type-safe CSS variables using [globalVars](/docs/styling/writing-styles#property-conflicts).
---
## Using Tokens
There are various ways to consume Panda tokens depending on your need at that point in time.
Learn the various ways to consume Panda tokens in your project.
## Style Properties
The recommended way to consume your tokens is in the `css` function or style props.
```jsx
import { css } from '../styled-system/css'
const App = () => (
)
```
## Composite values
Some CSS properties like `border`, `box-shadow` allow you to specify multiple properties in its value. Panda allows you
to reference tokens in these composite values by using either the `token()` string function (similar to the JS
equivalent) or the token reference syntax `{path.to.token}` (similar to the semantic tokens equivalent).
The `token()` function is useful when you need to provide a fallback value. The token reference syntax is useful when
you don't need a fallback value or prefer using a more concise syntax.
{/* */}
```js
import { css } from '../styled-system/css'
const className = css({ border: '1px solid token(colors.red.400)' })
```
You can also provide a fallback value.
```js
import { css } from '../styled-system/css'
const className = css({ border: '1px solid token(colors.red.400, red)' })
```
```js
import { css } from '../styled-system/css'
const className = css({ border: '1px solid {colors.red.400}' })
```
{/* */}
You can also use it in media queries or any other CSS at-rule.
{/* */}
```js
import { css } from '../styled-system/css'
const className = css({
'@media screen and (min-width: token(sizes.4xl))': {
color: 'green.400'
}
})
```
```js
import { css } from '../styled-system/css'
const className = css({
'@media screen and (min-width: {sizes.4xl})': {
color: 'green.400'
}
})
```
{/* */}
## Vanilla JS
Use the generated `token` function to query design tokens in your project. This is useful if you need direct access to
your design tokens in the `style` attribute or when using CSS-in-JS libraries like `styled-components` or
`@emotion/styled`
> This approach is useful for incrementally adopting Panda in existing projects or
> [dynamic styling](/docs/styling/dynamic-styling#using-token)
### Style Attribute
```tsx filename="src/App.tsx"
import { token } from '../styled-system/tokens'
function App() {
return (
)
}
```
Each of your design tokens will be available in the generated `/tokens` folder. It looks like this:
```js filename="styled-system/tokens.ts"
const tokens = {
// ...
'colors.blue.200': {
value: '#bfdbfe',
variable: 'var(--colors-blue-200)'
}
// ...
}
```
- The `token()` function returns the raw value of the token.
- The `token.var()` function returns the CSS custom property used to reference the token.
Both functions are typesafe and expect a known dot-separated token path, they also accept a fallback value as a second
argument.
Using the example above, `token('colors.blue.200')` would return `#bfdbfe` and `token.var('colors.blue.200')` would
return `var(--colors-blue-200)`.
### Styled Components
```tsx
import styled from 'styled-components'
const Button = styled.button`
background: ${token('colors.blue.200')};
`
```
### Emotion
```tsx
import styled from '@emotion/styled'
const Button = styled.button`
background: ${token('colors.blue.200')};
`
```
---
# Design Systems
## Avoiding CSS collisions
What happens when two presets define a token or recipe under the same name in a single build, and how to catch it.
This page is about a single Panda build that combines more than one source of tokens and recipes, your own config
plus one or more [presets](/docs/design-systems/presets). It's a different problem from [Federated
Micro-Frontends](/docs/design-systems/federated-microfrontends), which covers multiple separately-built bundles
loaded onto the same page at runtime. If your setup is the federated case, `prefix` is the fix, and that guide covers
it in depth. This page covers what happens earlier, before any of that CSS is even emitted.
## The collision
[Presets merge in order, and later entries win on conflicts](/docs/design-systems/presets#extend-the-preset-in-the-app).
That's fine when you're intentionally overriding one preset's tokens with another's, it's the mechanism `theme.extend`
relies on. It's a problem when it happens by accident: two presets you didn't expect to overlap both define a
`button` recipe, or both define a `brand` color token, with different values.
```ts filename="panda.config.ts"
export default defineConfig({
presets: ['@acme/marketing-preset', '@acme/product-preset']
})
```
If both presets define `theme.recipes.button`, whichever preset is listed last wins, completely. There's no error,
no warning, and no partial merge of the two recipes, one definition silently replaces the other. The same is true for
a token defined in both presets, or in a preset and your own `theme` (not `theme.extend`).
## Check whether you actually have one
Panda doesn't detect this for you at build time, since it can't know which name collisions are intentional overrides
and which are accidents. Run `panda debug` and open `styled-system/debug/config.json`:
```bash
panda debug
```
Search that file for the recipe or token name you're unsure about. There's only ever one entry per name in the
merged output, so if it's there at all, check whether its definition (its variants, its value) actually matches the
preset you expected it to come from. If it doesn't, the other preset won. See
[Debugging](/docs/reference/debugging#panda-debug) for what else `panda debug` outputs.
## Fix it by renaming, not by reordering
Reordering `presets` only changes which definition wins, it doesn't stop the collision, and it silently flips which
one you're now shipping. Rename instead, ideally in the preset's own source. If you don't own the preset, do it at
the boundary with a `preset:resolved` hook, which runs once per preset before they're merged:
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
presets: ['@acme/marketing-preset', '@acme/product-preset'],
plugins: [
{
name: 'rename-marketing-button',
hooks: {
'preset:resolved': ({ preset, name }) => {
if (name === '@acme/marketing-preset' && preset.theme?.recipes?.button) {
preset.theme.recipes.marketingButton = preset.theme.recipes.button
delete preset.theme.recipes.button
}
return preset
}
}
}
]
})
```
Now both recipes exist side by side as `marketingButton` and `productButton`, instead of one silently replacing the
other. Apply the same pattern to `theme.tokens` for a colliding token name. See
[Hooks](/docs/design-systems/hooks#modifying-presets) for the rest of what `preset:resolved` can do.
This is a different mechanism from [`prefix`](/docs/design-systems/federated-microfrontends#setting-prefix), which
namespaces every emitted class name and CSS variable uniformly at the config level for multi-bundle setups. Here,
the collision is inside a single config's own merged data, not between two builds, so the fix is renaming the
specific colliding keys, not prefixing everything.
## See also
- [Presets](/docs/design-systems/presets) for how preset merging and `theme.extend` work.
- [Hooks](/docs/design-systems/hooks#modifying-presets) for the `preset:resolved` example this page's fix is based on.
- [Federated Micro-Frontends](/docs/design-systems/federated-microfrontends) for the separate, multi-bundle version
of this problem.
---
## Build a design system
Ship tokens, recipes, and components as one package. Apps opt in with designSystem.
You own the design system and the apps, and both run Panda. Run `panda lib` in the package, then point each app at it
with `designSystem: '@acme/ds'`.
That replaces wiring `presets`, `importMap`, and `include` by hand. The app reuses the styles the design system already
extracted, instead of scanning its source again.
Sharing only tokens, or styling apps that don't use Panda? See [when to use each](#choose-how-to-distribute).
## Choose how to distribute
| You want to⦠| Use | Guide |
| ---------------------------------------------------------- | ---------------------------------------- | -------------------------------------------------- |
| Share a full design system between apps that all use Panda | `panda lib` + `designSystem` | This guide |
| Share only tokens, recipes, or patterns (no components) | A [preset](/docs/design-systems/presets) | [Presets](/docs/design-systems/presets) |
| Style apps that don't use Panda | A prebuilt static CSS file | [Component library](/docs/design-systems/overview) |
## Set up the monorepo
Keep the design system and its apps as separate packages. This walkthrough uses pnpm workspaces.
```yaml filename="pnpm-workspace.yaml"
packages:
- 'packages/*'
```
```txt
my-design-system/
βββ packages/
β βββ ds/ # the design system
β βββ app/ # an app that consumes it
βββ package.json
βββ pnpm-workspace.yaml
```
## Configure the theme
Create `packages/ds/panda.config.ts`. Define tokens, recipes, and patterns. Set `jsxFramework` if you ship JSX
components.
```ts filename="packages/ds/panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
presets: ['@pandacss/preset-base'],
include: ['src/**/*.{ts,tsx}'],
outdir: 'styled-system',
jsxFramework: 'react',
theme: {
tokens: {
colors: {
brand: { value: '#facc15' }
},
radii: {
md: { value: '0.375rem' }
}
},
recipes: {
button: {
className: 'button',
base: {
display: 'inline-flex',
alignItems: 'center',
borderRadius: 'md',
paddingInline: '4',
color: 'black',
backgroundColor: 'brand'
}
}
}
}
})
```
## Author your components
Generate `styled-system` with `panda codegen`, then build components on top of it. Import from the local
`styled-system`. Consumers import the components from your package name.
```tsx filename="packages/ds/src/button/button.tsx"
import type { ComponentPropsWithoutRef } from 'react'
import { button } from '../../styled-system/recipes'
export function Button(props: ComponentPropsWithoutRef<'button'>) {
return
}
```
```ts filename="packages/ds/src/index.ts"
export { Button } from './button/button'
```
## Set up package.json
Write the package entry, the build scripts, and a peer dependency on `@pandacss/dev`. Don't list `styled-system` exports
yourself. `panda lib` adds them.
```json filename="packages/ds/package.json"
{
"name": "@acme/ds",
"version": "1.0.0",
"type": "module",
"exports": {
".": "./src/index.ts"
},
"scripts": {
"codegen": "panda codegen",
"lib": "panda codegen && panda lib",
"watch": "panda codegen && panda lib --watch"
},
"peerDependencies": {
"@pandacss/dev": "^2.0.0",
"react": ">=18"
}
}
```
## Publish the library artifacts
```bash
pnpm --filter @acme/ds lib
```
`panda lib` writes three files under `dist/panda/`:
- `lib.json` is the manifest a consumer resolves as `@acme/ds/panda/lib.json`.
- `preset.mjs` is your theme (tokens, recipes, patterns).
- `buildinfo.json` is the extraction result, so apps don't scan your source again.
An app never imports `preset.mjs` or `buildinfo.json` by hand. `designSystem` loads both.
It also syncs `package.json` exports. Your `"."` entry stays. Everything else below is added for you, and only for
categories codegen actually emitted. This walkthrough has no patterns, so `./patterns` is absent.
```json filename="packages/ds/package.json"
{
"name": "@acme/ds",
"exports": {
".": "./src/index.ts",
"./panda/*": "./dist/panda/*",
"./css": {
"types": "./styled-system/css/index.d.ts",
"default": "./styled-system/css/index.js"
},
"./css/*": {
"types": "./styled-system/css/*.d.ts",
"default": "./styled-system/css/*.js"
},
"./helpers": {
"types": "./styled-system/helpers.d.ts",
"default": "./styled-system/helpers.js"
},
"./recipes": {
"types": "./styled-system/recipes/index.d.ts",
"default": "./styled-system/recipes/index.js"
},
"./recipes/*": {
"types": "./styled-system/recipes/*.d.ts",
"default": "./styled-system/recipes/*.js"
},
"./jsx": {
"types": "./styled-system/jsx/index.d.ts",
"default": "./styled-system/jsx/index.js"
},
"./jsx/*": {
"types": "./styled-system/jsx/*.d.ts",
"default": "./styled-system/jsx/*.js"
},
"./tokens": {
"types": "./styled-system/tokens/index.d.ts",
"default": "./styled-system/tokens/index.js"
}
}
}
```
Don't edit those subpaths by hand. The next `panda lib` rewrites them.
### Ship fallback files
If a consumer can't use the shipped `buildinfo.json` (corrupt file, schema skew), Panda re-extracts the paths listed in
`lib.json` as `files`. No `files` means that recovery fails closed.
`panda lib` infers those paths from your `include` globs. If `package.json` `"files"` would not publish them (a
`"files": ["dist"]` package whose sources live in `src/`), Panda drops the inferred paths and warns with
`design_system_files_not_publishable`. For a built-only package, pass the files you actually publish:
```bash
panda lib --files './**/*.{js,mjs}'
```
Build `dist/panda/` before you publish. A `"prepublishOnly": "panda codegen && panda lib"` script keeps it fresh without
committing generated output.
## Point an app at it
The app writes one field. Bundler setup, imports, extensions, and prefixes live on
[Consume a design system](/docs/design-systems/consuming-a-design-system).
```ts filename="packages/app/panda.config.ts"
export default defineConfig({
designSystem: '@acme/ds'
})
```
## Develop with watch mode
```bash
pnpm --filter @acme/ds watch # panda codegen once, then panda lib --watch
pnpm --filter @acme/app dev
```
`panda lib --watch` rewrites `dist/panda/` when tokens, recipes, or components change. It does not rerun
`panda codegen`. If a change updates generated `styled-system` files, run `panda codegen` again (or keep a second
codegen watcher).
## Version and publish
```bash
pnpm --filter @acme/ds lib
pnpm --filter @acme/ds publish
```
The design system and its apps must run the same Panda major. `panda lib` stamps that range from the package's
`@pandacss/dev` peer, or from `--panda`. A mismatch fails the consumer build with
`design_system_peer_range_unsatisfied`.
The manifest `version` field is informational. Panda does not enforce it. Use normal semver when you rename or remove
tokens.
## Troubleshooting
- **`design_system_files_not_publishable`** means inferred fallback paths would not ship in the npm tarball. Pass
`--files` for the sources you actually publish, or consumers can't recover from stale build info.
- **`design_system_export_overwritten`** means `panda lib` replaced a `package.json` export that didn't match what it
would write. Let it own the `./panda/*` and `styled-system` subpaths.
Wire the app next: [Consume a design system](/docs/design-systems/consuming-a-design-system).
---
## Conditions
Learn how to customize conditions in your Panda config
Conditions allow you to apply different styles and behaviors based on specific conditions or states. They provide a way
to target specific elements or apply styles in response to certain events or conditions.
## Creating a condition
To create a condition, you can use the conditions property in the config. Let's say we want to create a `groupHover`
pseudo condition that applies styles to an element when a parent container with the `group` role is hovered.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
conditions: {
extend: {
groupHover: '[role=group]:where(:hover, [data-hover]) &'
}
}
})
```
> β οΈ The `&` character is mandatory, it is a placeholder for the current selector. It will be replaced with the actual
> selector when the condition is used. It has to be used either at the beginning or at the end of the condition.
Then you can run the following command to generate the conditions JS code:
{/* */}
```bash
pnpm panda codegen
```
```bash
npm panda codegen
```
```bash
yarn panda codegen
```
```bash
bun panda codegen
```
{/* */}
Now, we can use the `groupHover` condition in our components.
```jsx
import { css } from '../styled-system/css'
function App() {
return (
)
}
```
## Customizing Built-in Conditions
You can extend the [default conditions](/docs/styling/conditional-styles#reference) by using the `conditions.extend`
property in the config.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
conditions: {
extend: {
// Extend the default `dark` condition
dark: '.dark &, [data-theme="dark"] &'
}
}
})
```
Then you can run the following command to update the conditions JS code:
{/* */}
```bash
pnpm panda codegen
```
```bash
npm panda codegen
```
```bash
yarn panda codegen
```
```bash
bun panda codegen
```
{/* */}
## Using tokens
You can also use tokens in your conditions, and they will be resolved to their actual values:
```tsx
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
conditions: {
extend: {
mq: '@media (min-width: token(sizes.4xl))',
size2: '&[data-size=token(spacing.2)]'
}
}
})
```
## Mixed conditions
You can also use mixed conditions (nested at-rules/selectors) under a single condition name:
```tsx
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
conditions: {
extend: {
supportHover: ['@media (hover: hover) and (pointer: fine)', '&:hover']
}
}
})
```
```ts
import { css } from '../styled-system/css'
css({
_supportHover: {
color: 'red'
}
})
```
will generate the following CSS:
```css
@media (hover: hover) and (pointer: fine) {
&:hover {
color: red;
}
}
```
## Multi-block conditions
Sometimes you need a single condition to generate multiple independent CSS blocks, each with its own at-rule. For example, you might want hover feedback on desktop (pointer devices) and active/press feedback on touch devices.
Use the object syntax with `@slot` markers to define multi-block conditions. Each path from the root of the object to a `@slot` value becomes an independent CSS block:
```tsx
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
conditions: {
extend: {
hoverActive: {
'@media (hover: hover)': {
'&:is(:hover, [data-hover])': '@slot',
},
'@media (hover: none)': {
'&:is(:active, [data-active])': '@slot',
},
},
},
},
})
```
```ts
import { css } from '../styled-system/css'
css({
_hoverActive: {
bg: 'red',
},
})
```
will generate the following CSS:
```css
@media (hover: hover) {
.hoverActive\:bg_red:is(:hover, [data-hover]) {
background: red;
}
}
@media (hover: none) {
.hoverActive\:bg_red:is(:active, [data-active]) {
background: red;
}
}
```
This is also useful for any scenario where a single condition needs to apply the same styles across multiple independent at-rule blocks. Objects with a single `@slot` path behave identically to the [mixed conditions](#mixed-conditions) array syntax.
## Container queries
Read more about how to define type-safe container queries [here](/docs/styling/conditional-styles#container-queries)
## Minimal setup
If you want to use Panda with the bare minimum, without any of the defaults, you can read more about it
[here](/docs/design-systems/minimal-setup)
---
## Config Functions
Functions to expose types for your config.
Config functions help define and provide type information for your configuration. These utilities enhance code
readability, enforce consistency, and ensure robust type checking.
## Config Creators
To help defining config in a type-safe way, you can use the following helpers:
### `defineConfig`
Function for [config](/docs/reference/config) definitions.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
theme: {},
include: ['src/**/*.{js,jsx,ts,tsx}']
})
```
### `defineRecipe`
Function for [recipe](/docs/styling/recipes#config-recipe) definitions.
```ts
import { defineRecipe } from '@pandacss/dev'
export const buttonRecipe = defineRecipe({
className: 'button',
description: 'The styles for the Button component',
base: {
display: 'flex'
},
variants: {
visual: {
funky: { bg: 'red.200', color: 'white' },
edgy: { border: '1px solid {colors.red.500}' }
}
},
defaultVariants: {
visual: 'funky',
size: 'sm'
}
})
```
### `defineSlotRecipe`
Function for [slot recipe](/docs/styling/slot-recipes#config-slot-recipe) definitions.
```ts
import { defineSlotRecipe } from '@pandacss/dev'
export const checkboxRecipe = defineSlotRecipe({
className: 'checkbox',
description: 'The styles for the Checkbox component',
slots: ['root', 'control', 'label'],
base: {
root: { display: 'flex', alignItems: 'center', gap: '2' },
control: { borderWidth: '1px', borderRadius: 'sm' },
label: { marginStart: '2' }
},
variants: {
size: {
sm: {
control: { width: '8', height: '8' },
label: { fontSize: 'sm' }
},
md: {
control: { width: '10', height: '10' },
label: { fontSize: 'md' }
}
}
},
defaultVariants: {
size: 'sm'
}
})
```
### `defineParts`
It can be useful when you want to have the equivalent of a slot recipe without needing to split the class names bindings
and instead just having a className that handles children on 1 DOM element.
It pairs well with [ZagJs](https://zagjs.com/) and [Ark-UI](https://ark-ui.com/)
Let's refactor the previous example to use parts instead of slots:
```ts
import { defineParts, definetRecipe } from '@pandacss/dev'
const parts = defineParts({
root: { selector: '& [data-part="root"]' },
control: { selector: '& [data-part="control"]' },
label: { selector: '& [data-part="label"]' }
})
export const checkboxRecipe = defineRecipe({
className: 'checkbox',
description: 'A checkbox style',
base: parts({
root: { display: 'flex', alignItems: 'center', gap: '2' },
control: { borderWidth: '1px', borderRadius: 'sm' },
label: { marginStart: '2' }
}),
variants: {
size: {
sm: parts({
control: { width: '8', height: '8' },
label: { fontSize: 'sm' }
}),
md: parts({
control: { width: '10', height: '10' },
label: { fontSize: 'md' }
})
}
},
defaultVariants: {
size: 'sm'
}
})
```
### `definePattern`
Function for [pattern](/docs/design-systems/patterns) definitions.
```ts
import { definePattern } from '@pandacss/dev'
const visuallyHidden = definePattern({
transform(props) {
return {
srOnly: true,
...props
}
}
})
```
### `definePreset`
Function for [preset](/docs/design-systems/presets#creating-a-preset) definitions.
```ts
import { definePreset } from '@pandacss/dev'
export const pandaPreset = definePreset({
theme: {
extend: {
tokens: {
colors: { primary: { value: 'blue.500' } }
}
}
}
})
```
### `definePlugin`
Function for [plugin](/docs/reference/config#plugins) definitions.
```ts
import { definePlugin } from '@pandacss/dev'
export const plugin = definePlugin({
name: 'css-report',
hooks: {
'cssgen:done': ({ path, content }) => {
console.log(`generated ${content.length} bytes`, path)
}
}
})
```
### `defineKeyframes`
Function for [keyframes](/docs/theming/theme#keyframes) definitions.
```ts
import { defineKeyframes } from '@pandacss/dev'
export const keyframes = defineKeyframes({
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' }
}
})
```
### `defineGlobalStyles`
Function for [global styles](/docs/styling/writing-styles#global-styles) definitions.
```ts
import { defineGlobalStyles } from '@pandacss/dev'
const globalCss = defineGlobalStyles({
'html, body': {
color: 'gray.900',
lineHeight: '1.5'
}
})
```
### `defineUtility`
Function for [utility](/docs/design-systems/utilities) definitions.
```ts
import { defineUtility } from '@pandacss/dev'
export const br = defineUtility({
className: 'rounded',
values: 'radii',
transform(value) {
return { borderRadius: value }
}
})
```
### `defineTextStyles`
Function for [text styles](/docs/theming/text-styles) definitions.
```ts
import { defineTextStyles } from '@pandacss/dev'
export const textStyles = defineTextStyles({
body: {
description: 'The body text style - used in paragraphs',
value: {
fontFamily: 'Inter',
fontWeight: '500',
fontSize: '16px',
lineHeight: '24',
letterSpacing: '0',
textDecoration: 'None',
textTransform: 'None'
}
}
})
```
### `defineLayerStyles`
Function for [layer styles](/docs/theming/layer-styles) definitions.
```ts
import { defineLayerStyles } from '@pandacss/dev'
const layerStyles = defineLayerStyles({
container: {
description: 'container styles',
value: {
background: 'gray.50',
border: '2px solid',
borderColor: 'gray.500'
}
}
})
```
### `defineStyles`
Function for style definitions.
This comes in handy when you want to define reusable styles in the config.
E.g. a set of styles to be used in multiple variants within a [recipe](/docs/styling/recipes#config-recipe).
```ts {3, 14, 18} filename="recipes/button.ts"
import { defineRecipe, defineStyles } from '@pandacss/dev'
const buttonVisualStyles = defineStyles({
borderRadius: 'lg',
boxShadow: 'sm'
})
export const buttonRecipe = defineRecipe({
// ...
variants: {
visual: {
funky: {
bg: 'red.200',
color: 'white',
...buttonVisualStyles
},
edgy: {
border: '1px solid {colors.red.500}',
...buttonVisualStyles
}
}
}
})
```
## Token Creators
To help defining tokens in a type-safe way, you can use the following helpers:
### `defineTokens`
```ts
import { defineTokens } from '@pandacss/dev'
const theme = {
tokens: defineTokens({
colors: {
primary: { value: '#ff0000' }
}
})
}
```
You can also use this function to define tokens in a separate file:
```ts filename="tokens/colors.ts"
import { defineTokens } from '@pandacss/dev'
export const colors = defineTokens.colors({
primary: { value: '#ff0000' }
})
```
### `defineSemanticTokens`
```ts
import { defineSemanticTokens } from '@pandacss/dev'
const theme = {
semanticTokens: defineSemanticTokens({
colors: {
primary: {
value: { _light: '{colors.blue.400}', _dark: '{colors.blue.200}' }
}
}
})
}
```
You can also use this function to define tokens in a separate file:
```ts filename="tokens/colors.semantic.ts"
import { defineSemanticTokens } from '@pandacss/dev'
export const colors = defineSemanticTokens.colors({
primary: {
value: { _light: '{colors.blue.400}', _dark: '{colors.blue.200}' }
}
})
```
---
## Consume a design system
Point an app at a Panda design system with one config field.
Install the package. Add one field:
```ts filename="panda.config.ts"
export default defineConfig({
designSystem: '@acme/ds'
})
```
Panda loads the package's theme, reuses the styles it already extracted, and recognizes imports from both `@acme/ds`
and your local `styled-system`. You don't add the preset, `importMap`, or the library's build info by hand.
If you're authoring the package, start at [Build a design system](/docs/design-systems/building-a-design-system).
## Add the dependency
```json filename="package.json"
{
"dependencies": {
"@acme/ds": "workspace:*"
},
"devDependencies": {
"@pandacss/dev": "^2.0.0",
"@pandacss/vite": "^2.0.0"
}
}
```
Use the published version on npm when the design system isn't in the same repo.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
designSystem: '@acme/ds',
include: ['src/**/*.tsx'],
outdir: 'styled-system',
theme: {
extend: {
tokens: {
spacing: {
6: { value: '1.5rem' }
}
}
}
}
})
```
`theme.extend` layers on top of the design system's theme. The app value wins when both sides define the same token
path.
## Wire up the bundler
With Vite, add the `@pandacss/vite` plugin. It runs codegen and injects CSS. `transform: true` is optional: it inlines
static `css()` calls at build time. See the [framework guides](/docs/styling/installation) for other setups.
```ts filename="vite.config.ts"
import pandacss from '@pandacss/vite'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [pandacss(), react()]
})
```
If the app runs Panda through PostCSS instead, only the PostCSS config changes. `designSystem`, the entry CSS, and
the imports stay the same.
```js filename="postcss.config.mjs"
import pandacss from '@pandacss/postcss'
export default {
plugins: [pandacss()]
}
```
The plugin regenerates `styled-system` on every build, but the folder has to exist the first time the bundler resolves
your imports. Run `panda codegen` (or `panda build`) in a `predev`/`prebuild` script so a fresh checkout builds β the
[example apps](https://github.com/chakra-ui/panda-examples) are wired this way. To emit CSS without the folder at all,
use [`panda cssgen`](/docs/reference/cli#cssgen).
## Add the entry CSS
Panda emits into [cascade layers](/docs/styling/cascade-layers). Declare their order once and import that file at the
app root. The bundler plugin injects the generated CSS. This file only sets layer order.
```css filename="src/index.css"
@layer reset, base, tokens, recipes, utilities;
```
```tsx filename="src/main.tsx"
import { createRoot } from 'react-dom/client'
import { App } from './app'
import './index.css'
createRoot(document.getElementById('root')!).render()
```
## Import components and css
Import components from the design system package. Import `css` from your local `styled-system` whenever the app
extends tokens, utilities, conditions, or breakpoints. That keeps `css({ p: '6' })` typed against the merged theme.
```tsx filename="src/app.tsx"
import { Button } from '@acme/ds'
import { css } from '../styled-system/css'
export function App() {
return (
)
}
```
An app that adds nothing of its own can import `css` from `@acme/ds/css` instead. Both paths extract.
## What the app generates
Your local `styled-system` re-exports what the design system already shipped, and only generates what you added.
Authoring a field means you wrote it in this config (or a non-design-system preset), not that you inherited it.
| You author in the app | `css()`, `cva()`, `cx` | Recipes and patterns the design system already owns |
| --------------------- | ---------------------- | --------------------------------------------------- |
| Nothing | Re-export from `@acme/ds` | Re-export |
| Tokens, utilities, conditions, or breakpoints | Generated locally | Still re-exported |
| `prefix`, `hash`, `separator`, `jsxFramework`, `jsxStyleProps`, or `syntax` | Full local tree | Full local tree |
| A nested design system (the package itself extends another) | Full local tree | Full local tree |
## Add your own recipes
Author them under `theme.extend`. Import both your recipes and the design system's from the local `styled-system`.
```ts filename="panda.config.ts"
export default defineConfig({
designSystem: '@acme/ds',
theme: {
extend: {
recipes: {
panel: {
className: 'panel',
base: { display: 'flex', flexDirection: 'column', gap: '3', p: '3' }
}
}
}
}
})
```
```tsx filename="src/app.tsx"
import { Button } from '@acme/ds'
import { button, panel } from '../styled-system/recipes'
export function App() {
return (
)
}
```
### Override a design system value
Write your own definition of the token, recipe, or pattern. It merges over the original and wins.
- A token path both sides define reports `design_system_token_conflict` (info).
- A recipe or pattern both sides define reports `design_system_artifact_conflict` (warning). The design system's copy
drops out of the re-export in favor of yours.
## Isolate styles with a prefix
Two Panda builds emit the same class names by default, like `.button` and `--colors-brand`. Set `prefix` when those
styles load in more than one independently-built bundle.
```ts filename="panda.config.ts"
export default defineConfig({
designSystem: '@acme/ds',
prefix: 'app' // .button β .app-button, --colors-brand β --app-colors-brand
})
```
Pick the prefix by the app's identity, not by design system version. A prefix that differs from the design system's
regenerates the runtime locally.
## Nested design systems
A design system can extend another. You still write one field, the leaf package:
```ts
export default defineConfig({
designSystem: '@acme/marketing-ds'
})
```
Panda walks the parent chain, merges presets root-first, and hydrates each layer. The app emits a full local
`styled-system` instead of re-exporting. Output is correct, just not deduped.
You cannot point `designSystem` at two unrelated packages. Put the second package in `include` if it only consumes
the first, or give it its own parent link if it is a real child design system.
## Troubleshooting
- **`design_system_manifest_not_found` / `design_system_manifest_not_exported`** means the installed package has no
`./panda/*` export. The author needs to run `panda lib` and republish. Then reinstall.
- **`design_system_export_missing`** means a styled-system subpath the app's generated files import (`./css`,
`./recipes`, β¦) is missing from the package exports. Same fix: rebuild with `panda lib`.
- **`design_system_peer_range_unsatisfied`** means this app's Panda major doesn't match the design system's. Upgrade
them together.
- **`design_system_buildinfo_stale`** means Panda re-extracted the design system's fallback `files` because
`buildinfo.json` was unusable. Styles stay correct. If the warning persists after a package update, the author
needs to republish. If the build errors instead, the manifest has no `files` and recovery is disabled.
- **`design_system_token_conflict` / `design_system_artifact_conflict`** are expected when you override on purpose.
The [example apps](https://github.com/chakra-ui/panda-examples) show one design system shipped three ways: a monorepo
that builds the system and a Next.js app together, an app that installs the published CSS and components with no Panda,
and an app that runs Panda and extends the system's tokens. Grab any one with `degit`.
---
## Ecosystem plugins
What a Panda plugin is, when to write one, and how to share hooks as a package.
A plugin is a named bundle of [hooks](/docs/design-systems/hooks) you distribute as its own package, instead of pasting
the same hooks into every project's config. It's the hooks counterpart to a [preset](/docs/design-systems/presets): a
preset shares theme and tokens, a plugin shares behavior.
## Framework support is built in
You don't add a plugin to make Panda read Vue, Svelte, or Astro files. The compiler extracts styles from `.vue`,
`.svelte`, and `.astro` single-file components directly, so the [Vue](/docs/styling/vue), [Svelte](/docs/styling/svelte),
and [Astro](/docs/styling/astro) install pages have no plugin to add. If you're upgrading from v1, the separate
`@pandacss/plugin-vue` and `@pandacss/plugin-svelte` packages are gone β that work moved into the compiler.
## When to write your own
Most one-off customization belongs in an inline plugin in your config, not a separate package β see
[Hooks](/docs/design-systems/hooks) for examples like transforming source before extraction or reacting to generated
output. Reach for a published plugin when you want to share that behavior across projects or teams, the same reason you'd
reach for a preset instead of copy-pasting tokens.
A plugin is a `name` and a `hooks` object. `definePlugin` gives you the types:
```ts filename="my-plugin.ts"
import { definePlugin } from '@pandacss/dev'
export function myPlugin() {
return definePlugin({
name: 'my-plugin',
hooks: {
'parser:before': ({ filePath, content }) => {
// transform a file's source before Panda extracts from it
return content
},
},
})
}
```
Users add the factory to their `plugins` array:
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
import { myPlugin } from './my-plugin'
export default defineConfig({
plugins: [myPlugin()],
})
```
## See also
- [Hooks](/docs/design-systems/hooks) for every hook and how to author and share a plugin.
- [Presets](/docs/design-systems/presets) for sharing theme and tokens the same way.
---
## Environment-specific config
Branch Panda config on process.env for production hash, minify, and CSS optimizations.
`panda.config.ts` runs in Node when Panda codegen, cssgen, or your bundler plugin starts. Branch on `process.env` to
keep dev output readable and turn on production optimizations in CI and release builds.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
const isProd = process.env.NODE_ENV === 'production'
export default defineConfig({
include: ['./src/**/*.{js,jsx,ts,tsx}'],
outdir: 'styled-system',
minify: isProd,
hash: isProd
})
```
Hash class names and CSS variables separately if you only want one:
```ts
hash: isProd ? { className: true, cssVar: true } : false
```
## What to toggle
| Option | Development | Production |
| ------------------------------------------ | ------------------------------------------ | ----------------------- |
| [`hash`](/docs/reference/config#hash) | `false` (readable class names in DevTools) | `true` (shorter output) |
| [`minify`](/docs/reference/config#minify) | `false` | `true` |
## Keep dev and prod in sync
[`hash`](/docs/styling/writing-styles#hashing) changes class names in **both** generated CSS and `styled-system`
output. Run `panda codegen` and cssgen with the same config you ship. If production enables `hash`, your production
build step should run Panda with `NODE_ENV=production`.
If `NODE_ENV` is unreliable in your toolchain, use your own flag:
```ts
const isProd = (process.env.PANDA_ENV ?? process.env.NODE_ENV) === 'production'
```
## Verify the resolved config
Inspect `styled-system/debug/config.json` after running `panda debug` and confirm `hash` and `minify` match what you
expect for that run. See [Debugging](/docs/reference/debugging#panda-debug).
---
## Federated Micro-Frontends
How to safely run multiple Panda-built applications (and multiple versions of the same design system) on a single host page using `prefix` and `cssVarRoot`, whether your design system ships static CSS or build info.
Panda supports federated micro-frontend architectures (multiple
independently-built apps loaded onto a single host page) through the
`prefix` and `cssVarRoot` config options.
This guide shows how to set up a Panda lib so it can co-exist with other
Panda libs (or with a different version of itself) without class-name or CSS
variable collisions.
## When you need this
Any setup that loads two or more independently-built Panda stylesheets into
one document is affected:
- Webpack Module Federation
- single-spa, qiankun, or other micro-frontend orchestrators
- Dynamically-injected widgets (Intercom-style embeds)
- A host page that pins one version of your design system while a remote
pins a different version
If your bundles never share a document (for example with Next.js multi-zone
routing or iframe embedding), you do not need this guide. Each app's CSS
loads in its own document and the cascade cannot cross.
## The two classes of clash
There are two distinct ways federated bundles collide.
### Override leakage (single version)
Two remotes load the same DS version, and one of them defines a local
override:
```css
/* Remote 1 */
.ds-background { background-color: blue; }
.remote1-background { background-color: red; }
/* Remote 2 */
.ds-background { background-color: blue; }
```
Load order decides whether `.remote1-background` wins. Wrap your DS rules
in a [cascade layer](/docs/styling/cascade-layers):
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
layers: {
recipes: 'ds.recipes',
utilities: 'ds.utilities'
}
})
```
Unlayered styles always beat layered styles per the CSS cascade spec, so the
remote's local overrides are now deterministic regardless of load order.
### Cross-version collision
Remote A pins `@acme/ds@1`, Remote B pins `@acme/ds@2`. Both bundles emit a
`.button` rule from `defineRecipe`, and both emit `--colors-brand` on
`:root`, with **different** values:
```css
/* v1's bundle */
:root { --colors-brand: #ea580c; }
.button { background: var(--colors-brand); padding: 8px 12px; }
/* v2's bundle */
:root { --colors-brand: #2563eb; }
.button { background: var(--colors-brand); padding: 20px 40px; }
```
Same selector, different rule. Same custom property, different value. The
cascade picks one globally, and the loser is silently overwritten. **Cascade
layers do not help here.** Both versions emit into the same `recipes`
layer, and within a single layer load order still decides.
This is the case `prefix` is designed for.
## Two distribution models
`prefix` is the fix in both cases. It's the only thing that survives a
cross-version collision. What differs between setups is **who owns the
prefix** and **what it's keyed to**, and that follows from how the design
system ships its CSS:
| Distribution | Who sets `prefix` | Keyed to | Tradeoff |
| -------------------------------- | ----------------- | ------------------------------ | --------------------------------------------------------------------------------- |
| Lib ships **static CSS** | Lib author | DS version (e.g. `acme-v1`) | One DS bundle per major, shared across every remote |
| Lib ships **build info + preset**| Remote owner | Remote identity (e.g. `remote1`) | Each remote tree-shakes its own DS slice; cross-version isolation falls out for free |
In the **static-CSS** model the lib runs `panda cssgen` itself and publishes
the finished stylesheet, so the prefix has to be baked in at the lib's build
time, and the only thing it can safely key to is its own version. The
sections from [Setting `prefix`](#setting-prefix) through [Recommended remote
config](#recommended-remote-config) walk this model in detail.
In the **build-info** model the lib ships extraction metadata and a preset
instead of CSS, and each consuming remote generates the final stylesheet
during its own app build. The prefix moves to the remote's config, keyed to
the remote's identity. See [Model 2: Library ships build info +
preset](#model-2-library-ships-build-info--preset) below.
> If the lib and every consumer all use Panda, the build-info model is the
> one the [Component Library guide](/docs/design-systems/ship-styled-system#ship-the-build-info-file)
> already recommends. It tree-shakes the output down to the styles each app
> actually uses. Federation doesn't change that recommendation; it just adds
> the `prefix` requirement on the consumer side.
## Setting `prefix`
Set a distinct `prefix` per design-system version. Panda prepends it to
every emitted class name and CSS variable.
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
prefix: 'acme-v1',
preflight: false,
theme: {
extend: {
tokens: {
colors: {
brand: { value: '#ea580c' }
}
},
recipes: {
button: {
className: 'button',
base: {
background: 'brand',
padding: '8px 12px'
}
}
}
}
}
})
```
After `panda cssgen`, the bundle emits:
```css
:where(:root, :host) {
--acme-v1-colors-brand: #ea580c;
}
.acme-v1-button {
background: var(--acme-v1-colors-brand);
padding: 8px 12px;
}
```
A second version with `prefix: 'acme-v2'` emits `.acme-v2-button` and
`--acme-v2-colors-brand`. Both bundles can sit in the same `` without
clashing. The cascade has nothing to fight over.
> **Note:** Source code in the lib and consumer apps doesn't change. Panda's
> runtime `css()`, `cva()`, and recipe helpers read `prefix` from config and
> emit the namespaced class names automatically.
## Deriving the prefix from your package
Hardcoding the prefix is brittle. The robust pattern is to derive it from
your package's major version:
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
import pkg from './package.json' assert { type: 'json' }
const major = pkg.version.split('.')[0]
const slug = pkg.name.replace(/[@/]/g, '-').replace(/^-/, '')
export default defineConfig({
prefix: `${slug}-v${major}`,
// ...
})
```
For `@acme/lib@1.4.2`, this produces `acme-lib-v1`. Bumping to a 2.x release
automatically rotates the prefix to `acme-lib-v2`, so two majors loaded into
the same host page are structurally isolated without any human intervention.
> **Note:** Patch and minor releases share a prefix. If you need every
> release to be structurally distinct, use the full version
> (`acme-lib-v1-4-2`). Major-only is the recommended default. It matches
> the granularity humans already reason about for breaking changes.
## Scoping tokens with `cssVarRoot`
`prefix` namespaces the CSS variable **names**. That alone is enough to
prevent the `:root` collision. If you also want the variables to be
**scoped to a subtree** (so tokens declared by one remote don't apply
to elements outside its mount point), set `cssVarRoot`:
```ts filename="panda.config.ts"
export default defineConfig({
prefix: 'acme-v1',
cssVarRoot: '.acme-v1-scope'
})
```
Emitted CSS:
```css
.acme-v1-scope {
--acme-v1-colors-brand: #ea580c;
}
```
The remote then wraps its mount point in an element that carries the class:
```tsx
```
Elements outside `.acme-v1-scope` won't resolve `--acme-v1-colors-brand`.
Useful when multiple remotes share a host page but each remote's tokens
should be contained to its own DOM subtree.
`cssVarRoot` is optional. `prefix` alone is sufficient for most federated
setups because the variable names already differ per version.
## How `defineRecipe` and `cva` are affected
Both recipe APIs benefit from `prefix`, in slightly different ways.
`defineRecipe` emits a stable class name based on the recipe name (`.button`,
`.button--size-lg`). Without `prefix`, two versions of the same recipe
produce identical class names with different declarations. With `prefix`,
each version emits its own namespaced class (`acme-v1-button`,
`acme-v2-button`), and the collision is gone.
`cva` with raw values is already collision-free:
```tsx filename="src/Button.tsx"
import { cva } from '../styled-system/css'
const button = cva({
base: {
background: '#ea580c',
padding: '8px 12px'
}
})
// emits .bg_\#ea580c .p_8px_12px ...
```
The atomic class names encode the **value**, so two versions with different
values naturally produce different class names. `prefix` adds a second layer
of isolation but isn't strictly required here.
`cva` with semantic tokens is the quiet case:
```tsx
const button = cva({
base: { background: 'brand' }
})
// emits .bg_brand { background: var(--colors-brand) }
```
The atomic class name is **stable** across versions (`bg_brand` in both,
with identical rule body), so there's no class-name clash. But both versions
declare `--colors-brand` on `:root` with different values. The cascade picks
one globally, and the visual result is wrong even though the class names
matched. `prefix` fixes this by namespacing both the class
(`acme-v1-bg_brand`) and the variable (`--acme-v1-colors-brand`).
## Recommended remote config
For a Panda lib intended to ship into a federated host:
```ts filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'
import pkg from './package.json' assert { type: 'json' }
const major = pkg.version.split('.')[0]
const slug = pkg.name.replace(/[@/]/g, '-').replace(/^-/, '')
export default defineConfig({
prefix: `${slug}-v${major}`,
preflight: false,
jsxFramework: 'react',
outdir: 'styled-system',
theme: {
extend: {
// tokens, recipes, etc.
}
}
})
```
> **Note:** `preflight: false` is recommended for libs and remotes. A reset
> stylesheet shipped from a remote can stomp on the host's body styles.
> Let the host own the reset.
## Model 2: Library ships build info + preset
Everything above assumes the lib publishes prebuilt CSS. The other shape is
[shipping build info](/docs/design-systems/ship-styled-system#ship-the-build-info-file)
and a preset: each consuming remote generates the final stylesheet during its
own build. The collision is identical, and `prefix` is still the fix. It
just moves to the **consumer's** config, keyed to the remote's identity
rather than the DS version.
`panda buildinfo` only writes extraction results (which styles are used), not the
token and recipe **definitions**. So a build-info consumer wires up two
pieces: the build-info file (via `include`) and the lib's preset (via
`presets`). The preset supplies the definitions, the build-info file supplies
the usage.
`prefix` lives on the consumer's top-level config and is applied at emission,
so `.button` becomes `.remote1-button` and `--colors-brand` becomes
`--remote1-colors-brand`, regardless of whether the definition came from the
remote's own source or from the lib's preset.
```ts filename="remote1/panda.config.ts"
import { defineConfig } from '@pandacss/dev'
import { acmeDsPreset } from '@acme-org/design-system'
export default defineConfig({
prefix: 'remote1',
presets: ['@pandacss/dev/presets', acmeDsPreset],
importMap: '@acme-org/styled-system',
include: [
'./node_modules/@acme-org/design-system/dist/panda.buildinfo.json',
'./src/**/*.{ts,tsx}'
]
})
```
### Cross-version isolation is automatic
Because the prefix is keyed to the remote, not to the DS version, version
isolation falls out without anyone tracking versions. If `remote1` pins
`@acme-org/design-system@29` and `remote2` pins `@30`, each remote resolves
its own copy in `node_modules`, gets its own preset plus build info, and
emits CSS under its own remote prefix:
```css
/* remote1's bundle (DS v29) */
.remote1-button { ... }
:where(:root, :host) { --remote1-colors-brand: #ea580c; }
/* remote2's bundle (DS v30) */
.remote2-button { ... }
:where(:root, :host) { --remote2-colors-brand: #2563eb; }
```
Two payloads can't collide on a selector or variable name, so the host page
loads both without a fight.
> **Note:** In the build-info model the lib never sets a `prefix`. Each
> consumer namespaces itself. A lib that baked in its own prefix would force
> every consumer to share it, which puts back the collision you set out to
> avoid.
## Verifying the output
After running `panda cssgen`, grep the emitted bundle for the names that
previously collided:
```bash
grep -E "^\.button|--colors-brand:" styled-system/styles.css
```
You should see every match carrying your prefix:
```css
.acme-v1-button { ... }
:where(:root, :host) { ... --acme-v1-colors-brand: ...; ... }
```
If you see unprefixed matches, double-check that `prefix` is set at the
**top level** of `defineConfig` (not nested inside `theme`), and regenerate
the bundle.
## Limits of `prefix`
`prefix` solves the structural class-name and CSS-variable collisions for
multi-version federated setups. It does not solve:
- **Same-prefix collisions.** Two libs that both choose `prefix: 'design'`
collide as if neither had a prefix. The prefix string is your isolation
key. Choose it like you'd choose a package name.
- **Reset collisions.** If multiple remotes ship `preflight: true`, the
resets fight regardless of `prefix` because they target raw element
selectors. Set `preflight: false` on remotes.
- **Hand-written CSS.** `prefix` operates on what Panda emits. CSS
authored outside Panda still uses your hand-written selectors.
For total style isolation against an untrusted host, consider wrapping the
remote in an iframe or Shadow DOM. Heavier-weight, but bulletproof.
## Related
- [Cascade Layers](/docs/styling/cascade-layers): solves
single-version override leakage
- [Hashing](/docs/styling/writing-styles#hashing): alternative to prefix
when you want short opaque class names instead of namespace strings
- [Component Library](/docs/design-systems/overview): distribution
patterns for shipping a Panda lib
- [Presets](/docs/design-systems/presets): sharing tokens and recipes
across consumers
---
## forwardProps
Use the styled() factory to wrap single elements or third-party components for distribution.
Not every component in a library is a multi-part compound like an accordion or tabs. A `Button`, `Input`, or a
wrapped third-party component is usually a single element, and the [`styled()` factory](/docs/styling/style-props#factory-function)
is the right tool for that. This page covers the decisions specific to wrapping something for distribution; the
factory's full API lives at [JSX Style Props](/docs/styling/style-props#factory-function).
## Wrapping a third-party component
`styled()` accepts any component, not just JSX intrinsics like `'button'`. This is how you add Panda's style props to
a component you don't own:
```tsx
import { styled } from '../styled-system/jsx'
import { Button as HeadlessButton } from 'some-headless-lib'
const Button = styled(HeadlessButton)
```
Users can now pass style props directly, on top of whatever props the wrapped component already accepts.
## Deciding what gets forwarded
By default, `styled()` forwards every prop except recipe variants and style props to the underlying element. That's
usually right for a plain `'button'` or `'div'`, but wrapping someone else's component raises a specific question:
does the wrapped component need to see a value that Panda would otherwise treat as a variant or a style prop?
That's what `forwardProps` is for. List the prop names that should reach your component as-is, in addition to
driving the recipe:
```tsx
import { styled } from '../styled-system/jsx'
const Input = styled('input', {}, { forwardProps: ['size'] })
```
Here, `size` still selects the matching recipe variant, but the underlying `` also receives it as a real HTML
attribute. Without `forwardProps`, Panda would consume `size` for styling and never pass it through.
> A forwarded prop becomes a plain prop on the wrapped component: it no longer feeds recipe styling on its own after
> that point. If you need a prop to both style a slot and reach the component, and you're wrapping a multi-part
> compound rather than a single element, see [Forwarding props](/docs/styling/jsx-style-context#forwarding-props) in
> `createSlotRecipeContext` instead.
## When forwardProps isn't enough
`forwardProps` takes a fixed list. If which props get forwarded depends on the component you're wrapping, for
example when integrating a library like Framer Motion that has its own valid prop set, use `shouldForwardProp`
instead. See [shouldForwardProp](/docs/styling/style-props#shouldforwardprop) for the full signature and a worked
example.
## Naming the factory
Users of your library see whatever name you export, not the underlying `styled()` call. If your library wraps
several third-party components this way, keep the naming consistent with the component being wrapped so users
can tell your exports apart from the library's own.
## See also
- [Wrap headless UI](/docs/design-systems/wrap-headless-ui) covers the equivalent decision for multi-part compounds
built with `createSlotRecipeContext`, not single-element wrappers.
- [Track usage in wrapped components](/docs/design-systems/track-usage) covers how Panda's static extraction handles
a component re-exported or wrapped like this.
---
## Panda Integration Hooks
Leveraging hooks in Panda to create custom functionality.
Panda hooks let you add new functionality or change existing behavior at specific points in the compiler lifecycle.
Hooks are callbacks you attach to named plugins in the `plugins` array. There is no root-level `hooks` object β every
hook lives on a plugin with a `name` and a `hooks` map.
```ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
plugins: [
{
name: 'my-plugin',
hooks: {
'cssgen:done': ({ content, path }) => {
// ...
},
},
},
],
})
```
Here are some things you can do with hooks:
- Modify the resolved config (`config:resolved`), like stripping out patterns, tokens, or keyframes.
- Modify a preset after it's resolved (`preset:resolved`), like removing tokens or theme properties from a preset.
- Transform a source file into `tsx`-friendly syntax before it's parsed (`parser:before`), so Panda can extract its style
usage β this also lets you support templating languages Panda doesn't parse natively.
- Adjust the generated JS and DTS artifacts before they're written (`codegen:prepare`), or react after they're written
(`codegen:done`).
- Observe the final CSS after it's produced (`cssgen:done`), for reporting or downstream tooling.
## Examples
### Modifying the config
Use the `utils` helpers on the `config:resolved` hook to change the resolved config. This example removes the `stack`
pattern.
```ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
plugins: [
{
name: 'remove-stack-pattern',
hooks: {
'config:resolved': ({ config, utils }) => {
return utils.omit(config, ['patterns.stack'])
},
},
},
],
})
```
### Modifying presets
Use the `preset:resolved` hook to change a preset after it's resolved. This is useful for filtering out parts of a
preset.
```ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
plugins: [
{
name: 'trim-preset-colors',
hooks: {
'preset:resolved': ({ utils, preset, name }) => {
if (name === '@pandacss/preset-panda') {
return utils.omit(preset, ['theme.tokens.colors', 'theme.semanticTokens.colors'])
}
return preset
},
},
},
],
})
```
### Transforming a source file before parsing
Use `parser:before` to rewrite a file's content before Panda parses it. The hook receives `{ filePath, content }` and
returns the transformed string. Return nothing to leave the content unchanged.
This is how you support source that isn't standard `tsx` β pre-process it into syntax Panda's parser understands, then
return it.
```ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
plugins: [
{
name: 'strip-directives',
hooks: {
'parser:before': ({ filePath, content }) => {
if (!filePath.endsWith('.astro')) return
return content.replace(/^---[\s\S]*?---/, '')
},
},
},
],
})
```
To scope a hook to specific files, pass an object with a `filter` and a `handler` instead of a bare function:
```ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
plugins: [
{
name: 'scoped-parser',
hooks: {
'parser:before': {
filter: { id: '**/*.{jsx,tsx}' },
handler: ({ content }) => content,
},
},
},
],
})
```
### Observing the final CSS
`cssgen:done` runs after the final CSS is produced, for the CLI, Vite, and PostCSS. It's observe-only β the hook
receives `{ artifact, content, path?, β¦ }` and its return value is ignored, so you can't rewrite the CSS here.
```ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
plugins: [
{
name: 'css-report',
hooks: {
'cssgen:done': ({ artifact, content, path }) => {
if (artifact === 'styles.css') {
console.log(`Generated ${content.length} bytes at ${path}`)
}
},
},
},
],
})
```
To strip unused tokens or keyframes from the final CSS, use the top-level `optimize` config instead of a hook:
```ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
optimize: {
removeUnusedTokens: true,
removeUnusedKeyframes: true,
},
})
```
For any other CSS transform, run PostCSS after Panda.
> Note: With `removeUnusedTokens`, you can't rely on the JS function
> [`token.var`](/docs/styling/dynamic-styling#using-tokenvar) (or [token(xxx)](/docs/styling/dynamic-styling#using-token)
> where `xxx` is a [semanticToken](/docs/theming/tokens#semantic-tokens) path) from `styled-system/tokens`, because the
> CSS variables are removed based on the usage found in the generated CSS.
## Sharing hooks
Hooks are shared as plugins. A plugin is a plain object with a `name` and a `hooks` object.
Plugins differ from `presets` in that they can't be extended, but they run in sequence in the order they appear in the
`plugins` array, with the user's own config called last.
```ts
import { defineConfig } from '@pandacss/dev'
const myPlugin = {
name: 'strip-stack',
hooks: {
'config:resolved': ({ config, utils }) => {
return utils.omit(config, ['patterns.stack'])
},
},
}
export default defineConfig({
plugins: [myPlugin],
})
```
## Reference
```ts
export interface PandaHooks {
/**
* Called after authored presets are merged, before defaults and serialization.
*/
'config:resolved': (args: ConfigResolvedHookArgs) => MaybeAsyncReturn
/**
* Called when an authored preset is resolved, before all configs are merged.
*/
'preset:resolved': (args: PresetResolvedHookArgs) => MaybeAsyncReturn
/**
* Called after reading file content but before parsing it.
* Use this to transform non-standard source into TSX-friendly syntax.
*/
'parser:before': (args: ParserResultBeforeHookArgs) => MaybeAsyncReturn
/**
* Called before generated files are written by a JS host.
*/
'codegen:prepare': (args: CodegenPrepareHookArgs) => void | CodegenPrepareArtifact[]
/**
* Called after generated files are written by a JS host.
*/
'codegen:done': (args: CodegenDoneHookArgs) => void
/**
* Called after final CSS is produced by a JS host (observe-only; no rewrite).
* Fires for CLI, Vite, and PostCSS string sinks. Use `optimize` or PostCSS to mutate CSS.
*/
'cssgen:done': (args: CssgenDoneHookArgs) => void
}
```
Each hook can be a plain function or an object with a `filter` and a `handler`, which is useful for scoping
`parser:before` to specific files:
```ts
export type PandaHook = Handler | { filter?: HookFilter; handler: Handler }
```
The argument types are:
```ts
export interface ConfigResolvedHookArgs {
config: Config
path: string
dependencies: string[]
utils: ConfigResolvedHookUtils
}
export interface PresetResolvedHookArgs {
preset: Config
name: string
utils: ConfigResolvedHookUtils
}
export interface ConfigResolvedHookUtils {
omit(obj: T, paths: string[]): T
pick(obj: T, paths: string[]): Partial
traverse(obj: unknown, callback: (item: TraverseItem) => void, options?: TraverseOptions): void
}
export interface ParserResultBeforeHookArgs {
filePath: string
content: string
original?: string
}
export interface CodegenPrepareHookArgs {
artifacts: CodegenPrepareArtifact[]
outdir: string
cwd?: string
}
export interface CodegenDoneHookArgs {
files: string[]
outdir: string
cwd?: string
}
export interface CssgenDoneHookArgs {
artifact: 'styles.css' | 'styles.layer' | 'styles.split'
content: string
/** Absolute path when written to disk; omitted for string sinks (Vite/PostCSS). */
path?: string
outfile?: string
outdir?: string
cwd?: string
manifest?: CssgenDoneManifest
layerRanges?: CssgenDoneLayerRanges
}
```
---
## Isolated declarations
Export components and recipes with variants without bloating your .d.ts files.
Say you export this button, with two variants, colocated right in your component file:
```tsx
import { styled } from 'styled-system/jsx'
export const Button = styled('button', {
base: { px: '4', rounded: 'md' },
variants: {
visual: {
solid: { bg: 'blue.500', color: 'white' },
outline: { borderWidth: '1px', borderColor: 'blue.500' }
}
}
})
```
Nothing looks wrong here. But the `.d.ts` TypeScript emits for it looks like this:
```ts
export declare const Button: {
__variants: {
visual: {
solid: { bg: string; color: string }
outline: { borderWidth: string; borderColor: string }
}
}
}
```
Every CSS value from your component, copied into the type. Add ten more variants and the `.d.ts` grows right along with
them. This page shows why that happens and the one line that stops it.
## Why the CSS ends up in your types
TypeScript has to write down some type for `Button`, and it infers one from the object literal you passed to `styled()`.
That inferred type has no name. Nothing in your code called it anything, so when TypeScript emits the declaration file,
it can't reference a name that doesn't exist. It writes out the whole structure instead, CSS values included.
This gets stricter, not just slower, once you turn on
[`isolatedDeclarations`](https://www.typescriptlang.org/tsconfig/#isolatedDeclarations). That flag requires every
exported `const` initialized by a function call to carry an explicit type. `styled(...)`, `cva(...)`, and `sva(...)` are
all function calls, so without an annotation, `isolatedDeclarations` fails the build outright. The question isn't
whether to add a type. It's whether that type is one you can actually write by hand, and right now it isn't, because the
real type is anonymous.
## The fix
Keep the object literal exactly as it is. Add one explicit annotation next to the export that names only the variant
keys, no CSS.
```tsx
import { styled, type StyledComponent } from 'styled-system/jsx'
export const Button: StyledComponent<'button', { visual?: 'solid' | 'outline' }> = styled('button', {
base: { px: '4', rounded: 'md' },
variants: {
visual: {
solid: { bg: 'blue.500', color: 'white' },
outline: { borderWidth: '1px', borderColor: 'blue.500' }
}
}
})
```
Now the emitted `.d.ts` is one line:
```ts
export declare const Button: StyledComponent<'button', { visual?: 'solid' | 'outline' }>
```
The CSS never leaves your source file. The `as` prop, style props, and `splitVariantProps` all still work exactly like
before, you've only changed what TypeScript writes down, not what the component does.
The annotation type is different depending on which function you're annotating. Here's each one.
## Annotating `styled`
Use `StyledComponent`, shown above. `Tag` is the element name as a string literal, `Props` is an object
listing each variant name and its allowed values.
## Annotating `cva`
Use `RecipeRuntimeFn`:
```tsx
import { cva } from 'styled-system/css'
import type { RecipeRuntimeFn } from 'styled-system/types'
export const button: RecipeRuntimeFn<{ visual?: 'solid' | 'outline' }> = cva({
base: { px: '4' },
variants: {
visual: {
solid: { bg: 'blue.500', color: 'white' },
outline: { borderWidth: '1px' }
}
}
})
```
`RecipeRuntimeFn` accepts a second type parameter for the variant map, but it defaults to `object`. Leave it off unless
some other code reads `button.variantMap` and needs the exact key arrays.
## Annotating `sva`
Use `SlotRecipeRuntimeFn`. The slot names come first, as a string union:
```tsx
import { sva } from 'styled-system/css'
import type { SlotRecipeRuntimeFn } from 'styled-system/types'
export const button: SlotRecipeRuntimeFn<'root' | 'icon', { visual?: 'solid' | 'outline' }> = sva({
slots: ['root', 'icon'],
base: { root: { px: '4' }, icon: { w: '4' } },
variants: {
visual: {
solid: { root: { bg: 'blue.500' } },
outline: { root: { borderWidth: '1px' } }
}
}
})
```
## One gotcha with boolean variants
Write a variant as `{ true: {...}, false: {...} }` and Panda types it as `boolean`, not the string union
`'true' | 'false'`. Match that in your annotation:
```tsx
export const button: RecipeRuntimeFn<{ disabled?: boolean }> = cva({
variants: {
disabled: {
true: { opacity: '0.5' },
false: { opacity: '1' }
}
}
})
```
Annotate it as `disabled?: 'true' | 'false'` instead and the types won't match what `cva` actually produces.
## Don't want to write the annotation at all?
If you don't want to write the annotation, move the recipe into your config the recipe in `panda.config.ts` instead of
inline, and Panda generates a named type for it. No annotation to write, no CSS in any `.d.ts`, ever:
```ts
// panda.config.ts
export default defineConfig({
theme: {
recipes: {
button: {
className: 'button',
base: { px: '4', rounded: 'md' },
variants: {
visual: {
solid: { bg: 'blue.500', color: 'white' },
outline: { borderWidth: '1px' }
}
}
}
}
}
})
```
Import the generated props type and build your component against it:
```tsx
import { button, type ButtonVariantProps } from 'styled-system/recipes'
export interface ButtonProps extends ButtonVariantProps {}
export const Button = (props: ButtonProps) => {
const [variants, rest] = button.splitVariantProps(props)
return
}
```
`ButtonVariantProps` is a real, named type with zero CSS in it, and it updates the moment you change the recipe. If
you're starting a new component and don't care about keeping the recipe colocated, this is less work than writing the
annotation yourself.
## What to watch for
You're typing the variant keys by hand, so nothing forces you to update the annotation when you add or rename a variant.
Get it wrong and the mismatch won't show up as a type error, `cva` will just silently accept props that don't exist.
Config recipes don't have this problem, since their type is generated from the same source as the CSS.
A few inline recipes can't get a clean annotation at all: variants built from spreads, computed keys, or conditionals
don't reduce to a fixed set of keys you can type. If you hit one of those, move it into your config instead of fighting
the types.
---
## Minimal Setup
How to set up Panda with the bare minimum.
The default Panda setup includes utilities and design tokens by default. In this guide, you'll see how to strip out the
defaults and start from scratch.
## Removing default tokens
To remove the default design tokens injected by Panda, set the `presets` key to an empty array:
```js
export default defineConfig({
// ...
presets: []
})
```
This allows you to define your own tokens, without having to use the `extend` key in the theme.
```js
export default defineConfig({
// ...
theme: {
tokens: {
colors: {
primary: { value: '#ff0000' }
}
}
}
})
```
## Removing default utilities
The default utilities come from `@pandacss/preset-base`. To remove them, omit `@pandacss/preset-base` from `presets` (or
use `presets: []`) β Panda doesn't add it for you.
Panda doesn't automatically know which tokens are valid for which CSS properties, so it is necessary to tell Panda that
my tokens from the "colors" category are valid for the CSS property "color".
```js
export default defineConfig({
// ...
presets: [],
utilities: {
color: {
values: 'colors'
}
},
theme: {
tokens: {
colors: {
primary: { value: '#ff0000' }
}
}
}
})
```
This makes `
Text
` work as expected.
## Re-using Panda presets
Panda offers 2 presets:
- `@pandacss/preset-base`: This is a relatively unopinionated set of utilities for mapping CSS properties to values
(almost everyone will want these)
You can use these presets by installing them via npm and adding them to your `presets` key:
```js
export default defineConfig({
// ...
presets: ['@pandacss/preset-base']
})
```
- `@pandacss/preset-panda` as an opinionated set of tokens if you don't want to define your own colors/spacing/fonts
etc.
```js
export default defineConfig({
// ...
presets: ['@pandacss/preset-panda']
})
```
> Note: Panda doesn't add `@pandacss/preset-base` or `@pandacss/preset-panda` for you. Install the ones you want and list
> them in `presets`.
---
## Monorepo dev workflow
Running Panda's watch mode across a shared package and the apps that consume it, and what to check when changes don't show up.
Once you have a [shared styled-system package](/docs/design-systems/shared-styled-system), local development usually
means running Panda's watch mode in more than one workspace package at once. This page covers what that setup looks
like and the most common way it goes wrong: a change that shows up in the DOM but not in the generated CSS until you
restart.
## Watching across a workspace boundary
An app's `include` glob can point at a sibling package's source through the workspace symlink in `node_modules`, and
Panda's watcher follows it like any other included path:
```tsx filename="apps/marketing/panda.config.ts"
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
//...
include: [
'./node_modules/@acme/design-system/src/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}'
]
})
```
Run the app's own watch command and it picks up changes in both places:
```bash
panda --watch
```
If the shared package also needs its own local dev loop, for example you're iterating on it in isolation before an
app picks up the change, give it the same script. Most monorepo component-library setups run `panda --watch` in each
package that has its own `panda.config.ts`, not just the consuming app.
## When changes don't show up without a restart
If you save a change in the shared package and the page updates but the styling doesn't, or doesn't update at all
until you restart the dev server, check these in order:
1. **Confirm Panda actually saw the change.** Run with `PANDA_DEBUG=file:*` and save the file again. If nothing
prints, Panda's watcher didn't detect it, this is a filesystem event problem, not a bundler one.
2. **Try polling.** Native filesystem events don't always fire reliably across workspace symlinks, network drives,
Docker volumes, or WSL. Panda's watch mode has a `--poll` flag for exactly this:
```bash
panda --watch --poll
```
Polling is slower than native events, so only reach for it once step 1 shows Panda isn't seeing changes with the
default watcher.
3. **Confirm your bundler's own dev server is watching the shared package too.** If step 1 shows Panda regenerated
the CSS correctly but the browser still shows the old styles, the app's dev server (Vite, webpack, whichever
you're using) may not be watching that path itself, some dev servers exclude `node_modules` (including workspace
symlinks) from their own file watcher by default, separately from whatever Panda is doing. This is a bundler
configuration question, and the exact fix depends on which one you're using, but the symptom is the same: Panda's
output on disk is correct, the running dev server just hasn't reloaded it.
## Build order
If the shared package needs to be built (not just watched) before an app can resolve its types, for example a
TypeScript project reference or a package that ships compiled `.d.ts` files rather than resolving `panda.config.ts`
output directly, start the shared package's watch process before the app's. Otherwise the app's first type-check can
fail against output that doesn't exist yet.
## See also
- [Shared styled-system in a monorepo](/docs/design-systems/shared-styled-system) for setting up the package this
workflow assumes already exists.
- [Debugging](/docs/reference/debugging) for `PANDA_DEBUG` and `panda debug` in more depth.
---
## Using Panda in a Component Library
How to build and ship a component library on Panda, from setup through packaging and troubleshooting.
A component library ships finished, styled components: a `