Skip to main content
Technology & EngineeringCss Styling Services382 lines

Panda CSS

"Panda CSS: build-time CSS-in-JS, atomic CSS, recipes, patterns, tokens, conditions, RSC compatible, type-safe styles"

Quick Summary28 lines
Panda CSS is a build-time CSS-in-JS engine that generates atomic CSS from TypeScript style definitions. It combines the developer experience of CSS-in-JS (co-located styles, type safety, design tokens) with the performance of static CSS extraction. Every style call is analyzed at build time and compiled into small, reusable atomic class names, producing highly cacheable and deduplicated stylesheets. Panda is fully compatible with React Server Components because no JavaScript is shipped to the client for styling. It provides a layered API: raw `css()` for ad-hoc styles, `recipes` for component variants, `patterns` for layout primitives, and a token system for design consistency.

## Key Points

1. **Use semantic tokens for colors.** Define `surface`, `text.primary`, `border.subtle` as semantic tokens that resolve differently in light and dark mode rather than using raw color scales.
2. **Run `panda codegen` in CI.** Include codegen as a build step and commit the `styled-system/` output so type checking works in editors and CI pipelines.
3. **Use patterns for layout, css() for visual styling.** Patterns like `stack`, `flex`, and `grid` handle spacing and layout. Use `css()` for colors, borders, and visual details.
4. **Prefer recipes for reusable components.** Recipes provide a clean variant API, enable compound variants, and keep style logic centralized.
5. **Leverage responsive arrays/objects consistently.** Use `{ base: "4", md: "6", lg: "8" }` for responsive values rather than manually writing media queries.
6. **Keep token scales small and intentional.** Avoid creating dozens of one-off token values; curate a tight scale for spacing, font sizes, and colors.
2. **Importing from `styled-system/` in server-only modules without codegen.** If codegen has not run, the imports will be missing and the build will fail.
3. **Mixing Panda with another CSS-in-JS library.** Running Emotion or styled-components alongside Panda introduces a second runtime and conflicting class-name strategies.
4. **Over-nesting conditional styles.** Deeply nested `_hover: { _dark: { _first: { ... } } }` becomes unreadable. Extract shared conditions into recipe variants instead.
5. **Ignoring the `include` configuration.** If source files are not in the `include` glob, Panda will not scan them and the generated CSS will be incomplete, causing invisible styling failures.

## Quick Example

```bash
npm install -D @pandacss/dev
npx panda init --postcss
```

```css
/* app/globals.css */
@layer reset, base, tokens, recipes, utilities;
```
skilldb get css-styling-services-skills/Panda CSSFull skill: 382 lines
Paste into your CLAUDE.md or agent config

Panda CSS

Core Philosophy

Panda CSS is a build-time CSS-in-JS engine that generates atomic CSS from TypeScript style definitions. It combines the developer experience of CSS-in-JS (co-located styles, type safety, design tokens) with the performance of static CSS extraction. Every style call is analyzed at build time and compiled into small, reusable atomic class names, producing highly cacheable and deduplicated stylesheets. Panda is fully compatible with React Server Components because no JavaScript is shipped to the client for styling. It provides a layered API: raw css() for ad-hoc styles, recipes for component variants, patterns for layout primitives, and a token system for design consistency.

Setup

Installation

npm install -D @pandacss/dev
npx panda init --postcss

Panda Configuration

// panda.config.ts
import { defineConfig } from "@pandacss/dev";

export default defineConfig({
  preflight: true,
  include: [
    "./src/**/*.{ts,tsx}",
    "./app/**/*.{ts,tsx}",
  ],
  exclude: [],
  theme: {
    extend: {
      tokens: {
        colors: {
          brand: {
            50: { value: "#eff6ff" },
            500: { value: "#3b82f6" },
            600: { value: "#2563eb" },
            700: { value: "#1d4ed8" },
            900: { value: "#1e3a8a" },
          },
        },
        fonts: {
          body: { value: "'Inter', sans-serif" },
          heading: { value: "'Cal Sans', 'Inter', sans-serif" },
        },
      },
      semanticTokens: {
        colors: {
          surface: {
            value: { base: "{colors.white}", _dark: "{colors.gray.900}" },
          },
          text: {
            primary: {
              value: { base: "{colors.gray.900}", _dark: "{colors.gray.50}" },
            },
            secondary: {
              value: { base: "{colors.gray.600}", _dark: "{colors.gray.400}" },
            },
          },
          border: {
            subtle: {
              value: { base: "{colors.gray.200}", _dark: "{colors.gray.700}" },
            },
          },
        },
      },
    },
  },
  jsxFramework: "react",
  outdir: "styled-system",
});

PostCSS Integration

// postcss.config.cjs
module.exports = {
  plugins: {
    "@pandacss/dev/postcss": {},
  },
};

CSS Entry Point

/* app/globals.css */
@layer reset, base, tokens, recipes, utilities;

Code Generation

npx panda codegen

Key Techniques

Using the css() Function

import { css } from "../../styled-system/css";

function ProfileHeader({ user }: { user: User }) {
  return (
    <header
      className={css({
        display: "flex",
        alignItems: "center",
        gap: "4",
        p: "6",
        bg: "surface",
        borderRadius: "xl",
        borderWidth: "1px",
        borderColor: "border.subtle",
        shadow: "sm",
        _hover: { shadow: "md" },
        transition: "shadows",
      })}
    >
      <img
        src={user.avatar}
        alt={user.name}
        className={css({
          w: "16",
          h: "16",
          rounded: "full",
          objectFit: "cover",
        })}
      />
      <div>
        <h1
          className={css({
            fontSize: "2xl",
            fontWeight: "bold",
            color: "text.primary",
            fontFamily: "heading",
          })}
        >
          {user.name}
        </h1>
        <p className={css({ fontSize: "sm", color: "text.secondary", mt: "1" })}>
          {user.role}
        </p>
      </div>
    </header>
  );
}

Recipes for Component Variants

// button.recipe.ts
import { cva, type RecipeVariantProps } from "../../styled-system/css";

export const buttonStyle = cva({
  base: {
    display: "inline-flex",
    alignItems: "center",
    justifyContent: "center",
    fontWeight: "medium",
    rounded: "lg",
    cursor: "pointer",
    transition: "colors",
    _focusVisible: {
      outline: "2px solid",
      outlineColor: "brand.500",
      outlineOffset: "2px",
    },
    _disabled: {
      opacity: 0.5,
      cursor: "not-allowed",
    },
  },
  variants: {
    intent: {
      primary: {
        bg: "brand.500",
        color: "white",
        _hover: { bg: "brand.600" },
        _active: { bg: "brand.700" },
      },
      secondary: {
        bg: "transparent",
        color: "text.primary",
        borderWidth: "1px",
        borderColor: "border.subtle",
        _hover: { bg: "gray.50", _dark: { bg: "gray.800" } },
      },
      danger: {
        bg: "red.500",
        color: "white",
        _hover: { bg: "red.600" },
      },
    },
    size: {
      sm: { fontSize: "sm", px: "3", py: "1.5", gap: "1.5" },
      md: { fontSize: "md", px: "4", py: "2", gap: "2" },
      lg: { fontSize: "lg", px: "6", py: "3", gap: "2.5" },
    },
  },
  defaultVariants: {
    intent: "primary",
    size: "md",
  },
});

export type ButtonVariants = RecipeVariantProps<typeof buttonStyle>;

Using Recipes in Components

import { buttonStyle, type ButtonVariants } from "./button.recipe";

type ButtonProps = ButtonVariants &
  React.ButtonHTMLAttributes<HTMLButtonElement> & {
    leftIcon?: React.ReactNode;
  };

export function Button({ intent, size, leftIcon, children, ...props }: ButtonProps) {
  return (
    <button className={buttonStyle({ intent, size })} {...props}>
      {leftIcon && <span>{leftIcon}</span>}
      {children}
    </button>
  );
}

Patterns for Layout Primitives

import { flex, grid, stack, center } from "../../styled-system/patterns";

function DashboardLayout({ children }: { children: React.ReactNode }) {
  return (
    <div className={flex({ direction: "column", minH: "dvh" })}>
      <nav className={center({ h: "16", px: "6", borderBottomWidth: "1px" })}>
        <span>Dashboard</span>
      </nav>
      <main className={flex({ flex: "1", overflow: "hidden" })}>
        <aside
          className={stack({
            gap: "2",
            w: "64",
            p: "4",
            borderRightWidth: "1px",
            hideBelow: "md",
          })}
        >
          {/* Sidebar nav items */}
        </aside>
        <section className={grid({
          columns: { base: 1, md: 2, lg: 3 },
          gap: "6",
          p: "6",
          flex: "1",
          overflow: "auto",
        })}>
          {children}
        </section>
      </main>
    </div>
  );
}

Responsive and Conditional Styles

import { css } from "../../styled-system/css";

function FeatureCard({ feature }: { feature: Feature }) {
  return (
    <article
      className={css({
        p: { base: "4", md: "6" },
        rounded: "xl",
        bg: "surface",
        borderWidth: "1px",
        borderColor: "border.subtle",
        display: "flex",
        flexDir: { base: "column", md: "row" },
        gap: { base: "3", md: "6" },
        _hover: { borderColor: "brand.500" },
        _dark: { bg: "gray.800" },
      })}
    >
      <div
        className={css({
          w: { base: "full", md: "48" },
          h: { base: "40", md: "auto" },
          rounded: "lg",
          overflow: "hidden",
          flexShrink: 0,
        })}
      >
        <img
          src={feature.image}
          alt=""
          className={css({ w: "full", h: "full", objectFit: "cover" })}
        />
      </div>
      <div className={css({ flex: "1" })}>
        <h3 className={css({ fontSize: "xl", fontWeight: "semibold", color: "text.primary" })}>
          {feature.title}
        </h3>
        <p className={css({ mt: "2", color: "text.secondary", lineHeight: "relaxed" })}>
          {feature.description}
        </p>
      </div>
    </article>
  );
}

Slot Recipes for Multi-Part Components

// card.slot-recipe.ts
import { sva } from "../../styled-system/css";

export const card = sva({
  slots: ["root", "header", "body", "footer"],
  base: {
    root: {
      bg: "surface",
      rounded: "xl",
      borderWidth: "1px",
      borderColor: "border.subtle",
      overflow: "hidden",
    },
    header: {
      px: "6",
      py: "4",
      borderBottomWidth: "1px",
      borderColor: "border.subtle",
      fontWeight: "semibold",
    },
    body: { px: "6", py: "4" },
    footer: {
      px: "6",
      py: "4",
      borderTopWidth: "1px",
      borderColor: "border.subtle",
      display: "flex",
      justifyContent: "flex-end",
      gap: "3",
    },
  },
  variants: {
    elevated: {
      true: { root: { shadow: "lg", borderWidth: "0" } },
    },
  },
});

Best Practices

  1. Use semantic tokens for colors. Define surface, text.primary, border.subtle as semantic tokens that resolve differently in light and dark mode rather than using raw color scales.
  2. Run panda codegen in CI. Include codegen as a build step and commit the styled-system/ output so type checking works in editors and CI pipelines.
  3. Use patterns for layout, css() for visual styling. Patterns like stack, flex, and grid handle spacing and layout. Use css() for colors, borders, and visual details.
  4. Prefer recipes for reusable components. Recipes provide a clean variant API, enable compound variants, and keep style logic centralized.
  5. Leverage responsive arrays/objects consistently. Use { base: "4", md: "6", lg: "8" } for responsive values rather than manually writing media queries.
  6. Keep token scales small and intentional. Avoid creating dozens of one-off token values; curate a tight scale for spacing, font sizes, and colors.

Anti-Patterns

  1. Using runtime style calculations in css(). Panda extracts styles at build time. Dynamic expressions like css({ width: \${percent}%` })will not work; use inlinestyle` for truly dynamic values.
  2. Importing from styled-system/ in server-only modules without codegen. If codegen has not run, the imports will be missing and the build will fail.
  3. Mixing Panda with another CSS-in-JS library. Running Emotion or styled-components alongside Panda introduces a second runtime and conflicting class-name strategies.
  4. Over-nesting conditional styles. Deeply nested _hover: { _dark: { _first: { ... } } } becomes unreadable. Extract shared conditions into recipe variants instead.
  5. Ignoring the include configuration. If source files are not in the include glob, Panda will not scan them and the generated CSS will be incomplete, causing invisible styling failures.

Install this skill directly: skilldb add css-styling-services-skills

Get CLI access →