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,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