Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:56:16 +08:00
commit 1702c25f85
11 changed files with 5809 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
{
"name": "panda-css-workflows",
"description": "Expert Panda CSS workflows for React + Vite projects - setup, tokens, recipes, and components",
"version": "1.2.0",
"author": {
"name": "Shaun Fox"
},
"skills": [
"./skills/"
],
"agents": [
"./agents/panda-architect.md"
]
}

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# panda-css-workflows
Expert Panda CSS workflows for React + Vite projects - setup, tokens, recipes, and components

225
agents/panda-architect.md Normal file
View File

@@ -0,0 +1,225 @@
---
name: panda-architect
description: Specialized agent for complex Panda CSS architectural work including setting up new projects, refactoring to Panda CSS, and designing component libraries
type: general-purpose
model: sonnet
tools:
- "*"
skills:
- panda-component-impl
- panda-create-stories
- panda-form-architecture
- panda-recipe-patterns
- panda-review-component
- panda-setup-config
- panda-token-architecture
---
You are a Panda CSS architecture expert specializing in React + Vite projects. You handle complex, multi-step architectural work like project setup, token system design, and CSS refactoring.
## Core Expertise
### 1. Panda CSS Configuration & Setup
- panda.config.ts architecture and best practices
- Preset creation for design systems
- Build integration with Vite
- TypeScript integration and path aliases
- strictTokens enforcement
### 2. Token Architecture
- Base tokens vs semantic tokens (two-layer system)
- Color palettes with numeric scales (0-100)
- Unified spacing/sizing scales
- Typography systems (fonts, weights, sizes, line heights)
- Theme-aware semantic tokens for light/dark modes
- Text styles for typography presets
### 3. Recipe Patterns
- Regular recipes for single-part components
- Slot recipes for multi-part components
- Variant design (size, variant, state)
- Compound variants for complex combinations
- Shared base styles between related recipes
- Dynamic variant generation from tokens
### 4. Component Implementation
- Box component as polymorphic foundation
- splitProps utility for CSS/HTML prop separation
- Recipe integration in React components
- TypeScript patterns with Panda CSS
- Accessibility best practices (ARIA, keyboard, focus)
- Icon systems and SVG sprites
### 5. Design System Patterns
- Conditions (pseudo-classes, states, responsive, container queries)
- Custom patterns (icon sizing, containers)
- Global styles and CSS reset
- Component composition strategies
## Your Working Approach
### Always Start with Planning
1. **Create a TodoWrite checklist** for any multi-step task
2. Break down complex work into clear, trackable steps
3. Mark progress as you complete each step
4. Don't skip accessibility or testing steps
### Reference Documentation When Needed
Use Context7 MCP to fetch up-to-date Panda CSS documentation:
- Ask it to resolve the library ID for "panda-css"
- Then fetch relevant docs for specific topics (setup, tokens, recipes, etc.)
- Reference official patterns when making architectural decisions
### Use Working Examples
Look at the `examples/` directory in the plugin for concrete patterns:
- **Configuration**: `panda.config.ts` - Full config with preset integration
- **Preset architecture**: `preset.ts` - Complete preset structure
- **Base tokens**: `primitives/` - Color scales, typography, sizing, animation
- **Semantic tokens**: `semantics/` - Theme-aware token layer
- **Utilities**: `utils/splitProps.ts`, `utils/ThemeContext.tsx`
- **Text styles**: `textStyles.ts`
- **Conditions**: `conditions.ts`
Read these files when you need concrete examples of implementation patterns.
### Follow Best Practices
- **strictTokens: true** - No hard-coded values allowed
- **Two-layer tokens** - Base tokens → semantic tokens
- **Semantic HTML first** - ARIA only to fill accessibility gaps
- **Visible focus states** - Use `_focusVisible` condition
- **Theme-aware tokens** - Structure: `{ base: '...', _dark: '...' }`
- **Recipe-based styling** - Avoid inline CSS prop usage
## Common Task Patterns
### Setting Up Panda CSS in a New Project
1. Verify React + Vite environment
2. Create TodoWrite checklist with setup steps
3. Install `@pandacss/dev` as dev dependency
4. Run `npx panda init --postcss`
5. Configure `panda.config.ts`:
- Set `strictTokens: true`
- Set `jsxFramework: 'react'`
- Set `jsxStyleProps: 'all'`
- Configure output paths
6. Update `vite.config.ts` with path aliases
7. Update `tsconfig.json` with path mappings
8. Add Panda build scripts to package.json
9. Create initial token structure (colors, spacing, typography)
10. Build and validate with a test component
### Designing a Complete Token System
1. Create TodoWrite checklist for token architecture
2. Define **base tokens**:
- Color scales (0-100 or similar numeric system)
- Spacing scale (unified for spacing, sizes, radii)
- Typography tokens (fonts, weights, sizes, line heights)
- Animation tokens (durations, easings)
3. Create **semantic token layer**:
- Reference base tokens via `{colors.name.shade}` syntax
- Define theme-aware tokens: `{ base: '...', _dark: '...' }`
- Organize by purpose (backgrounds, borders, text, etc.)
4. Set up **text styles** for typography presets
5. Configure responsive breakpoints
6. Create example components/recipes to validate tokens
7. Enable `strictTokens` and verify no violations
### Creating Component Recipes
1. Determine recipe type (regular vs slot)
2. Define base styles (shared foundations)
3. Create variants (size, variant, visual state)
4. Add compound variants for complex combinations
5. Set default variants
6. Extract shared bases if multiple related recipes
7. Test all variant combinations
8. Verify theme switching works correctly
### Building React Components with Recipes
1. Import recipe and variant types
2. Use Box component as polymorphic base if needed
3. Apply recipe with variant props
4. Use splitProps to separate CSS from HTML props
5. Add TypeScript types (props + variant types)
6. Implement accessibility:
- Semantic HTML elements
- ARIA attributes where needed
- Keyboard navigation
- Focus management with `_focusVisible`
7. Test in light and dark themes
### Refactoring Existing CSS to Panda
1. Create TodoWrite checklist for migration
2. Audit existing styles and extract all design values
3. Create Panda token system from extracted values
4. Convert component styles to recipes (one component at a time)
5. Update component implementations to use recipes
6. Enable `strictTokens: true` and fix violations
7. Run visual regression tests to validate parity
8. Remove old CSS-in-JS dependencies
## Key Architecture Decisions
**Config Structure**:
```typescript
// Separate concerns into modules
import { tokens } from './tokens'
import { semanticTokens } from './semantic-tokens'
import { recipes } from './recipes'
import { textStyles } from './text-styles'
export default defineConfig({
strictTokens: true,
jsxFramework: 'react',
theme: { extend: { tokens, semanticTokens, recipes, textStyles } }
})
```
**Token Organization**:
```typescript
// Base tokens: numeric scales
colors: {
brand: { 0: '#fff', 10: '#...', ..., 100: '#000' }
}
// Semantic tokens: reference base
colors: {
bg: {
primary: { base: '{colors.brand.10}', _dark: '{colors.brand.90}' }
}
}
```
**Recipe Structure**:
```typescript
// Extract shared bases
const buttonBase = { ... }
export const button = defineRecipe({
base: buttonBase,
variants: { size: {...}, variant: {...} },
defaultVariants: { size: 'md', variant: 'solid' }
})
```
**Component Pattern**:
```typescript
import { button, type ButtonVariants } from '../styled-system/recipes'
import { splitProps } from '../utils/splitProps'
type ButtonProps = ButtonVariants & React.ComponentProps<'button'>
export const Button = (props: ButtonProps) => {
const [variantProps, htmlProps] = splitProps(props, ['size', 'variant'])
return <button className={button(variantProps)} {...htmlProps} />
}
```
## Your Communication Style
- **Proactive**: Anticipate needs and suggest improvements
- **Systematic**: Use TodoWrite for complex work, check off progress
- **Thorough**: Don't skip steps, especially accessibility and testing
- **Practical**: Show concrete code examples, not just theory
- **Educational**: Explain *why* certain patterns are best practices
When you complete a task, summarize what was done, point out key decisions made, and suggest next steps if appropriate.

73
plugin.lock.json Normal file
View File

@@ -0,0 +1,73 @@
{
"$schema": "internal://schemas/plugin.lock.v1.json",
"pluginId": "gh:shaunrfox/okshaun-claude-marketplace:panda-css-workflows",
"normalized": {
"repo": null,
"ref": "refs/tags/v20251128.0",
"commit": "93e30a6a26bb8ea14afb4f0c9da39a101c2a39a5",
"treeHash": "fa508dd0b281e8f377b82c767b21a21e992696ff177086acb972369fba6c1f60",
"generatedAt": "2025-11-28T10:28:17.959769Z",
"toolVersion": "publish_plugins.py@0.2.0"
},
"origin": {
"remote": "git@github.com:zhongweili/42plugin-data.git",
"branch": "master",
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
},
"manifest": {
"name": "panda-css-workflows",
"description": "Expert Panda CSS workflows for React + Vite projects - setup, tokens, recipes, and components",
"version": "1.2.0"
},
"content": {
"files": [
{
"path": "README.md",
"sha256": "ce5eeb7d0b08ec8420a33aab420cee9942957205e26eb57784151582ae40aa66"
},
{
"path": "agents/panda-architect.md",
"sha256": "9959e4d98b17c4d7bbfaa8d7aa4e25f4390e2d841a3e370cea75f6c79076c58a"
},
{
"path": ".claude-plugin/plugin.json",
"sha256": "03be85148fed92f785b4578cea894f3a7e372c4bc11cb17e54ddc882200be0ce"
},
{
"path": "skills/panda-token-architecture.md",
"sha256": "07fbaf523c37ac1d5f43f21153aa4d06e8507e375a8b4b03e932b2c9f48d9e7d"
},
{
"path": "skills/panda-form-architecture.md",
"sha256": "26d971369550ad0903ac46591927dd7c49b6ff260101f1f58ae2a300b41225fa"
},
{
"path": "skills/panda-review-component.md",
"sha256": "88ab46bbaab0ae957324d933f45071799bad2281ea3e3b07c0d438184d02f0cf"
},
{
"path": "skills/panda-setup-config.md",
"sha256": "ae7b6457a418f10e49642b1e4d02c22bccf715d5866dceb964ae349a6d8320ae"
},
{
"path": "skills/panda-create-stories.md",
"sha256": "26bb1309c8464d254437bc7bbd95d2b1097a994deae1b69d2734ca63973f7c83"
},
{
"path": "skills/panda-recipe-patterns.md",
"sha256": "a9b4b5f3a6e4cbc8b4160462c092a7ca7cf176ecfb0f5d7d6b37d8c3d7aebbde"
},
{
"path": "skills/panda-component-impl.md",
"sha256": "756c833f36b739c5048383a3ea51fe864b06af108c6b35a1e85bcdbaf04e11de"
}
],
"dirSha256": "fa508dd0b281e8f377b82c767b21a21e992696ff177086acb972369fba6c1f60"
},
"security": {
"scannedAt": null,
"scannerVersion": null,
"flags": []
}
}

View File

@@ -0,0 +1,709 @@
---
name: panda-component-impl
description: Build React components that properly use Panda CSS patterns, recipes, TypeScript integration, and accessibility best practices
---
# Panda CSS Component Implementation
## When to Use This Skill
Use this skill when:
- Building React components with Panda CSS styling
- Implementing recipe-based component variants
- Creating polymorphic components (components that can render as different elements)
- Integrating TypeScript with Panda CSS types
- Implementing accessible components with Panda CSS
- Setting up component file structure
For creating the recipes themselves, use the **panda-recipe-patterns** skill first.
## Component File Structure
```
src/
components/
Button/
Button.tsx # Component implementation
index.tsx # Public exports
Button.stories.tsx # Storybook documentation (optional)
Icon/
Icon.tsx
index.tsx
svg/ # SVG source files (for icon systems)
CheckBox/
CheckBox.tsx
index.tsx
```
**Pattern**: Each component in its own directory with implementation + exports.
## Base Component Pattern (Box)
The Box component is the foundation - a polymorphic element that accepts all Panda CSS style props.
Create: `src/components/Box/Box.tsx`
```typescript
import { createElement } from 'react'
import type { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react'
import { cx } from '@styled-system/css'
import { box } from '@styled-system/patterns'
import type { SystemStyleObject } from '@styled-system/types'
import { splitProps } from '~/utils/splitProps'
// Box can render as any HTML element
export type BoxProps =
Omit<ComponentPropsWithoutRef<ElementType>, 'as'> &
SystemStyleObject & // Enable all Panda CSS style props
{
as?: ElementType
children?: ReactNode
}
export const Box = ({ as = 'div', ...props }: BoxProps) => {
// Separate Panda CSS props from HTML props
const [className, otherProps] = splitProps(props)
// Combine box pattern with custom className
const comboClassName = cx(box({}), className)
return createElement(as, { className: comboClassName, ...otherProps })
}
```
**Key Points**:
- `as` prop: Polymorphic rendering (div, button, a, span, etc.)
- `SystemStyleObject`: Enables all Panda CSS style props (bg, px, fontSize, etc.)
- `splitProps`: Utility to separate CSS props from HTML props
- `createElement`: Dynamic element creation based on `as` prop
## The splitProps Utility
Critical utility for separating Panda CSS props from HTML attributes.
Create: `src/utils/splitProps.ts`
```typescript
import { cx, css, splitCssProps } from '@styled-system/css'
/**
* Splits component props into Panda CSS props and HTML props.
* Returns [className, otherProps].
*/
export const splitProps = (
props: Record<string, any>
): [string, Record<string, any>] => {
// Panda's utility: splits CSS props from other props
const [cssProps, otherProps] = splitCssProps(props)
// Extract css prop separately
const { css: cssProp, ...styleProps } = cssProps
// Generate className from CSS props
const generatedClassName = css(cssProp, styleProps)
// Merge with existing className if present
const existingClassName = otherProps.className || ''
const mergedClassName = cx(existingClassName, generatedClassName)
// Remove className from otherProps (it's now in mergedClassName)
const { className, ...remainingProps } = otherProps
return [mergedClassName, remainingProps]
}
```
**Why**: Enables inline style props on components while keeping clean HTML output.
**Usage**:
```typescript
// Component receives both CSS and HTML props
<Button bg="blue.50" px="20" onClick={handleClick} disabled>
// splitProps separates them:
// cssProps: { bg: 'blue.50', px: '20' }
// htmlProps: { onClick: handleClick, disabled: true }
```
## Recipe-Based Components
### Simple Recipe Component
Create: `src/components/Button/Button.tsx`
```typescript
import { type FC } from 'react'
import { cx } from '@styled-system/css'
import { button, type ButtonVariantProps } from '@styled-system/recipes'
import { Box, type BoxProps } from '../Box/Box'
import { splitProps } from '~/utils/splitProps'
export type ButtonProps =
BoxProps &
ButtonVariantProps &
{
loading?: boolean
disabled?: boolean
href?: string
}
export const Button: FC<ButtonProps> = ({
variant,
size,
loading = false,
disabled = false,
href,
...props
}) => {
// Separate Panda CSS props from HTML props
const [className, otherProps] = splitProps(props)
// Determine element type
const as = href ? 'a' : 'button'
// Combine recipe className with custom className
const comboClassName = cx(
button({ variant, size }), // Recipe styles
className // Custom overrides
)
return (
<Box
as={as}
className={comboClassName}
disabled={loading || disabled}
href={href}
{...otherProps}
/>
)
}
```
**Pattern Breakdown**:
1. Import recipe and its variant types from `@styled-system/recipes`
2. Extend `BoxProps` with `ButtonVariantProps` for full type safety
3. Use `splitProps` to separate CSS from HTML props
4. Apply recipe with `button({ variant, size })`
5. Merge recipe className with custom className using `cx`
6. Pass to Box component for rendering
### Slot Recipe Component
Multi-part components use slot recipes.
Create: `src/components/CheckBox/CheckBox.tsx`
```typescript
import { type FC, type InputHTMLAttributes } from 'react'
import { checkbox, type CheckboxVariantProps } from '@styled-system/recipes'
import { Box } from '../Box/Box'
import { Icon } from '../Icon/Icon'
export type CheckBoxProps =
Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> &
CheckboxVariantProps &
{
label?: string
indeterminate?: boolean
error?: boolean
}
export const CheckBox: FC<CheckBoxProps> = ({
size,
label,
indeterminate = false,
error = false,
checked,
...props
}) => {
// Get slot class names from recipe
const { container, input, indicator } = checkbox({ size })
return (
<Box as="label" className={container}>
<Box
as="input"
type="checkbox"
className={input}
checked={checked}
// Data attributes for custom states
{...(indeterminate && { 'data-indeterminate': true })}
{...(error && { 'data-error': true })}
{...props}
/>
{/* Different icons for different states */}
<Icon className={indicator} name="checkbox" data-state="unchecked" />
<Icon className={indicator} name="checkbox-checked" data-state="checked" />
<Icon className={indicator} name="checkbox-indeterminate" data-state="indeterminate" />
{label && (
<Box as="span" className={checkbox().label}>
{label}
</Box>
)}
</Box>
)
}
```
**Slot Recipe Pattern**:
1. Destructure slot classes: `{ container, input, indicator }`
2. Apply each slot class to corresponding element
3. Use data attributes for custom states: `data-indeterminate`, `data-error`
4. Recipe CSS targets these data attributes via conditions
### Component with Conditional Rendering
Create: `src/components/Button/Button.tsx` (with loading state)
```typescript
import { type FC, type ReactNode } from 'react'
import { cx } from '@styled-system/css'
import { button, type ButtonVariantProps } from '@styled-system/recipes'
import { Box, type BoxProps } from '../Box/Box'
import { Spinner } from '../Spinner/Spinner'
import { splitProps } from '~/utils/splitProps'
export type ButtonProps =
BoxProps &
ButtonVariantProps &
{
loading?: boolean
leftIcon?: ReactNode
rightIcon?: ReactNode
children: ReactNode
}
export const Button: FC<ButtonProps> = ({
variant,
size,
loading = false,
disabled = false,
leftIcon,
rightIcon,
children,
...props
}) => {
const [className, otherProps] = splitProps(props)
return (
<Box
as="button"
className={cx(button({ variant, size }), className)}
disabled={loading || disabled}
aria-busy={loading} // Accessibility: announce loading state
{...otherProps}
>
{/* Show spinner when loading */}
{loading && <Spinner size={size} />}
{/* Show left icon if not loading */}
{!loading && leftIcon}
{/* Button text */}
<span>{children}</span>
{/* Right icon */}
{!loading && rightIcon}
</Box>
)
}
```
## TypeScript Patterns
### Extract Recipe Types
```typescript
import { button, type ButtonVariantProps } from '@styled-system/recipes'
// ButtonVariantProps includes:
// - variant?: 'primary' | 'secondary' | 'outline' | 'ghost'
// - size?: 'small' | 'medium' | 'large'
```
**Pattern**: Always use generated variant types for prop types.
### Omit Conflicting Props
```typescript
import { text, type TextVariantProps } from '@styled-system/recipes'
// Avoid prop conflicts between Box and recipe
export type TextProps =
Omit<BoxProps, keyof TextVariantProps> & // Remove conflicts
TextVariantProps &
{
children: ReactNode
}
```
**Why**: Prevents TypeScript errors when Box and recipe define same props.
### Conditional Value Types
For responsive/theme-aware props:
```typescript
import { type ConditionalValue } from '@styled-system/types'
import { type ColorToken } from '@styled-system/tokens'
export type IconProps = {
fill?: ConditionalValue<ColorToken> // Enables: fill="blue.50" or fill={{ base: 'blue.50', _dark: 'blue.40' }}
}
```
### Component Props Pattern
```typescript
import { type ComponentPropsWithoutRef } from 'react'
// Get all props for a specific HTML element
export type InputProps = ComponentPropsWithoutRef<'input'> & {
// Custom props
}
// For polymorphic components
export type BoxProps = ComponentPropsWithoutRef<ElementType> & {
as?: ElementType
}
```
## Accessibility Patterns
### Always Include focusVisible
In recipes:
```typescript
base: {
_focusVisible: {
outlineWidth: '2',
outlineOffset: '1',
outlineColor: { base: 'blue.50', _dark: 'blue.40' }
}
}
```
**Why**: Provides visible focus indication for keyboard navigation.
### ARIA Attributes
```typescript
export const Button: FC<ButtonProps> = ({ loading, disabled, ...props }) => {
return (
<button
disabled={loading || disabled}
aria-disabled={disabled}
aria-busy={loading}
{...props}
/>
)
}
```
**Common ARIA Attributes**:
- `aria-label`: Label for screen readers
- `aria-disabled`: Disabled state
- `aria-busy`: Loading state
- `aria-checked`: Checkbox/radio state
- `aria-expanded`: Collapsed/expanded state
- `aria-pressed`: Toggle button state
### Keyboard Interaction
```typescript
export const MenuItem: FC<MenuItemProps> = ({ onClick, ...props }) => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === ' ' || e.key === 'Spacebar' || e.key === 'Enter') {
e.preventDefault()
onClick?.(e)
}
}
return (
<Box
role="button"
tabIndex={0}
onKeyDown={handleKeyDown}
onClick={onClick}
{...props}
/>
)
}
```
**Why**: Ensure keyboard users can interact with custom components.
### Match Multiple State Selectors
In recipes, support both native and custom states:
```typescript
conditions: {
checked: '&:is(:checked, [data-checked], [aria-checked=true], [data-state="checked"])'
}
```
**Why**: Works with native inputs AND custom components.
## Icon Component Pattern
Icons often need special handling for sizing and color.
Create: `src/components/Icon/Icon.tsx`
```typescript
import { type FC } from 'react'
import { cx } from '@styled-system/css'
import { icon } from '@styled-system/patterns'
import type { ConditionalValue } from '@styled-system/types'
import type { ColorToken } from '@styled-system/tokens'
import { Box, type BoxProps } from '../Box/Box'
import { splitProps } from '~/utils/splitProps'
import { numericSizes } from '~/styles/tokens'
// Constrain size to numeric tokens only
export type AllowedIconSizes = keyof typeof numericSizes
export type IconProps =
Omit<BoxProps, 'size'> & // Remove BoxProps size
{
name: string // Icon identifier
size?: AllowedIconSizes
fill?: ConditionalValue<ColorToken>
}
export const Icon: FC<IconProps> = ({
name,
size = '24',
fill = 'currentColor',
...props
}) => {
const [className, otherProps] = splitProps(props)
return (
<Box
as="svg"
viewBox="0 0 24 24"
className={cx(icon({ size }), className)} // icon pattern sets width + height
fill="none"
stroke={fill === 'currentColor' ? fill : undefined}
{...otherProps}
>
{/* SVG sprite reference */}
<use xlinkHref={`/sprite.svg#${name}`} />
</Box>
)
}
```
**Pattern**: Custom icon pattern enforces square sizing via tokens.
## Responsive Components
### Responsive Props
Use object syntax for breakpoint-based props:
```typescript
<Box
px={{ base: '16', md: '20', lg: '24' }}
fontSize={{ base: 'sm', md: 'md' }}
display={{ base: 'block', lg: 'flex' }}
/>
```
### Container Queries
For component-level responsive design:
```typescript
<Box
containerType="inline-size" // Enable container queries
width="full"
>
<Box
// Responsive based on CONTAINER size, not viewport
p={{ base: '12', '@container(min-width: 400px)': '20' }}
/>
</Box>
```
## Component Composition
### Compose with Box
```typescript
export const Card: FC<BoxProps> = (props) => {
return (
<Box
bg={{ base: 'white', _dark: 'slate.90' }}
borderRadius="8"
boxShadow="md"
p="20"
{...props} // Allow overrides
/>
)
}
```
**Pattern**: Provide sensible defaults, allow prop overrides.
### Compound Components
```typescript
// Menu.tsx
export const Menu: FC<MenuProps> = ({ children, ...props }) => {
const { container } = menu()
return <Box className={container} {...props}>{children}</Box>
}
// MenuItem.tsx
export const MenuItem: FC<MenuItemProps> = ({ children, ...props }) => {
const { item } = menu() // Access same recipe
return <Box className={item} {...props}>{children}</Box>
}
// Usage
<Menu>
<MenuItem>Item 1</MenuItem>
<MenuItem>Item 2</MenuItem>
</Menu>
```
## Best Practices Checklist
Create TodoWrite items when building components:
- [ ] Use Box as foundation for polymorphic components
- [ ] Apply splitProps to separate CSS from HTML props
- [ ] Import and use recipe variant types for TypeScript
- [ ] Include ARIA attributes for accessibility
- [ ] Add keyboard interaction for custom interactive elements
- [ ] Use _focusVisible for visible focus states
- [ ] Test component in light AND dark themes
- [ ] Validate all variant combinations work correctly
- [ ] Test with keyboard-only navigation
- [ ] Test with screen reader (basic check)
## Common Pitfalls
### Avoid: Mixing CSS Approaches
```typescript
// BAD: Mixing inline styles, Panda props, and classes
<Box
style={{ backgroundColor: 'red' }} // Inline style (avoid)
bg="blue.50" // Panda CSS (good)
className="custom-class" // External CSS (avoid)
/>
// GOOD: Use Panda CSS exclusively
<Box bg="blue.50" px="20" />
```
### Avoid: Not Using Recipe Types
```typescript
// BAD: Manual prop types (out of sync with recipe)
type ButtonProps = {
variant?: 'primary' | 'secondary'
}
// GOOD: Use generated types
import { type ButtonVariantProps } from '@styled-system/recipes'
type ButtonProps = ButtonVariantProps
```
### Avoid: Missing Accessibility
```typescript
// BAD: No keyboard support, no ARIA
<div onClick={handleClick}>Click me</div>
// GOOD: Proper semantics and keyboard support
<button onClick={handleClick} aria-label="Action button">
Click me
</button>
```
### Avoid: Over-wrapping
```typescript
// BAD: Unnecessary div wrappers
<Box>
<Box>
<Box>Content</Box>
</Box>
</Box>
// GOOD: Minimal, semantic structure
<Box>Content</Box>
```
## Accessing Official Panda CSS Docs
For component implementation patterns:
1. **Resolve library ID**: `mcp__MCP_DOCKER__resolve-library-id` with `libraryName: "panda-css"`
2. **Fetch docs**: `mcp__MCP_DOCKER__get-library-docs` with:
- `topic: "recipes"` - Using recipes in components
- `topic: "typescript"` - TypeScript patterns
- `topic: "patterns"` - Built-in patterns
## Exporting Components
Create: `src/components/Button/index.tsx`
```typescript
export { Button } from './Button'
export type { ButtonProps } from './Button'
```
Create: `src/index.ts` (main library export)
```typescript
// Components
export { Box } from './components/Box'
export { Button, IconButton } from './components/Button'
export { Icon } from './components/Icon'
export { CheckBox } from './components/CheckBox'
// ... more components
// Types
export type { BoxProps } from './components/Box'
export type { ButtonProps, IconButtonProps } from './components/Button'
export type { IconProps } from './components/Icon'
// ... more types
```
**Pattern**: Export both components and their prop types for consuming projects.
## Working Examples
Reference these files in the `examples/` directory for production-tested patterns:
**Utility Functions:**
- `examples/utils/splitProps.ts` - CSS/HTML prop separation utility
```typescript
// Separates Panda CSS props from HTML props
const [className, htmlProps] = splitProps(props);
// Uses splitCssProps, css(), and cx() to merge classNames
```
- `examples/utils/ThemeContext.tsx` - Theme provider with localStorage persistence
```typescript
// Manages light/dark theme with system preference detection
// Persists user preference to localStorage
// Applies theme class to document root
```
**Token & Configuration Integration:**
- `examples/preset.ts` - Shows how to integrate tokens with components via preset
- `examples/conditions.ts` - Custom conditions for component states
- `examples/textStyles.ts` - Typography presets for text components
**For Complete Component Examples:**
While this plugin focuses on architecture patterns, you can reference:
- The skills themselves (this file, panda-recipe-patterns) contain inline component examples
- Use the **panda-architect** agent for full component implementations

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,930 @@
---
name: panda-form-architecture
description: Design and implement composable form component architectures using atomic design principles with Panda CSS
---
# Panda CSS Form Architecture
## When to Use This Skill
Use this skill when:
- Building a form component system from scratch
- Refactoring existing forms to use composable patterns
- Creating form field wrappers for consistent accessibility and error handling
- Implementing a design system's form components
- Establishing form component hierarchy and composition patterns
For implementing individual form components (buttons, inputs), also reference **panda-component-impl**.
For creating recipes for these components, use **panda-recipe-patterns**.
## Form Component Composability Philosophy
Form components should follow a progressive composition model where simpler components combine into more complex ones. This atomic design approach creates:
- **Reusability**: Foundational components work in multiple contexts
- **Consistency**: Shared primitives ensure visual and behavioral uniformity
- **Flexibility**: Compose components differently for different use cases
- **Maintainability**: Changes to primitives cascade to all consumers
## Three-Layer Architecture
### Layer 1: Atomic Components (Primitives)
The foundational styled elements that map directly to HTML form controls.
**Characteristics:**
- Single responsibility (one HTML element)
- No internal composition
- Accept Panda CSS style props for customization
- Minimal logic (mostly styling)
**Components:**
```typescript
<Box> // Polymorphic base (any HTML element)
<Button> // Styled <button> or <a>
<TextInput> // Styled <input type="text">
<Textarea> // Styled <textarea>
<CheckBox> // Styled <input type="checkbox">
<Toggle> // Styled <input type="checkbox"> for toggle switches
<Radio> // Styled <input type="radio">
<Text> // Styled text for labels, help text, errors
<Label> // Styled <label> element
```
**Example Implementation (Atomic):**
Create: `src/components/TextInput/TextInput.tsx`
```typescript
import { type FC, type InputHTMLAttributes } from 'react'
import { cx } from '@styled-system/css'
import { textInput, type TextInputVariantProps } from '@styled-system/recipes'
import { Box, type BoxProps } from '../Box/Box'
import { splitProps } from '~/utils/splitProps'
export type TextInputProps =
Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> &
BoxProps &
TextInputVariantProps &
{
error?: boolean
}
export const TextInput: FC<TextInputProps> = ({
size,
error = false,
...props
}) => {
const [className, otherProps] = splitProps(props)
return (
<Box
as="input"
type="text"
className={cx(textInput({ size }), className)}
{...(error && { 'data-error': true })}
aria-invalid={error}
{...otherProps}
/>
)
}
```
**Key Points:**
- Simple wrapper around native input
- Applies Panda CSS recipe for styling
- Supports error state via data attribute
- Includes ARIA attribute for accessibility
- Allows style overrides via props
### Layer 2: Molecular Components (Composed Primitives)
Combine atomic components for common patterns. These handle basic composition without complex logic.
**Characteristics:**
- Combine 2-3 atomic components
- Handle common use cases (input + label)
- Still relatively simple
- Improve ergonomics (less boilerplate for consumers)
**Components:**
```typescript
<ToggleInput> // Toggle + Label
<CheckboxInput> // CheckBox + Label
<RadioInput> // Radio + Label
```
**Example Implementation (Molecular):**
Create: `src/components/CheckboxInput/CheckboxInput.tsx`
```typescript
import { type FC, type InputHTMLAttributes, type ReactNode } from 'react'
import { cx } from '@styled-system/css'
import { checkboxInput } from '@styled-system/recipes'
import { CheckBox, type CheckBoxProps } from '../CheckBox/CheckBox'
import { Box } from '../Box/Box'
import { Text } from '../Text/Text'
export type CheckboxInputProps =
Omit<CheckBoxProps, 'label'> &
{
label?: ReactNode
description?: string
}
export const CheckboxInput: FC<CheckboxInputProps> = ({
label,
description,
size,
id,
...props
}) => {
// Generate ID if not provided (for label/input association)
const inputId = id || `checkbox-${Math.random().toString(36).substr(2, 9)}`
const { container, labelText, descriptionText } = checkboxInput({ size })
return (
<Box as="label" htmlFor={inputId} className={container}>
<CheckBox
id={inputId}
size={size}
{...props}
/>
{label && (
<Box className={labelText}>
{label}
</Box>
)}
{description && (
<Text className={descriptionText} color="gray.60">
{description}
</Text>
)}
</Box>
)
}
```
**Pattern Breakdown:**
1. Wraps CheckBox (atomic) with label and description
2. Auto-generates ID for accessibility if not provided
3. Uses slot recipe for layout styling
4. Accepts ReactNode for flexible label content
5. Optional description for additional context
**When to Use Molecular vs Atomic:**
- Use **atomic** when you need maximum flexibility and custom layouts
- Use **molecular** for standard form layouts (label beside/above input)
- Both should be available in your component library
### Layer 3: Organism Components (Complex Wrappers)
Higher-level components that provide structure, accessibility features, and common patterns like error handling.
**Characteristics:**
- Orchestrate multiple components
- Provide consistent patterns (labels, help text, errors)
- Handle accessibility concerns (ARIA attributes, ID linking)
- Accept children for maximum flexibility
**Primary Component: FormField**
`FormField` is a critical wrapper that provides:
- Consistent label/input/error layout
- Automatic accessibility (aria-describedby, aria-invalid)
- Error and help text display
- Required field indication
**Example Implementation (Organism):**
Create: `src/components/FormField/FormField.tsx`
```typescript
import { type FC, type ReactNode, type ReactElement, cloneElement, isValidElement } from 'react'
import { formField } from '@styled-system/recipes'
import { Box } from '../Box/Box'
import { Label } from '../Label/Label'
import { Text } from '../Text/Text'
export type FormFieldProps = {
label: string
helpText?: string
errorText?: string
required?: boolean
children: ReactNode
htmlFor?: string
}
export const FormField: FC<FormFieldProps> = ({
label,
helpText,
errorText,
required = false,
children,
htmlFor,
}) => {
// Generate IDs for accessibility linking
const fieldId = htmlFor || `field-${Math.random().toString(36).substr(2, 9)}`
const helpTextId = `${fieldId}-help`
const errorTextId = `${fieldId}-error`
const { container, labelSlot, helpTextSlot, errorTextSlot } = formField()
// Clone children to inject ARIA attributes
const enhancedChildren = isValidElement(children)
? cloneElement(children as ReactElement<any>, {
id: fieldId,
'aria-describedby': [
helpText ? helpTextId : null,
errorText ? errorTextId : null,
]
.filter(Boolean)
.join(' ') || undefined,
'aria-invalid': !!errorText,
'aria-required': required,
})
: children
return (
<Box className={container}>
<Label htmlFor={fieldId} className={labelSlot}>
{label}
{required && (
<Text as="span" color="red.50" aria-label="required">
{' '}*
</Text>
)}
</Label>
{helpText && (
<Text id={helpTextId} className={helpTextSlot} color="gray.60">
{helpText}
</Text>
)}
{enhancedChildren}
{errorText && (
<Text
id={errorTextId}
className={errorTextSlot}
color="red.50"
role="alert"
>
{errorText}
</Text>
)}
</Box>
)
}
```
**Advanced Pattern Breakdown:**
1. **Auto-generates IDs**: Ensures proper label/input/error association
2. **Clones children**: Injects ARIA attributes into child input
3. **aria-describedby**: Links input to help text and errors
4. **aria-invalid**: Marks input as invalid when error present
5. **aria-required**: Indicates required fields to screen readers
6. **role="alert"**: Announces errors to screen readers immediately
**Accessibility Features:**
- Proper label/input association via `htmlFor` and `id`
- Help text linked via `aria-describedby`
- Error text linked via `aria-describedby` and marked as `role="alert"`
- Required fields indicated both visually (*) and semantically
- Screen reader support through proper ARIA attributes
## Full Form Implementation Example
Here's how all three layers compose into a complete, accessible form:
Create: `src/pages/UserProfileForm.tsx`
```typescript
import { type FC, type FormEvent, useState } from 'react'
import { Box } from '~/components/Box/Box'
import { FormField } from '~/components/FormField/FormField'
import { TextInput } from '~/components/TextInput/TextInput'
import { Textarea } from '~/components/Textarea/Textarea'
import { RadioInput } from '~/components/RadioInput/RadioInput'
import { CheckboxInput } from '~/components/CheckboxInput/CheckboxInput'
import { Button } from '~/components/Button/Button'
export const UserProfileForm: FC = () => {
const [errors, setErrors] = useState<Record<string, string>>({})
const favColors = ['blue', 'red', 'yellow', 'green']
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
// Form validation logic here
const formData = new FormData(e.currentTarget)
// Example validation
const firstName = formData.get('firstName') as string
if (!firstName) {
setErrors({ firstName: 'First name is required' })
return
}
// Submit form...
}
return (
<Box
as="form"
onSubmit={handleSubmit}
display="flex"
flexDirection="column"
gap="24"
maxWidth="600px"
margin="0 auto"
p="32"
>
<FormField
label="First name"
required
errorText={errors.firstName}
helpText="Enter your legal first name"
>
<TextInput
name="firstName"
placeholder="John"
error={!!errors.firstName}
/>
</FormField>
<FormField
label="Last name"
required
errorText={errors.lastName}
>
<TextInput
name="lastName"
placeholder="Doe"
error={!!errors.lastName}
/>
</FormField>
<FormField
label="Bio"
helpText="Tell us about yourself (optional)"
>
<Textarea
name="bio"
placeholder="I'm a developer who loves..."
rows={4}
/>
</FormField>
<FormField label="Favorite color" required>
<Box display="flex" flexDirection="column" gap="12">
{favColors.map((color) => (
<RadioInput
key={color}
name="favoriteColor"
value={color}
label={color.charAt(0).toUpperCase() + color.slice(1)}
/>
))}
</Box>
</FormField>
<CheckboxInput
name="newsletter"
label="Subscribe to newsletter"
description="Receive updates about new features and releases"
/>
<CheckboxInput
name="terms"
label="I agree to the terms and conditions"
required
/>
<Box display="flex" justifyContent="flex-end" gap="12">
<Button type="button" variant="secondary">
Cancel
</Button>
<Button type="submit" variant="primary">
Save Profile
</Button>
</Box>
</Box>
)
}
```
**Form Pattern Highlights:**
1. **FormField wrapper**: Consistent layout for all fields
2. **Error handling**: Centralized error state, passed to FormField
3. **Help text**: Contextual guidance for users
4. **Required indicators**: Visual and semantic marking
5. **Accessible structure**: Proper labels, ARIA attributes, semantic HTML
6. **Flexible composition**: Mix atomic and molecular components as needed
7. **Layout control**: Panda CSS props for spacing and arrangement
## Recipe Architecture for Form Components
### Atomic Component Recipes
Each atomic component should have its own recipe:
Create: `src/styles/recipes/text-input.recipe.ts`
```typescript
import { defineRecipe } from '@pandacss/dev'
export const textInputRecipe = defineRecipe({
className: 'textInput',
description: 'Text input field styles',
base: {
width: 'full',
px: '12',
py: '8',
fontSize: 'md',
fontFamily: 'body',
borderWidth: '1',
borderColor: { base: 'gray.30', _dark: 'gray.70' },
borderRadius: '6',
bg: { base: 'white', _dark: 'slate.90' },
color: { base: 'gray.90', _dark: 'gray.10' },
transition: 'all 0.2s',
_placeholder: {
color: { base: 'gray.50', _dark: 'gray.60' },
},
_focus: {
outline: 'none',
borderColor: { base: 'blue.50', _dark: 'blue.40' },
boxShadow: '0 0 0 3px token(colors.blue.20)',
},
_disabled: {
opacity: 0.5,
cursor: 'not-allowed',
bg: { base: 'gray.10', _dark: 'gray.80' },
},
// Error state
'&[data-error=true]': {
borderColor: { base: 'red.50', _dark: 'red.40' },
_focus: {
boxShadow: '0 0 0 3px token(colors.red.20)',
},
},
},
variants: {
size: {
sm: {
px: '8',
py: '6',
fontSize: 'sm',
},
md: {
px: '12',
py: '8',
fontSize: 'md',
},
lg: {
px: '16',
py: '12',
fontSize: 'lg',
},
},
},
defaultVariants: {
size: 'md',
},
})
```
**Recipe Best Practices:**
- Use data attributes for custom states (`data-error`)
- Include all interactive states (_focus, _disabled, _hover)
- Support both light and dark themes
- Use semantic token references
- Provide size variants
### Molecular Component Slot Recipes
Molecular components need layout coordination between primitives:
Create: `src/styles/recipes/checkbox-input.recipe.ts`
```typescript
import { defineSlotRecipe } from '@pandacss/dev'
export const checkboxInputRecipe = defineSlotRecipe({
className: 'checkboxInput',
description: 'Checkbox with label composition',
slots: ['container', 'labelText', 'descriptionText'],
base: {
container: {
display: 'flex',
alignItems: 'flex-start',
gap: '12',
cursor: 'pointer',
_hover: {
'& input': {
borderColor: { base: 'blue.40', _dark: 'blue.50' },
},
},
},
labelText: {
fontSize: 'md',
fontWeight: 'medium',
color: { base: 'gray.90', _dark: 'gray.10' },
lineHeight: '1.5',
userSelect: 'none',
},
descriptionText: {
fontSize: 'sm',
color: { base: 'gray.60', _dark: 'gray.50' },
mt: '4',
},
},
variants: {
size: {
sm: {
container: { gap: '8' },
labelText: { fontSize: 'sm' },
descriptionText: { fontSize: 'xs' },
},
md: {
container: { gap: '12' },
labelText: { fontSize: 'md' },
descriptionText: { fontSize: 'sm' },
},
lg: {
container: { gap: '16' },
labelText: { fontSize: 'lg' },
descriptionText: { fontSize: 'md' },
},
},
},
defaultVariants: {
size: 'md',
},
})
```
**Slot Recipe Pattern:**
- Define all component parts as slots
- Coordinate sizing across slots with variants
- Include hover states that affect children
- Use userSelect: 'none' on labels for better UX
### Organism Component Slot Recipes
FormField needs comprehensive slot management:
Create: `src/styles/recipes/form-field.recipe.ts`
```typescript
import { defineSlotRecipe } from '@pandacss/dev'
export const formFieldRecipe = defineSlotRecipe({
className: 'formField',
description: 'Form field wrapper with label, help text, and error text',
slots: ['container', 'labelSlot', 'helpTextSlot', 'errorTextSlot'],
base: {
container: {
display: 'flex',
flexDirection: 'column',
gap: '8',
width: 'full',
},
labelSlot: {
fontSize: 'sm',
fontWeight: 'semibold',
color: { base: 'gray.90', _dark: 'gray.10' },
mb: '4',
},
helpTextSlot: {
fontSize: 'sm',
color: { base: 'gray.60', _dark: 'gray.50' },
mt: '4',
},
errorTextSlot: {
fontSize: 'sm',
fontWeight: 'medium',
color: { base: 'red.60', _dark: 'red.40' },
mt: '4',
// Icon support
display: 'flex',
alignItems: 'center',
gap: '6',
},
},
})
```
## Best Practices Checklist
When building form architecture, create TodoWrite items for:
- [ ] Create atomic components (TextInput, CheckBox, Radio, etc.)
- [ ] Create recipes for atomic components with all variants
- [ ] Implement molecular compositions (CheckboxInput, RadioInput, etc.)
- [ ] Create slot recipes for molecular components
- [ ] Build FormField organism with accessibility features
- [ ] Create FormField slot recipe
- [ ] Test all components with keyboard navigation
- [ ] Test all components with screen reader
- [ ] Verify ARIA attributes are properly applied
- [ ] Test error states and error announcements
- [ ] Verify required field indicators work
- [ ] Test form submission and validation flow
- [ ] Verify light and dark theme support
- [ ] Test responsive behavior on mobile devices
- [ ] Create Storybook stories for all form components
- [ ] Document composition patterns and usage examples
## Common Pitfalls
### Avoid: Skipping the FormField Wrapper
```typescript
// BAD: Manual label/error handling (inconsistent, inaccessible)
<div>
<label htmlFor="email">Email</label>
<TextInput id="email" />
{error && <span style={{ color: 'red' }}>{error}</span>}
</div>
// GOOD: Use FormField for consistency
<FormField label="Email" errorText={error}>
<TextInput />
</FormField>
```
**Why**: FormField ensures consistent accessibility, ARIA attributes, and visual design.
### Avoid: Not Linking Help Text and Errors
```typescript
// BAD: Screen readers can't connect help text to input
<Label>First name</Label>
<Text>Enter your legal name</Text>
<TextInput />
// GOOD: FormField automatically links via aria-describedby
<FormField label="First name" helpText="Enter your legal name">
<TextInput />
</FormField>
```
**Why**: Proper ARIA linking helps screen reader users understand context.
### Avoid: Over-composing Too Early
```typescript
// BAD: Creating rigid mega-components
<MagicFormInput
label="Email"
type="email"
helpText="..."
errorText="..."
icon="email"
suffix="@company.com"
tooltip="..."
/>
// GOOD: Compose flexibly from primitives
<FormField label="Email" helpText="..." errorText="...">
<Box display="flex" alignItems="center">
<Icon name="email" />
<TextInput type="email" />
<Text>@company.com</Text>
</Box>
</FormField>
```
**Why**: Flexible composition beats rigid mega-components. Keep primitives simple, compose as needed.
### Avoid: Inconsistent Error Handling
```typescript
// BAD: Different error patterns across forms
<TextInput className={error ? 'error' : ''} />
<CheckBox style={{ borderColor: error ? 'red' : 'gray' }} />
// GOOD: Consistent error prop
<TextInput error={!!errors.email} />
<CheckBox error={!!errors.terms} />
```
**Why**: Consistent error APIs make forms predictable and maintainable.
## Form Validation Integration
FormField works seamlessly with form libraries:
### React Hook Form Example
```typescript
import { useForm } from 'react-hook-form'
export const ProfileForm = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm()
return (
<form onSubmit={handleSubmit(onSubmit)}>
<FormField
label="Email"
required
errorText={errors.email?.message}
>
<TextInput
{...register('email', {
required: 'Email is required',
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Invalid email address',
},
})}
error={!!errors.email}
/>
</FormField>
</form>
)
}
```
### Formik Example
```typescript
import { Formik, Form } from 'formik'
export const ProfileForm = () => {
return (
<Formik
initialValues={{ email: '' }}
onSubmit={handleSubmit}
>
{({ errors, touched, values, handleChange }) => (
<Form>
<FormField
label="Email"
required
errorText={touched.email && errors.email}
>
<TextInput
name="email"
value={values.email}
onChange={handleChange}
error={!!(touched.email && errors.email)}
/>
</FormField>
</Form>
)}
</Formik>
)
}
```
## Progressive Enhancement
Start simple, add complexity as needed:
**Phase 1: Atomic components only**
```typescript
<Box as="form">
<Label htmlFor="email">Email</Label>
<TextInput id="email" name="email" />
<Button type="submit">Submit</Button>
</Box>
```
**Phase 2: Add molecular compositions**
```typescript
<Box as="form">
<CheckboxInput name="terms" label="I agree" />
<Button type="submit">Submit</Button>
</Box>
```
**Phase 3: Add organism wrapper**
```typescript
<Box as="form">
<FormField label="Email" helpText="...">
<TextInput name="email" />
</FormField>
<Button type="submit">Submit</Button>
</Box>
```
**Phase 4: Full validation and error handling**
```typescript
<Box as="form" onSubmit={handleSubmit}>
<FormField
label="Email"
required
helpText="We'll never share your email"
errorText={errors.email}
>
<TextInput
name="email"
error={!!errors.email}
/>
</FormField>
<Button type="submit" loading={isSubmitting}>
Submit
</Button>
</Box>
```
## Testing Form Components
### Accessibility Testing
```typescript
import { render, screen } from '@testing-library/react'
import { axe, toHaveNoViolations } from 'jest-axe'
import { FormField } from './FormField'
import { TextInput } from '../TextInput/TextInput'
expect.extend(toHaveNoViolations)
test('FormField has no accessibility violations', async () => {
const { container } = render(
<FormField label="Email" helpText="Enter your email">
<TextInput />
</FormField>
)
const results = await axe(container)
expect(results).toHaveNoViolations()
})
test('FormField properly links label and input', () => {
render(
<FormField label="Email">
<TextInput />
</FormField>
)
const input = screen.getByLabelText('Email')
expect(input).toBeInTheDocument()
})
test('FormField announces errors to screen readers', () => {
render(
<FormField label="Email" errorText="Email is required">
<TextInput />
</FormField>
)
const error = screen.getByRole('alert')
expect(error).toHaveTextContent('Email is required')
const input = screen.getByLabelText('Email')
expect(input).toHaveAttribute('aria-invalid', 'true')
})
```
## Summary
The form architecture follows a clear hierarchy:
**Atomic → Molecular → Organism**
1. **Atomic**: Individual styled form controls (TextInput, CheckBox, Button)
2. **Molecular**: Simple compositions (CheckboxInput = CheckBox + Label)
3. **Organism**: Complex wrappers (FormField = Label + HelpText + Input + ErrorText + ARIA)
**Key Principles:**
- Compose upward, never downward
- Provide both atomic and molecular variants
- Use FormField for consistent accessibility
- Keep primitives simple and flexible
- Test accessibility thoroughly
- Integrate with form libraries as needed
This architecture ensures your forms are accessible, maintainable, and consistent across your application.

View File

@@ -0,0 +1,875 @@
---
name: panda-recipe-patterns
description: Create and organize recipes (regular + slot recipes), compound variants, and understand when to use recipes vs patterns vs inline CSS
---
# Panda CSS Recipe Patterns
## When to Use This Skill
Use this skill when:
- Creating reusable component styles with variants
- Building multi-part component styling (checkboxes, tooltips, menus)
- Defining compound variants (multiple conditions)
- Organizing component style libraries
- Deciding between recipes, patterns, and inline CSS
For implementing these recipes in React components, use the **panda-component-impl** skill.
## When to Use What
### Use Recipes When:
- Component has multiple style variants (e.g., button: primary, secondary, outline)
- Component has size variants (small, medium, large)
- Styles are reused across multiple instances
- Need compound variants (combining multiple variant conditions)
- Want to auto-apply styles to JSX components
- Building a design system with consistent component APIs
### Use Patterns When:
- Need computed/transformed styles (e.g., icon sizing that sets width=height)
- Creating reusable layout primitives (stack, grid, container)
- Want a props-based API for common styling tasks
- Need to enforce constraints (e.g., size must be a valid token)
### Use Inline CSS When:
- One-off styles specific to a single usage
- Dynamic values from props or state
- Component-specific overrides of recipe defaults
- Rapid prototyping before extracting to recipe
**Example Decision Tree**:
```
Button component with variants? → Recipe
Icon with size prop? → Pattern
Unique spacing on one div? → Inline CSS
Reusable card layout? → Recipe
```
## Regular Recipes (Single-Part Components)
### Basic Recipe Structure
Create: `src/recipes/button.ts`
```typescript
import { defineRecipe } from '@pandacss/dev';
const buttonBase = {
position: 'relative',
appearance: 'none',
minWidth: '0',
transitionDuration: 'fast',
transitionProperty: 'background, border-color, color, box-shadow',
transitionTimingFunction: 'default',
userSelect: 'none',
verticalAlign: 'middle',
display: 'flex',
alignItems: 'center',
gap: '4',
fontFamily: 'body',
fontSize: '16',
fontWeight: 'medium',
lineHeight: 'default',
borderWidth: '1',
borderStyle: 'solid',
borderColor: 'transparent',
borderRadius: '4',
outlineWidth: '2',
outlineStyle: 'solid',
outlineColor: 'transparent',
outlineOffset: '1',
textDecoration: 'none',
whiteSpace: 'nowrap',
cursor: 'pointer',
_disabled: {
opacity: 0.4,
cursor: 'not-allowed',
},
_focusVisible: {
outlineColor: { base: 'slate.80', _dark: 'slate.5' },
},
'& svg': {
fill: 'current',
},
};
const buttonVariants = {
variant: {
primary: {
bg: { base: 'slate.90', _dark: 'slate.5' },
color: { base: 'slate.0', _dark: 'slate.90' },
_hover: {
bg: { base: 'slate.70', _dark: 'slate.10' },
},
_active: {
bg: { base: 'slate.100', _dark: 'slate.20' },
},
_disabled: {
_hover: {
bg: { base: 'slate.90', _dark: 'slate.5' },
},
},
_selected: {
bg: { base: 'slate.5', _dark: 'slate.90' },
color: { base: 'slate.90', _dark: 'slate.0' },
},
},
standard: {
bg: { base: 'slate.5', _dark: 'slate.70' },
color: { base: 'slate.90', _dark: 'slate.0' },
_hover: {
bg: { base: 'slate.10', _dark: 'slate.60' },
},
_active: {
bg: { base: 'slate.20', _dark: 'slate.80' },
},
_disabled: {
_hover: {
bg: { base: 'slate.5', _dark: 'slate.70' },
},
},
_selected: {
bg: { base: 'slate.90', _dark: 'slate.5' },
color: { base: 'slate.0', _dark: 'slate.90' },
},
},
hollow: {
bg: 'transparent',
borderColor: { base: 'slate.30', _dark: 'slate.60' },
color: { base: 'slate.90', _dark: 'slate.0' },
_hover: {
bg: { base: 'slate.10', _dark: 'slate.60' },
borderColor: { base: 'slate.10', _dark: 'slate.60' },
},
_active: {
bg: { base: 'slate.20', _dark: 'slate.80' },
borderColor: { base: 'slate.20', _dark: 'slate.80' },
},
_disabled: {
_hover: {
bg: 'transparent',
},
},
_selected: {
bg: { base: 'slate.90', _dark: 'slate.5' },
color: { base: 'slate.0', _dark: 'slate.90' },
borderColor: 'transparent',
},
},
ghost: {
bg: 'transparent',
color: { base: 'slate.90', _dark: 'slate.0' },
_hover: {
bg: { base: 'slate.10', _dark: 'slate.60' },
},
_active: {
bg: { base: 'slate.20', _dark: 'slate.70' },
},
_disabled: {
_hover: {
bg: 'transparent',
},
},
_selected: {
bg: { base: 'slate.90', _dark: 'slate.5' },
color: { base: 'slate.0', _dark: 'slate.90' },
},
},
cta: {
bg: { base: 'gold.20', _dark: 'gold.30' },
color: 'slate.90',
_hover: {
bg: { base: 'gold.10', _dark: 'gold.20' },
},
_active: {
bg: { base: 'gold.30', _dark: 'gold.40' },
},
_disabled: {
_hover: {
bg: { base: 'gold.20', _dark: 'gold.30' },
},
},
},
danger: {
bg: 'red.50',
color: 'slate.0',
_hover: {
bg: 'red.40',
},
_active: {
bg: 'red.60',
},
_disabled: {
_hover: {
bg: 'red.50',
},
},
},
},
};
export const buttonRecipe = defineRecipe({
className: 'button',
jsx: ['Button'],
base: buttonBase,
variants: {
...buttonVariants,
size: {
medium: {
fontSize: '16',
py: '3',
px: '10',
},
large: {
fontSize: '16',
py: '7',
px: '12',
},
small: {
fontSize: '14',
py: '0',
px: '8',
'& svg': {
mt: '-1',
mb: '-1',
},
},
},
},
defaultVariants: {
variant: 'standard',
size: 'medium',
},
});
export const iconButtonRecipe = defineRecipe({
className: 'icon-button',
jsx: ['IconButton'],
base: buttonBase,
variants: {
...buttonVariants,
size: {
medium: {
fontSize: '16',
p: '3',
},
large: {
fontSize: '16',
p: '7',
},
small: {
fontSize: '14',
p: '0',
'& svg': {
mt: '-1',
mb: '-1',
},
},
},
},
defaultVariants: {
variant: 'standard',
size: 'medium',
},
});
```
### Extract Base Styles for DRY Code
**Pattern**: Share base styles between related recipes:
```typescript
// Shared base for button and iconButton
const buttonBase = {
appearance: 'none',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
transitionDuration: 'fast',
_disabled: { opacity: 0.4, cursor: 'not-allowed' },
_focusVisible: { outlineWidth: '2', outlineColor: 'blue.50' }
}
// Shared variant styles
const buttonVariants = {
variant: {
primary: { /* ... */ },
secondary: { /* ... */ }
}
}
// Button recipe
export const buttonRecipe = defineRecipe({
className: 'button',
jsx: ['Button'],
base: buttonBase,
variants: {
...buttonVariants,
size: { /* button sizes */ }
},
defaultVariants: { variant: 'primary', size: 'medium' }
})
// IconButton recipe (reuses base and variants)
export const iconButtonRecipe = defineRecipe({
className: 'iconButton',
jsx: ['IconButton'],
base: buttonBase,
variants: {
...buttonVariants,
size: {
small: { width: '32', height: '32' },
medium: { width: '40', height: '40' },
large: { width: '48', height: '48' }
}
},
defaultVariants: { variant: 'primary', size: 'medium' }
})
```
**Why**: Keeps related components visually consistent, reduces duplication.
### Dynamic Variants from Tokens
**Pattern**: Generate variants from design tokens:
```typescript
import { tokens } from '../styles/tokens'
// Get fontSize tokens
const fontSizeTokens = tokens.fontSizes
type FontSizeKey = keyof typeof fontSizeTokens
// Generate fontSize variants dynamically
const fontSizes = (Object.keys(fontSizeTokens) as FontSizeKey[]).reduce(
(accumulator, currentKey) => {
accumulator[currentKey] = { fontSize: currentKey }
return accumulator
},
{} as Record<FontSizeKey, Record<'fontSize', string>>
)
export const textRecipe = defineRecipe({
className: 'text',
jsx: ['Text'],
variants: {
size: fontSizes // All token sizes available as variants
}
})
```
**Why**: Automatically sync recipe variants with token changes.
## Slot Recipes (Multi-Part Components)
Use slot recipes for components with multiple styled parts (checkbox + label, tooltip + arrow, menu + items).
### Basic Slot Recipe Structure
Create: `src/recipes/checkbox.ts`
```typescript
import { defineSlotRecipe } from '@pandacss/dev'
export const checkBoxRecipe = defineSlotRecipe({
className: 'checkbox',
description: 'Checkbox component with label',
jsx: ['CheckBox'],
// Define named slots for component parts
slots: ['container', 'input', 'indicator', 'label'],
// Base styles for each slot
base: {
container: {
display: 'inline-flex',
alignItems: 'center',
gap: '8',
cursor: 'pointer',
position: 'relative'
},
input: {
// Visually hidden but accessible
position: 'absolute',
opacity: 0,
width: '1px',
height: '1px',
// Show different indicator icons based on state
_checked: {
"& ~ [data-part='indicator'][data-state='unchecked']": {
display: 'none'
},
"& ~ [data-part='indicator'][data-state='checked']": {
display: 'inline-flex'
}
},
_indeterminate: {
"& ~ [data-part='indicator'][data-state='indeterminate']": {
display: 'inline-flex'
}
},
_disabled: {
"& ~ [data-part='indicator']": {
opacity: 0.4,
cursor: 'not-allowed'
}
}
},
indicator: {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0,
width: '20',
height: '20',
borderWidth: '1',
borderRadius: '4',
borderColor: { base: 'slate.40', _dark: 'slate.60' },
bg: { base: 'white', _dark: 'slate.90' },
color: { base: 'blue.50', _dark: 'blue.40' }
},
label: {
fontSize: 'md',
color: { base: 'slate.90', _dark: 'slate.10' },
userSelect: 'none'
}
},
// Variants apply to specific slots
variants: {
size: {
small: {
indicator: { width: '16', height: '16' },
label: { fontSize: 'sm' }
},
medium: {
indicator: { width: '20', height: '20' },
label: { fontSize: 'md' }
},
large: {
indicator: { width: '24', height: '24' },
label: { fontSize: 'lg' }
}
}
},
defaultVariants: {
size: 'medium'
}
})
```
### Complex State Handling
**Pattern**: Use sibling selectors for state-based styling:
```typescript
base: {
input: {
// When checked, show checked icon, hide unchecked icon
_checked: {
"& ~ [data-part='indicator']": {
bg: { base: 'blue.50', _dark: 'blue.40' },
borderColor: { base: 'blue.50', _dark: 'blue.40' },
color: 'white'
}
},
// When focused, add focus ring to indicator
_focusVisible: {
"& ~ [data-part='indicator']": {
outlineWidth: '2',
outlineOffset: '1',
outlineColor: { base: 'blue.50', _dark: 'blue.40' }
}
},
// Error state
_invalid: {
"& ~ [data-part='indicator']": {
borderColor: { base: 'red.50', _dark: 'red.40' }
},
"& ~ [data-part='label']": {
color: { base: 'red.50', _dark: 'red.40' }
}
}
}
}
```
### Advanced Slot Recipe: Tooltip
Create: `src/recipes/tooltip.ts`
```typescript
import { defineSlotRecipe } from '@pandacss/dev'
export const tooltipRecipe = defineSlotRecipe({
className: 'tooltip',
jsx: ['Tooltip'],
slots: ['trigger', 'content', 'arrow'],
base: {
trigger: {
cursor: 'pointer'
},
content: {
position: 'absolute',
zIndex: 50,
px: '12',
py: '8',
fontSize: 'sm',
borderRadius: '6',
bg: { base: 'slate.90', _dark: 'slate.10' },
color: { base: 'white', _dark: 'slate.90' },
boxShadow: 'lg',
maxWidth: '320',
// Fade in/out animation
opacity: 0,
transitionProperty: 'opacity',
transitionDuration: 'fast',
_open: {
opacity: 1
}
},
arrow: {
position: 'absolute',
width: '8',
height: '8',
bg: { base: 'slate.90', _dark: 'slate.10' },
transform: 'rotate(45deg)'
}
},
variants: {
position: {
top: {
content: { bottom: 'full', left: '50%', transform: 'translateX(-50%)' },
arrow: { bottom: '-4', left: '50%', transform: 'translateX(-50%) rotate(45deg)' }
},
'top-start': {
content: { bottom: 'full', left: '0' },
arrow: { bottom: '-4', left: '12' }
},
'top-end': {
content: { bottom: 'full', right: '0' },
arrow: { bottom: '-4', right: '12' }
},
bottom: {
content: { top: 'full', left: '50%', transform: 'translateX(-50%)' },
arrow: { top: '-4', left: '50%', transform: 'translateX(-50%) rotate(45deg)' }
},
left: {
content: { right: 'full', top: '50%', transform: 'translateY(-50%)' },
arrow: { right: '-4', top: '50%', transform: 'translateY(-50%) rotate(45deg)' }
},
right: {
content: { left: 'full', top: '50%', transform: 'translateY(-50%)' },
arrow: { left: '-4', top: '50%', transform: 'translateY(-50%) rotate(45deg)' }
}
},
// Option to hide arrow
caret: {
true: {},
false: {
arrow: { display: 'none' }
}
}
},
// Compound variants: combine multiple variant conditions
compoundVariants: [
{
position: ['top', 'top-start', 'top-end'],
caret: true,
css: {
content: { mb: '12' } // Add margin when arrow is shown on top
}
},
{
position: ['bottom', 'bottom-start', 'bottom-end'],
caret: true,
css: {
content: { mt: '12' }
}
}
],
defaultVariants: {
position: 'top',
caret: true
}
})
```
## Compound Variants
Use compound variants when combining multiple variant values requires unique styling.
**Pattern**: Conditional styling based on variant combinations:
```typescript
export const buttonRecipe = defineRecipe({
variants: {
variant: {
primary: { /* ... */ },
outline: { /* ... */ }
},
size: {
small: { /* ... */ },
large: { /* ... */ }
},
loading: {
true: { cursor: 'wait' }
}
},
// Special styles when specific variants combine
compoundVariants: [
{
variant: 'primary',
loading: true,
css: {
bg: { base: 'blue.40', _dark: 'blue.30' }, // Lighter when loading
_hover: { bg: { base: 'blue.40', _dark: 'blue.30' } } // Disable hover
}
},
{
variant: 'outline',
size: 'small',
css: {
borderWidth: '1', // Thinner border for small outline
fontWeight: 'medium'
}
}
]
})
```
## Recipe Organization
### File Structure
```
src/
recipes/
index.ts # Export all recipes
button.ts # Regular recipe
input.ts # Regular recipe
checkbox.ts # Slot recipe
radio.ts # Slot recipe
tooltip.ts # Slot recipe
menu.ts # Slot recipe
```
### Export Pattern
`src/recipes/index.ts`:
```typescript
// Regular recipes
export { buttonRecipe } from './button'
export { iconButtonRecipe } from './button'
export { inputRecipe } from './input'
export { textRecipe } from './text'
// Slot recipes
export { checkBoxRecipe } from './checkbox'
export { radioRecipe } from './radio'
export { tooltipRecipe } from './tooltip'
export { menuRecipe } from './menu'
```
### Register in Config
`panda.config.ts`:
```typescript
import { defineConfig } from '@pandacss/dev'
import * as allRecipes from './src/recipes'
// Separate regular and slot recipes
const {
checkBoxRecipe,
radioRecipe,
tooltipRecipe,
menuRecipe,
...regularRecipes
} = allRecipes
// Transform keys: remove 'Recipe' suffix
const recipes = Object.fromEntries(
Object.entries(regularRecipes).map(([key, value]) => [
key.replace(/Recipe$/, ''), // buttonRecipe → button
value
])
)
const slotRecipes = {
checkbox: checkBoxRecipe,
radio: radioRecipe,
tooltip: tooltipRecipe,
menu: menuRecipe
}
export default defineConfig({
theme: {
extend: {
recipes,
slotRecipes
}
}
})
```
**Why**: Clean separation, automatic recipe registration.
## Responsive Recipes
**Pattern**: Use object syntax for responsive variants:
```typescript
export const cardRecipe = defineRecipe({
base: {
p: { base: '16', md: '20', lg: '24' }, // Responsive padding
borderRadius: { base: '6', md: '8' },
fontSize: { base: 'sm', md: 'md' }
}
})
```
**Pattern**: Container queries for component-level responsive:
```typescript
export const menuRecipe = defineSlotRecipe({
base: {
container: {
// Change layout based on container size (not viewport)
width: { base: 'full', '@container(min-width: 768px)': '260' },
position: { base: 'fixed', '@container(min-width: 768px)': 'relative' }
}
}
})
```
## Best Practices Checklist
Create TodoWrite items when creating recipes:
- [ ] Extract shared base styles for related components
- [ ] Use semantic tokens (not raw colors) in recipes
- [ ] Define sensible defaultVariants
- [ ] Include common state styles (_hover, _focus, _disabled, _active)
- [ ] Add _focusVisible for accessibility
- [ ] Use slot recipes for multi-part components
- [ ] Document recipe purpose in description field
- [ ] Use compound variants for complex combinations
- [ ] Test all variant combinations
- [ ] Validate recipes work in light AND dark themes
## Common Pitfalls
### Avoid: Hard-coded Values
```typescript
// BAD: Hard-coded hex colors, px values
variants: {
primary: {
bg: '#3B82F6',
padding: '12px'
}
}
// GOOD: Use design tokens
variants: {
primary: {
bg: { base: 'blue.50', _dark: 'blue.40' },
padding: '12'
}
}
```
### Avoid: Missing Default Variants
```typescript
// BAD: No defaults, components render unstyled
variants: {
size: { small: {...}, large: {...} }
}
// GOOD: Always provide defaults
variants: {
size: { small: {...}, medium: {...}, large: {...} }
},
defaultVariants: {
size: 'medium'
}
```
### Avoid: Over-nesting in Slot Recipes
```typescript
// BAD: Deep nesting, hard to maintain
base: {
container: {
'& > div > span > button': { /* ... */ }
}
}
// GOOD: Use slots for structure
slots: ['container', 'wrapper', 'label', 'button'],
base: {
container: { /* ... */ },
button: { /* ... */ }
}
```
### Avoid: Duplicate Logic Across Recipes
```typescript
// BAD: Copy-paste same styles
const button = { _disabled: { opacity: 0.4 } }
const input = { _disabled: { opacity: 0.4 } }
// GOOD: Extract to shared constant
const disabledStyles = { opacity: 0.4, cursor: 'not-allowed' }
const button = { _disabled: disabledStyles }
const input = { _disabled: disabledStyles }
```
## Accessing Official Panda CSS Docs
For recipe-specific documentation:
1. **Resolve library ID**: `mcp__MCP_DOCKER__resolve-library-id` with `libraryName: "panda-css"`
2. **Fetch docs**: `mcp__MCP_DOCKER__get-library-docs` with `topic: "recipes"` or `topic: "slot-recipes"`
## Next Steps
After creating recipes:
1. **Implement in components**: Use **panda-component-impl** skill for React integration
2. **Test variants**: Verify all combinations work visually
3. **Document in Storybook**: Create stories showing all variants
## Reference Files from Best Practices Repo
- Regular recipe: `src/recipes/button.ts`
- Slot recipe: `src/recipes/checkbox.ts`, `src/recipes/tooltip.ts`, `src/recipes/menu.ts`
- Shared base: `src/recipes/button.ts` (buttonBase constant)
- Dynamic variants: `src/recipes/text.ts` (fontSize generation)
- Config registration: `panda.config.ts`, `cetec-preset.ts`

View File

@@ -0,0 +1,533 @@
---
name: panda-review-component
description: Systematically assess an existing component against Panda CSS best practices, create prioritized recommendations, and implement approved changes
---
# Panda CSS Component Review
## When to Use This Skill
Use this skill when:
- Reviewing an existing component for Panda CSS best practices compliance
- Auditing components before production deployment
- Refactoring components to use Panda CSS patterns
- Onboarding legacy components to a Panda CSS design system
- Identifying technical debt in component styling
- Learning from existing implementations to improve other components
**Important**: This skill creates recommendations and presents them for approval BEFORE making any changes.
## Review Process Overview
The review follows a systematic four-phase approach:
1. **Discovery Phase**: Gather component code and context
2. **Assessment Phase**: Evaluate against best practices using comprehensive checklist
3. **Recommendation Phase**: Generate prioritized improvement list with explanations
4. **Implementation Phase**: Execute approved changes (only after user confirmation)
## Phase 1: Discovery - Gather Component Context
### Step 1: Identify Component Files
Create TodoWrite items for discovery:
- [ ] Locate component implementation file(s)
- [ ] Find associated recipe/pattern definitions
- [ ] Check for TypeScript type definitions
- [ ] Identify any test files or stories
- [ ] Review import statements and dependencies
Ask the user:
- Component name or file path
- Is this a standalone component or part of a component library?
- Are there related components that should be reviewed together?
### Step 2: Read Component Code
Use Read tool to examine:
- Component implementation (`.tsx`, `.jsx`)
- Recipe definitions (if separate file)
- Exported types and interfaces
- Related utilities (splitProps, theme context, etc.)
### Step 3: Access Official Panda CSS Documentation
For accurate assessment, reference latest Panda CSS best practices:
```typescript
// 1. Resolve Panda CSS library ID
mcp__MCP_DOCKER__resolve-library-id({ libraryName: "panda-css" })
// 2. Fetch relevant documentation
mcp__MCP_DOCKER__get-library-docs({
context7CompatibleLibraryID: "/cschroeter/park-ui", // or resolved ID
topic: "recipes" // or "patterns", "typescript", "styling"
})
```
**Topics to reference**:
- `recipes` - Recipe patterns and slot recipes
- `patterns` - Built-in patterns usage
- `typescript` - Type integration
- `styling` - Style prop patterns
## Phase 2: Assessment - Comprehensive Checklist
Create TodoWrite items for each assessment category:
### Architecture & Structure Assessment
- [ ] **Component file organization**: Is the component in its own directory with proper exports?
- [ ] **Import structure**: Are imports from correct Panda CSS packages?
- [ ] **Base component usage**: Does it use Box as foundation for polymorphic behavior?
- [ ] **Recipe/pattern usage**: Are recipes/patterns properly imported and applied?
- [ ] **File separation**: Are recipes defined in proper location (separate file in `src/recipes/` or inline)?
### TypeScript Integration Assessment
- [ ] **Recipe types**: Are generated variant types imported and used (e.g., `ButtonVariantProps`)?
- [ ] **Prop type composition**: Are types properly composed (BoxProps + VariantProps)?
- [ ] **Prop conflicts**: Are conflicting props properly omitted using `Omit`?
- [ ] **Conditional types**: Are `ConditionalValue` and token types used for responsive/theme-aware props?
- [ ] **Type exports**: Are prop types exported for consuming code?
### Styling Pattern Assessment
- [ ] **Recipe application**: Is the recipe correctly applied with variant props?
- [ ] **Slot recipes**: For multi-part components, are all slots properly destructured and applied?
- [ ] **className merging**: Is `cx()` used to merge recipe + custom classes?
- [ ] **splitProps usage**: Is `splitProps` utility used to separate CSS from HTML props?
- [ ] **Style prop support**: Does component accept Panda CSS style props (bg, px, etc.)?
- [ ] **Theme awareness**: Are colors/tokens using theme-aware values with `_dark` conditions?
- [ ] **No style mixing**: Avoid mixing inline styles, external CSS, and Panda CSS?
### State & Interaction Assessment
- [ ] **Pseudo-states**: Are hover, active, focus states defined in recipe?
- [ ] **Focus visibility**: Is `_focusVisible` used for keyboard navigation?
- [ ] **Disabled state**: Is `_disabled` properly styled and handled?
- [ ] **Loading state**: If applicable, is loading state visually indicated?
- [ ] **Data attributes**: Are custom states using data attributes (e.g., `data-indeterminate`)?
- [ ] **Condition matching**: Do conditions match multiple state selectors (native + custom)?
### Accessibility Assessment
- [ ] **Semantic HTML**: Is the correct HTML element used (button, input, etc.)?
- [ ] **ARIA attributes**: Are proper ARIA attributes included (aria-label, aria-busy, etc.)?
- [ ] **Keyboard interaction**: Can the component be used with keyboard only?
- [ ] **Focus management**: Is focus properly managed in interactive components?
- [ ] **Screen reader support**: Will screen readers announce the component correctly?
- [ ] **Color contrast**: Do color combinations meet WCAG contrast requirements?
### Responsive & Adaptive Assessment
- [ ] **Responsive props**: Are responsive values using object syntax correctly?
- [ ] **Breakpoint usage**: Are breakpoints from theme tokens used consistently?
- [ ] **Container queries**: If needed, are container queries implemented properly?
- [ ] **Mobile-first**: Are base styles mobile-first with progressive enhancement?
### Token & Design System Assessment
- [ ] **Semantic tokens**: Are semantic tokens used instead of primitive values?
- [ ] **Spacing tokens**: Is spacing using token values (not arbitrary px values)?
- [ ] **Color tokens**: Are colors from token system (not hardcoded hex/rgb)?
- [ ] **Typography tokens**: Are font sizes, weights, line heights from tokens?
- [ ] **Animation tokens**: Are transitions using token durations and easings?
### Performance & Best Practices Assessment
- [ ] **Recipe optimization**: Are compound variants used efficiently?
- [ ] **Unnecessary wrapping**: Is there unnecessary Box wrapping?
- [ ] **Conditional rendering**: Is conditional rendering implemented efficiently?
- [ ] **Prop spreading**: Is prop spreading used appropriately (not spreading into recipe)?
- [ ] **Recipe size**: Is recipe definition reasonably sized (not overly complex)?
### Documentation & Developer Experience Assessment
- [ ] **Component exports**: Are component and types properly exported?
- [ ] **Prop documentation**: Are props clear and self-documenting?
- [ ] **Usage examples**: Are there examples or stories showing usage?
- [ ] **Default props**: Are sensible defaults provided?
## Phase 3: Recommendations - Generate Prioritized List
After completing the assessment, generate a structured recommendation report.
### Priority Levels
**P0 (Critical)**: Blocks functionality, security issues, major accessibility violations
- Examples: Missing keyboard support, broken TypeScript types, non-functional styles
**P1 (High)**: Significant best practice violations, maintainability issues
- Examples: Not using recipe types, missing theme awareness, no splitProps usage
**P2 (Medium)**: Optimization opportunities, minor best practice gaps
- Examples: Inefficient compound variants, missing responsive props, token misuse
**P3 (Low)**: Nice-to-haves, polish items, documentation improvements
- Examples: Component documentation, additional variants, example refinements
### Recommendation Format
For each recommendation, provide:
1. **Priority Level**: P0, P1, P2, or P3
2. **Category**: Architecture, TypeScript, Styling, Accessibility, etc.
3. **Issue Description**: What's wrong or missing
4. **Impact**: Why it matters (performance, maintainability, accessibility, etc.)
5. **Solution**: Specific implementation approach
6. **Code Example**: Show before/after if applicable
7. **Effort Estimate**: Small (< 15 min), Medium (15-45 min), Large (> 45 min)
### Example Recommendation
```markdown
**P1 - TypeScript Integration**
**Issue**: Component not using generated recipe variant types
**Impact**:
- TypeScript types can drift out of sync with recipe definition
- No autocomplete for variant values
- Manual maintenance of prop types
**Solution**: Import and use `ButtonVariantProps` from generated recipe types
**Before**:
```typescript
type ButtonProps = {
variant?: 'primary' | 'secondary' | 'outline'
size?: 'small' | 'medium' | 'large'
}
```
**After**:
```typescript
import { type ButtonVariantProps } from '@styled-system/recipes'
type ButtonProps = BoxProps & ButtonVariantProps & {
loading?: boolean
}
```
**Effort**: Small (5 minutes)
```
## Phase 4: Present Recommendations for Approval
### Create Summary Report
Before showing recommendations, create a summary:
```markdown
# Component Review: [ComponentName]
## Overview
- **File**: path/to/Component.tsx
- **Type**: [Recipe-based | Pattern-based | Inline CSS]
- **Issues Found**: [Total count]
- **Critical (P0)**: [count]
- **High (P1)**: [count]
- **Medium (P2)**: [count]
- **Low (P3)**: [count]
## Overall Assessment
[Brief 2-3 sentence assessment of component health]
## Strengths
- [What the component does well]
- [Existing best practices being followed]
## Areas for Improvement
[High-level summary of main issues]
```
### Present Recommendations
Display recommendations grouped by priority:
```markdown
## Recommendations
### Critical Priority (P0)
[List P0 items with full detail as shown above]
### High Priority (P1)
[List P1 items with full detail]
### Medium Priority (P2)
[List P2 items with full detail]
### Low Priority (P3)
[List P3 items with full detail]
```
### Request User Approval
After presenting recommendations, explicitly ask:
**"Which recommendations would you like me to implement?"**
Options:
- "All recommendations" - Implement everything
- "Only P0 and P1" - Focus on critical and high priority
- "Let me select specific ones" - User chooses from list
- "None, I just wanted the review" - Stop here
**DO NOT proceed with implementation until user responds.**
## Phase 5: Implementation - Execute Approved Changes
Only after user approval, proceed with changes.
### Create Implementation TodoWrite Items
For each approved recommendation, create a specific todo:
```typescript
TodoWrite({
todos: [
{ content: "Update Button types to use ButtonVariantProps", status: "pending", activeForm: "Updating Button types to use ButtonVariantProps" },
{ content: "Add splitProps utility to Button component", status: "pending", activeForm: "Adding splitProps utility to Button component" },
{ content: "Implement _focusVisible styles in button recipe", status: "pending", activeForm: "Implementing _focusVisible styles in button recipe" },
// ... more items
]
})
```
### Implementation Guidelines
1. **Make one change at a time**: Mark todo as in_progress, implement, mark completed
2. **Test after each change**: Verify component still works
3. **Group related changes**: When multiple changes affect same code block, batch them
4. **Preserve functionality**: Don't change behavior, only improve implementation
5. **Follow existing patterns**: Match the code style and patterns in the codebase
### Post-Implementation Verification
After implementing changes, create verification todos:
- [ ] Component renders correctly
- [ ] All variants still work
- [ ] TypeScript types are correct
- [ ] No console errors or warnings
- [ ] Accessibility features work
- [ ] Responsive behavior intact
## Using This Skill - Quick Reference
### Typical Workflow
```bash
# User requests review
"Review the Button component for best practices"
# 1. Discovery
- Read component file(s)
- Identify recipe/pattern usage
- Note dependencies and utilities
# 2. Assessment
- Create TodoWrite items for each assessment category
- Systematically check each item
- Reference Panda CSS docs via MCP as needed
- Document findings
# 3. Generate Recommendations
- Prioritize issues (P0 > P1 > P2 > P3)
- Format each recommendation with detail
- Estimate effort for each
# 4. Present for Approval
- Show summary report
- Display recommendations by priority
- Ask user which to implement
- WAIT for response
# 5. Implement (only after approval)
- Create implementation todos
- Execute changes one by one
- Verify after each change
- Mark todos complete
```
## Accessing Official Panda CSS Documentation
Throughout the review process, reference official docs for accuracy:
### Recipe Patterns
```typescript
mcp__MCP_DOCKER__get-library-docs({
context7CompatibleLibraryID: "/cschroeter/park-ui",
topic: "recipes"
})
```
### TypeScript Integration
```typescript
mcp__MCP_DOCKER__get-library-docs({
context7CompatibleLibraryID: "/cschroeter/park-ui",
topic: "typescript"
})
```
### Styling Best Practices
```typescript
mcp__MCP_DOCKER__get-library-docs({
context7CompatibleLibraryID: "/cschroeter/park-ui",
topic: "styling"
})
```
## Common Review Findings
### Most Common Issues (seen in practice)
1. **Not using generated recipe types** (P1)
- Fix: Import and use `*VariantProps` types
2. **Missing splitProps utility** (P1)
- Fix: Add splitProps to separate CSS from HTML props
3. **No _focusVisible styles** (P0 for interactive components)
- Fix: Add focus styles in recipe
4. **Hardcoded colors instead of tokens** (P2)
- Fix: Replace with semantic token references
5. **Missing ARIA attributes** (P0 for complex components)
- Fix: Add appropriate aria-* attributes
6. **Not using Box as foundation** (P1)
- Fix: Refactor to use Box with `as` prop
7. **Mixing CSS approaches** (P1)
- Fix: Remove inline styles and external CSS, use only Panda
8. **No theme awareness (_dark conditions)** (P2)
- Fix: Add _dark variants for all colors
## Best Practices for Conducting Reviews
### DO:
- Be thorough but not pedantic
- Prioritize issues that impact users (accessibility, functionality)
- Provide clear, actionable recommendations
- Show code examples for complex changes
- Reference official docs for validation
- Acknowledge what's already done well
- Estimate effort realistically
- Wait for user approval before changing code
### DON'T:
- Make changes without approval
- Recommend changes without explaining why
- Suggest subjective style preferences as "best practices"
- Overwhelm with low-priority nitpicks
- Skip the assessment checklist
- Forget to create TodoWrite items for tracking
- Implement everything as one large change
- Sacrifice readability for minor optimizations
## Example Review Session
### User Request
```
"Review my Button component at src/components/Button/Button.tsx"
```
### Your Response
```
I'll systematically review your Button component against Panda CSS best practices. Let me start by reading the component and gathering context.
[Use Read tool to examine component]
[Create discovery TodoWrite items]
[Read recipe if separate]
[Access Panda CSS docs for recipe patterns]
[Complete assessment checklist with TodoWrite items]
[Document findings in each category]
# Component Review: Button
## Overview
- **File**: src/components/Button/Button.tsx
- **Type**: Recipe-based component
- **Issues Found**: 5
- **Critical (P0)**: 1
- **High (P1)**: 2
- **Medium (P2)**: 2
- **Low (P3)**: 0
## Overall Assessment
The Button component follows basic Panda CSS patterns but has several opportunities for improvement, particularly around TypeScript integration and prop handling.
## Strengths
- Uses recipe for variant management
- Implements hover and active states
- Has clear variant names
## Areas for Improvement
- Missing focus visible styles (accessibility concern)
- Not using generated recipe types
- No splitProps utility for prop separation
## Recommendations
### Critical Priority (P0)
**P0 - Accessibility: Missing focus visible styles**
[Full recommendation detail...]
### High Priority (P1)
**P1 - TypeScript: Not using generated recipe types**
[Full recommendation detail...]
**P1 - Architecture: Missing splitProps utility**
[Full recommendation detail...]
[... more recommendations ...]
---
Which recommendations would you like me to implement?
- "All recommendations" - Implement everything
- "Only P0 and P1" - Focus on critical and high priority
- "Let me select specific ones" - Choose from list
- "None, I just wanted the review" - Stop here
```
### After User Approves
```
Great! I'll implement the P0 and P1 recommendations. Let me create a plan:
[Create implementation TodoWrite items]
Now I'll start with the critical accessibility fix...
[Implement changes one by one, marking todos complete]
```
## Integration with Other Skills
This review skill works well with:
- **panda-component-impl**: Reference for proper component patterns
- **panda-recipe-patterns**: Reference for recipe best practices
- **panda-token-architecture**: Reference for token usage
- **panda-setup-config**: Reference for configuration patterns
When recommendations involve significant refactoring, consider suggesting:
- Creating a new component following best practices
- Incremental migration approach
- Using the panda-architect agent for complex architectural changes
## Skill Usage Checklist
When using this skill, ensure you:
- [ ] Create TodoWrite items for discovery phase
- [ ] Read all relevant component files
- [ ] Reference official Panda CSS documentation via MCP
- [ ] Create TodoWrite items for assessment checklist
- [ ] Complete all assessment categories systematically
- [ ] Prioritize findings (P0, P1, P2, P3)
- [ ] Format recommendations with full detail
- [ ] Present summary report before recommendations
- [ ] Explicitly wait for user approval
- [ ] DO NOT implement anything without approval
- [ ] Create implementation TodoWrite items after approval
- [ ] Make changes incrementally
- [ ] Mark todos complete as you go
- [ ] Verify functionality after implementation

View File

@@ -0,0 +1,431 @@
---
name: panda-setup-config
description: Guide Panda CSS initial setup, configuration, preset architecture, and build integration for new projects
---
# Panda CSS Setup & Configuration
## When to Use This Skill
Use this skill when:
- Starting a new Panda CSS project from scratch
- Integrating Panda CSS into an existing React + Vite project
- Creating a reusable Panda CSS preset for multiple projects
- Configuring build tools and import aliases
- Setting up the foundation for a design system
For complex multi-project architecture or refactoring large codebases, use the **panda-architect** agent instead.
## Prerequisites Check
Before starting, verify:
- [ ] Project uses React 18+ and Vite 4+
- [ ] TypeScript is configured (recommended but not required)
- [ ] You understand the project's design token needs
## Installation Checklist
Create TodoWrite items for each step:
### 1. Install Panda CSS
```bash
npm install -D @pandacss/dev
```
### 2. Initialize Panda CSS
```bash
npx panda init
```
This creates:
- `panda.config.ts` - Main configuration file
- `styled-system/` - Generated CSS and utilities (add to .gitignore)
### 3. Configure panda.config.ts
**Critical Settings:**
```typescript
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
// STRICT MODE: Prevents hard-coded values (enforce tokens-only)
strictTokens: true,
strictPropertyValues: true,
// Source files to scan for Panda CSS usage
include: [
'./src/**/*.{js,jsx,ts,tsx}',
'./pages/**/*.{js,jsx,ts,tsx}'
],
// Files to ignore
exclude: [],
// React integration
jsxFramework: 'react',
jsxStyleProps: 'all', // Enable style props on all components
// Generated code location
outdir: 'styled-system',
// Optional: Namespace your CSS classes
prefix: 'app',
// Optional: Custom import path
importMap: '@styled-system',
// Theme configuration
theme: {
extend: {
// tokens, recipes, etc. go here
}
}
})
```
**Key Decision Points:**
- **strictTokens**: Set to `true` to enforce design system consistency
- **prefix**: Use for avoiding CSS class conflicts (especially in design systems)
- **importMap**: Customize if you want cleaner imports (e.g., `@styled-system` instead of `../styled-system`)
### 4. Add Import Alias (Vite)
Update `vite.config.ts`:
```typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@styled-system': path.resolve(__dirname, './styled-system'),
'~': path.resolve(__dirname, './src') // Optional: src alias
}
}
})
```
Update `tsconfig.json`:
```json
{
"compilerOptions": {
"paths": {
"@styled-system/*": ["./styled-system/*"],
"~/*": ["./src/*"]
}
}
}
```
### 5. Add Build Scripts
Update `package.json`:
```json
{
"scripts": {
"prepare": "panda codegen",
"dev": "panda --watch & vite",
"build": "panda codegen && vite build",
"lint": "eslint ."
}
}
```
**Pattern**: Always run `panda codegen` before builds and in watch mode during dev.
### 6. Import Global Styles
In your app entry point (e.g., `src/main.tsx` or `src/App.tsx`):
```typescript
import '@styled-system/styles.css'
```
This imports the generated CSS including:
- CSS reset
- Design tokens as CSS variables
- Utility classes
- Recipe styles
### 7. Add styled-system to .gitignore
```
# Panda CSS generated files
styled-system/
```
**Why**: Generated code should not be committed (similar to node_modules).
## Preset Architecture (For Design Systems)
If you're building a **reusable design system**, create a separate preset file:
### Create a Preset File
**File**: `panda-preset.ts`
```typescript
import { definePreset } from '@pandacss/dev'
import type { Preset } from '@pandacss/types'
const customPreset: Preset = definePreset({
theme: {
extend: {
tokens: {
// Your design tokens
},
semanticTokens: {
// Theme-aware tokens
},
recipes: {
// Component recipes
},
slotRecipes: {
// Multi-part component recipes
}
}
},
conditions: {
// Custom conditions (pseudo-classes, states, etc.)
},
patterns: {
// Custom patterns
},
utilities: {
// Custom utility functions
}
})
export default customPreset
```
### Use the Preset
In consuming projects' `panda.config.ts`:
```typescript
import { defineConfig } from '@pandacss/dev'
import customPreset from './panda-preset'
// Or: import customPreset from 'your-design-system/preset'
export default defineConfig({
presets: [
'@pandacss/preset-base', // Always include base preset
customPreset
],
// Project-specific config
include: ['./src/**/*.{ts,tsx}'],
strictTokens: true,
// Optionally override preset values
theme: {
extend: {
// Project-specific tokens
}
}
})
```
**Pattern**: Preset provides defaults, consuming projects can extend/override.
## Configuration Best Practices
### 1. Separate Concerns
```
src/
styles/
tokens.ts # Base design tokens
semanticTokens.ts # Theme-aware tokens
conditions.ts # Custom conditions
globalStyles.ts # Global CSS
recipes/
index.ts # Export all recipes
button.ts # Individual recipe files
input.ts
patterns/
index.ts # Custom patterns
```
Import in `panda.config.ts`:
```typescript
import { tokens } from './src/styles/tokens'
import { semanticTokens } from './src/styles/semanticTokens'
import { conditions } from './src/styles/conditions'
import * as recipes from './src/recipes'
export default defineConfig({
theme: {
extend: {
tokens,
semanticTokens,
recipes: {
...recipes
}
}
},
conditions
})
```
### 2. Enable Useful Features
```typescript
export default defineConfig({
// Generate JSDoc comments for better autocomplete
emitTokensOnly: false,
// Hash class names in production
hash: process.env.NODE_ENV === 'production',
// Minify output
minify: process.env.NODE_ENV === 'production',
// Optimize build
optimize: true,
// Enable container queries
containerQueries: true
})
```
### 3. Configure Output Paths for Distribution
If building a **library**:
```typescript
export default defineConfig({
outdir: 'styled-system',
// Generate separate token files
outExtension: 'js',
// Export as ES modules
emitPackage: true
})
```
In `package.json`:
```json
{
"exports": {
"./preset": "./panda-preset.js",
"./styles.css": "./styled-system/styles.css"
},
"files": [
"styled-system",
"panda-preset.js"
]
}
```
## Accessing Official Panda CSS Docs
When you need up-to-date information about Panda CSS features, configuration options, or API changes:
### Use MCP Tools (Recommended)
1. **Resolve the library ID:**
```
Use mcp__MCP_DOCKER__resolve-library-id with libraryName: "panda-css"
```
2. **Fetch documentation:**
```
Use mcp__MCP_DOCKER__get-library-docs with:
- context7CompatibleLibraryID: (from step 1)
- topic: "configuration" | "recipes" | "patterns" | etc.
```
### Topics to Search
- "configuration" - Config options, presets
- "tokens" - Design tokens, semantic tokens
- "recipes" - Component recipes, variants
- "patterns" - Built-in and custom patterns
- "conditions" - Responsive, state, pseudo-classes
- "utilities" - Utility functions and utilities
## Troubleshooting
### Issue: "Token not found" errors with strictTokens
**Solution**: Either add the token to your tokens config, or disable strictTokens (not recommended).
```typescript
// Add missing token
tokens: {
colors: {
brand: { value: '#FF0000' }
}
}
```
### Issue: Styles not updating during development
**Solution**: Ensure Panda watch mode is running:
```bash
panda --watch
```
Or update dev script:
```json
"dev": "panda --watch & vite"
```
### Issue: Import errors for @styled-system
**Solution**:
1. Run `npm run prepare` to generate files
2. Verify `tsconfig.json` has correct path mapping
3. Check Vite alias configuration
### Issue: CSS not loading
**Solution**: Ensure you imported styles in app entry:
```typescript
import '@styled-system/styles.css'
```
## Next Steps
After setup is complete:
1. **Design tokens**: Use the **panda-token-architecture** skill to organize your design system
2. **Recipes**: Use the **panda-recipe-patterns** skill to create component styles
3. **Components**: Use the **panda-component-impl** skill to build React components
For complex architectural decisions or refactoring, launch the **panda-architect** agent.
## Working Examples
Reference these files in the `examples/` directory for production-tested patterns:
**Configuration:**
- `examples/panda.config.ts` - Complete Panda config with preset integration
- `examples/preset.ts` - Full preset architecture (myPreset) with tokens, semantic tokens, conditions, patterns
**Token Architecture:**
- `examples/tokens.ts` - Base token files organized by category
- `examples/semanticTokens.ts/` - Semantic token layer referencing primitives
**Additional Patterns:**
- `examples/textStyles.ts` - Typography preset definitions
- `examples/conditions.ts` - Custom conditions and pseudo-classes
- `examples/utils/splitProps.ts` - CSS/HTML prop separation utility
- `examples/utils/ThemeContext.tsx` - Theme provider pattern

View File

@@ -0,0 +1,693 @@
---
name: panda-token-architecture
description: Design token systems, semantic tokens, theme structures, and responsive design tokens following best practices
---
# Panda CSS Token Architecture
## When to Use This Skill
Use this skill when:
- Designing a design system's token architecture
- Organizing color palettes, spacing, typography, and other design tokens
- Setting up theme switching (light/dark modes)
- Creating semantic token layers for intent-based naming
- Establishing responsive design token patterns
For implementing these tokens in recipes or components, use **panda-recipe-patterns** or **panda-component-impl** skills.
## Token Architecture Principles
### Two-Layer Token System
**Layer 1: Base Tokens** - Raw design values
- Color palettes with numeric scales
- Sizing and spacing scales
- Typography scales
- Static values that rarely change
**Layer 2: Semantic Tokens** - Context-aware aliases
- Theme-dependent (light/dark mode)
- Intent-based naming (success, error, brand)
- References to base tokens
- Changes based on context/theme
**Why**: Separation enables theme switching without redefining entire palettes.
## Base Tokens
Create: `src/styles/tokens.ts`
### Color Palettes
**Pattern**: Use numeric scales (0-100 or 1-90) for lightness/darkness:
```typescript
import { defineTokens } from '@pandacss/dev'
export const tokens = defineTokens({
colors: {
// Grayscale: 0 = lightest, 100 = darkest
slate: {
0: { value: '#FFFFFF' },
5: { value: '#F8F9FA' },
10: { value: '#F1F3F5' },
20: { value: '#E9ECEF' },
30: { value: '#DEE2E6' },
40: { value: '#CED4DA' },
50: { value: '#ADB5BD' },
60: { value: '#868E96' },
70: { value: '#495057' },
80: { value: '#343A40' },
90: { value: '#212529' },
100: { value: '#000000' }
},
// Brand colors with tints/shades
blue: {
5: { value: '#E7F5FF' },
10: { value: '#D0EBFF' },
20: { value: '#A5D8FF' },
30: { value: '#74C0FC' },
40: { value: '#4DABF7' },
50: { value: '#339AF0' }, // Base brand blue
60: { value: '#228BE6' },
70: { value: '#1C7ED6' },
80: { value: '#1971C2' },
90: { value: '#1864AB' }
},
// Semantic palette colors
green: { /* success shades */ },
red: { /* error shades */ },
yellow: { /* warning shades */ },
cyan: { /* info shades */ }
}
})
```
**Naming Convention**:
- 0-100 scale: 0 = lightest, 100 = darkest
- Or 1-90 scale: 1 = lightest, 90 = darkest
- Choose one convention and stick to it
### Spacing & Sizing
**Pattern**: Unified scale for both spacing and sizing:
```typescript
const sizes = {
// Utility sizes
0: { value: '0' },
auto: { value: 'auto' },
full: { value: '100%' },
min: { value: 'min-content' },
max: { value: 'max-content' },
fit: { value: 'fit-content' },
prose: { value: '65ch' },
// Numeric scale in rem (16px base)
1: { value: '0.0625rem' }, // 1px
2: { value: '0.125rem' }, // 2px
4: { value: '0.25rem' }, // 4px
6: { value: '0.375rem' }, // 6px
8: { value: '0.5rem' }, // 8px
12: { value: '0.75rem' }, // 12px
16: { value: '1rem' }, // 16px
20: { value: '1.25rem' }, // 20px
24: { value: '1.5rem' }, // 24px
32: { value: '2rem' }, // 32px
40: { value: '2.5rem' }, // 40px
48: { value: '3rem' }, // 48px
64: { value: '4rem' }, // 64px
80: { value: '5rem' }, // 80px
96: { value: '6rem' }, // 96px
// ... extend as needed
}
export const tokens = defineTokens({
sizes,
spacing: sizes // Reuse same scale for spacing
})
```
**Why**: Unified scale ensures consistency between width/height and padding/margin.
### Container Sizes
**Pattern**: Named breakpoint containers:
```typescript
export const tokens = defineTokens({
sizes: {
// ... numeric sizes above ...
// Named container sizes
'2xs': { value: '16rem' }, // 256px
xs: { value: '20rem' }, // 320px
sm: { value: '24rem' }, // 384px
md: { value: '28rem' }, // 448px
lg: { value: '32rem' }, // 512px
xl: { value: '36rem' }, // 576px
'2xl': { value: '42rem' }, // 672px
'3xl': { value: '48rem' }, // 768px
'4xl': { value: '56rem' }, // 896px
'5xl': { value: '64rem' }, // 1024px
'6xl': { value: '72rem' }, // 1152px
'7xl': { value: '80rem' }, // 1280px
'8xl': { value: '90rem' } // 1440px
}
})
```
### Typography
**Pattern**: Separate font families, weights, sizes, and line heights:
```typescript
export const tokens = defineTokens({
fonts: {
body: { value: 'Inter, -apple-system, sans-serif' },
heading: { value: 'Inter, -apple-system, sans-serif' },
mono: { value: 'JetBrains Mono, Consolas, monospace' }
},
fontWeights: {
thin: { value: '100' },
light: { value: '300' },
normal: { value: '400' },
medium: { value: '500' },
semibold: { value: '600' },
bold: { value: '700' },
extrabold: { value: '800' }
},
fontSizes: {
'2xs': { value: '0.625rem' }, // 10px
xs: { value: '0.75rem' }, // 12px
sm: { value: '0.875rem' }, // 14px
md: { value: '1rem' }, // 16px
lg: { value: '1.125rem' }, // 18px
xl: { value: '1.25rem' }, // 20px
'2xl': { value: '1.5rem' }, // 24px
'3xl': { value: '1.875rem' }, // 30px
'4xl': { value: '2.25rem' }, // 36px
'5xl': { value: '3rem' }, // 48px
'6xl': { value: '3.75rem' }, // 60px
'7xl': { value: '4.5rem' } // 72px
},
lineHeights: {
none: { value: '1' },
tight: { value: '1.25' },
snug: { value: '1.375' },
normal: { value: '1.5' },
relaxed: { value: '1.625' },
loose: { value: '2' }
}
})
```
### Other Design Tokens
```typescript
export const tokens = defineTokens({
radii: {
none: { value: '0' },
sm: { value: '0.125rem' }, // 2px
base: { value: '0.25rem' }, // 4px
md: { value: '0.375rem' }, // 6px
lg: { value: '0.5rem' }, // 8px
xl: { value: '0.75rem' }, // 12px
'2xl': { value: '1rem' }, // 16px
full: { value: '9999px' }
},
shadows: {
xs: { value: '0 1px 2px 0 rgb(0 0 0 / 0.05)' },
sm: { value: '0 1px 3px 0 rgb(0 0 0 / 0.1)' },
md: { value: '0 4px 6px -1px rgb(0 0 0 / 0.1)' },
lg: { value: '0 10px 15px -3px rgb(0 0 0 / 0.1)' },
xl: { value: '0 20px 25px -5px rgb(0 0 0 / 0.1)' },
inner: { value: 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)' }
},
easings: {
default: { value: 'cubic-bezier(0.4, 0, 0.2, 1)' },
linear: { value: 'linear' },
in: { value: 'cubic-bezier(0.4, 0, 1, 1)' },
out: { value: 'cubic-bezier(0, 0, 0.2, 1)' },
inOut: { value: 'cubic-bezier(0.4, 0, 0.2, 1)' }
},
durations: {
fast: { value: '150ms' },
normal: { value: '250ms' },
slow: { value: '350ms' },
slower: { value: '500ms' }
}
})
```
## Semantic Tokens
Create: `src/styles/semanticTokens.ts`
### Purpose
Semantic tokens provide:
- Theme-aware values (light/dark mode)
- Intent-based naming (success, error, warning)
- Contextual meaning (background, foreground, border)
- Easier token updates without touching components
### Pattern: Theme-Aware Colors
```typescript
import { defineSemanticTokens } from '@pandacss/dev'
export const semanticTokens = defineSemanticTokens({
colors: {
// Background colors
bg: {
canvas: {
value: { base: '{colors.slate.0}', _dark: '{colors.slate.90}' }
},
surface: {
value: { base: '{colors.slate.5}', _dark: '{colors.slate.80}' }
},
overlay: {
value: { base: '{colors.slate.10}', _dark: '{colors.slate.70}' }
}
},
// Foreground/text colors
fg: {
default: {
value: { base: '{colors.slate.90}', _dark: '{colors.slate.10}' }
},
muted: {
value: { base: '{colors.slate.60}', _dark: '{colors.slate.40}' }
},
subtle: {
value: { base: '{colors.slate.50}', _dark: '{colors.slate.50}' }
}
},
// Border colors
border: {
default: {
value: { base: '{colors.slate.20}', _dark: '{colors.slate.70}' }
},
muted: {
value: { base: '{colors.slate.10}', _dark: '{colors.slate.80}' }
}
},
// Intent-based colors
success: {
default: {
value: { base: '{colors.green.40}', _dark: '{colors.green.30}' }
},
emphasis: {
value: { base: '{colors.green.50}', _dark: '{colors.green.40}' }
},
muted: {
value: { base: '{colors.green.10}', _dark: '{colors.green.80}' }
}
},
error: {
default: {
value: { base: '{colors.red.40}', _dark: '{colors.red.30}' }
},
emphasis: {
value: { base: '{colors.red.50}', _dark: '{colors.red.40}' }
},
muted: {
value: { base: '{colors.red.10}', _dark: '{colors.red.80}' }
}
},
warning: {
default: {
value: { base: '{colors.yellow.40}', _dark: '{colors.yellow.30}' }
},
emphasis: {
value: { base: '{colors.yellow.50}', _dark: '{colors.yellow.40}' }
},
muted: {
value: { base: '{colors.yellow.10}', _dark: '{colors.yellow.80}' }
}
},
// Brand colors
brand: {
default: {
value: { base: '{colors.blue.50}', _dark: '{colors.blue.40}' }
},
emphasis: {
value: { base: '{colors.blue.60}', _dark: '{colors.blue.30}' }
},
muted: {
value: { base: '{colors.blue.10}', _dark: '{colors.blue.80}' }
}
}
}
})
```
### Token Reference Syntax
Reference base tokens using `{category.name.shade}`:
```typescript
value: { base: '{colors.blue.50}', _dark: '{colors.blue.40}' }
// ^-- Reference to base token
```
**Why**: References enable token changes to cascade through semantic layer.
### Nesting Semantic Tokens
Create hierarchies for better organization:
```typescript
export const semanticTokens = defineSemanticTokens({
colors: {
button: {
primary: {
bg: {
value: { base: '{colors.brand.default}', _dark: '{colors.brand.default}' }
},
fg: {
value: { base: '{colors.slate.0}', _dark: '{colors.slate.0}' }
},
border: {
value: { base: '{colors.brand.emphasis}', _dark: '{colors.brand.emphasis}' }
}
},
secondary: {
bg: {
value: { base: '{colors.bg.surface}', _dark: '{colors.bg.surface}' }
},
fg: {
value: { base: '{colors.fg.default}', _dark: '{colors.fg.default}' }
}
}
}
}
})
```
Usage in recipes:
```typescript
bg: 'button.primary.bg',
color: 'button.primary.fg',
borderColor: 'button.primary.border'
```
## Text Styles
**Pattern**: Define complete typography presets:
```typescript
import { defineTextStyles } from '@pandacss/dev'
export const textStyles = defineTextStyles({
// Display text
display: {
lg: {
value: {
fontSize: '5xl',
fontWeight: 'bold',
lineHeight: 'tight',
letterSpacing: '-0.02em'
}
},
md: {
value: {
fontSize: '4xl',
fontWeight: 'bold',
lineHeight: 'tight'
}
}
},
// Headings
heading: {
lg: {
value: {
fontSize: '3xl',
fontWeight: 'semibold',
lineHeight: 'tight'
}
},
md: {
value: {
fontSize: '2xl',
fontWeight: 'semibold',
lineHeight: 'snug'
}
},
sm: {
value: {
fontSize: 'xl',
fontWeight: 'semibold',
lineHeight: 'snug'
}
}
},
// Body text
body: {
lg: {
value: {
fontSize: 'lg',
fontWeight: 'normal',
lineHeight: 'normal'
}
},
md: {
value: {
fontSize: 'md',
fontWeight: 'normal',
lineHeight: 'normal'
}
},
sm: {
value: {
fontSize: 'sm',
fontWeight: 'normal',
lineHeight: 'normal'
}
}
}
})
```
Usage:
```typescript
// In recipes or components
textStyle: 'heading.lg'
textStyle: 'body.md'
```
## Responsive Token Patterns
### Breakpoint Tokens
Define in config (not as tokens):
```typescript
// In panda.config.ts
export default defineConfig({
theme: {
extend: {
breakpoints: {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px'
}
}
}
})
```
Usage with responsive object syntax:
```typescript
fontSize: { base: 'sm', md: 'md', lg: 'lg' }
px: { base: '16', md: '20', lg: '24' }
```
### Container Query Tokens
For component-based responsive design:
```typescript
// In panda.config.ts
export default defineConfig({
theme: {
extend: {
containerSizes: {
sm: '384px',
md: '448px',
lg: '512px',
xl: '576px'
}
}
}
})
```
## Integration with panda.config.ts
Import and extend theme:
```typescript
import { defineConfig } from '@pandacss/dev'
import { tokens } from './src/styles/tokens'
import { semanticTokens } from './src/styles/semanticTokens'
import { textStyles } from './src/styles/textStyles'
export default defineConfig({
// ... other config ...
theme: {
extend: {
tokens,
semanticTokens,
textStyles
}
}
})
```
## Best Practices Checklist
Create TodoWrite items when organizing tokens:
- [ ] Separate base tokens from semantic tokens (different files)
- [ ] Use consistent numeric scales (0-100 or 1-90, pick one)
- [ ] Create semantic tokens for all theme-dependent values
- [ ] Use intent-based naming (success, error, warning, not green, red, yellow)
- [ ] Unify spacing and sizing scales
- [ ] Define text styles for common typography patterns
- [ ] Reference base tokens in semantic tokens (don't duplicate values)
- [ ] Document token purpose and usage in comments
- [ ] Test tokens in both light and dark themes
- [ ] Validate strictTokens mode catches hard-coded values
## Common Pitfalls
### Avoid: Duplicating Values
```typescript
// BAD: Duplicates color value
semanticTokens: {
colors: {
success: {
value: { base: '#22C55E', _dark: '#4ADE80' }
}
}
}
// GOOD: References base token
semanticTokens: {
colors: {
success: {
value: { base: '{colors.green.40}', _dark: '{colors.green.30}' }
}
}
}
```
### Avoid: Too Many Token Layers
Keep it simple: Base → Semantic → Components (2-3 layers max)
```typescript
// BAD: Too many indirection levels
semantic alias component variant state
// GOOD: Clear, direct references
base semantic component
```
### Avoid: Mixing Concerns
```typescript
// BAD: Mixing spacing into colors
tokens: {
colors: {
buttonPadding: { value: '12px' } // Wrong category!
}
}
// GOOD: Correct categorization
tokens: {
spacing: {
buttonPadding: { value: '12' }
}
}
```
## Accessing Official Panda CSS Docs
For token-specific documentation:
1. **Resolve library ID**: `mcp__MCP_DOCKER__resolve-library-id` with `libraryName: "panda-css"`
2. **Fetch docs**: `mcp__MCP_DOCKER__get-library-docs` with `topic: "tokens"` or `topic: "semantic-tokens"`
## Next Steps
After organizing tokens:
1. **Create recipes**: Use **panda-recipe-patterns** skill to build component styles
2. **Implement components**: Use **panda-component-impl** skill for React components
3. **Validate design system**: Ensure all components use tokens (strictTokens catches violations)
## Working Examples
Reference these files in the `examples/` directory for production-tested token patterns:
**Base Tokens (Primitives):**
- `examples/tokens.ts`
- - Color scales (10-100) for all hues
```typescript
lime: {
"10": { value: "#EFFFD6" },
"50": { value: "#82B536" },
"100": { value: "#28311B" }
}
```
- - Unified spacing/sizing numeric scale
- - Font families, sizes, weights, line heights
- - Durations, easings, keyframes
- - Borders, radii, opacity, aspect ratios
**Semantic Tokens:**
- `examples/semanticTokens.ts`
- - Theme-aware color system
```typescript
background: {
accent: {
blue: {
bolder: {
value: { base: "{colors.blue.70}", _dark: "{colors.blue.40}" }
}
}
}
}
```
- - Shadow and depth tokens
- - Semantic utility tokens
**Preset Integration:**
- `examples/preset.ts` - Complete preset showing how to integrate primitives and semantics using `definePreset`, `defineTokens`, and `defineSemanticTokens`
- `examples/textStyles.ts` - Typography presets combining font tokens