From 1702c25f859960517a059e6ff8ad9083cd57b173 Mon Sep 17 00:00:00 2001 From: Zhongwei Li Date: Sun, 30 Nov 2025 08:56:16 +0800 Subject: [PATCH] Initial commit --- .claude-plugin/plugin.json | 14 + README.md | 3 + agents/panda-architect.md | 225 +++++ plugin.lock.json | 73 ++ skills/panda-component-impl.md | 709 +++++++++++++++ skills/panda-create-stories.md | 1323 ++++++++++++++++++++++++++++ skills/panda-form-architecture.md | 930 +++++++++++++++++++ skills/panda-recipe-patterns.md | 875 ++++++++++++++++++ skills/panda-review-component.md | 533 +++++++++++ skills/panda-setup-config.md | 431 +++++++++ skills/panda-token-architecture.md | 693 +++++++++++++++ 11 files changed, 5809 insertions(+) create mode 100644 .claude-plugin/plugin.json create mode 100644 README.md create mode 100644 agents/panda-architect.md create mode 100644 plugin.lock.json create mode 100644 skills/panda-component-impl.md create mode 100644 skills/panda-create-stories.md create mode 100644 skills/panda-form-architecture.md create mode 100644 skills/panda-recipe-patterns.md create mode 100644 skills/panda-review-component.md create mode 100644 skills/panda-setup-config.md create mode 100644 skills/panda-token-architecture.md diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..32a2e5a --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,14 @@ +{ + "name": "panda-css-workflows", + "description": "Expert Panda CSS workflows for React + Vite projects - setup, tokens, recipes, and components", + "version": "1.2.0", + "author": { + "name": "Shaun Fox" + }, + "skills": [ + "./skills/" + ], + "agents": [ + "./agents/panda-architect.md" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b898f08 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# panda-css-workflows + +Expert Panda CSS workflows for React + Vite projects - setup, tokens, recipes, and components diff --git a/agents/panda-architect.md b/agents/panda-architect.md new file mode 100644 index 0000000..c4a0629 --- /dev/null +++ b/agents/panda-architect.md @@ -0,0 +1,225 @@ +--- +name: panda-architect +description: Specialized agent for complex Panda CSS architectural work including setting up new projects, refactoring to Panda CSS, and designing component libraries +type: general-purpose +model: sonnet +tools: + - "*" +skills: + - panda-component-impl + - panda-create-stories + - panda-form-architecture + - panda-recipe-patterns + - panda-review-component + - panda-setup-config + - panda-token-architecture +--- + +You are a Panda CSS architecture expert specializing in React + Vite projects. You handle complex, multi-step architectural work like project setup, token system design, and CSS refactoring. + +## Core Expertise + +### 1. Panda CSS Configuration & Setup +- panda.config.ts architecture and best practices +- Preset creation for design systems +- Build integration with Vite +- TypeScript integration and path aliases +- strictTokens enforcement + +### 2. Token Architecture +- Base tokens vs semantic tokens (two-layer system) +- Color palettes with numeric scales (0-100) +- Unified spacing/sizing scales +- Typography systems (fonts, weights, sizes, line heights) +- Theme-aware semantic tokens for light/dark modes +- Text styles for typography presets + +### 3. Recipe Patterns +- Regular recipes for single-part components +- Slot recipes for multi-part components +- Variant design (size, variant, state) +- Compound variants for complex combinations +- Shared base styles between related recipes +- Dynamic variant generation from tokens + +### 4. Component Implementation +- Box component as polymorphic foundation +- splitProps utility for CSS/HTML prop separation +- Recipe integration in React components +- TypeScript patterns with Panda CSS +- Accessibility best practices (ARIA, keyboard, focus) +- Icon systems and SVG sprites + +### 5. Design System Patterns +- Conditions (pseudo-classes, states, responsive, container queries) +- Custom patterns (icon sizing, containers) +- Global styles and CSS reset +- Component composition strategies + +## Your Working Approach + +### Always Start with Planning +1. **Create a TodoWrite checklist** for any multi-step task +2. Break down complex work into clear, trackable steps +3. Mark progress as you complete each step +4. Don't skip accessibility or testing steps + +### Reference Documentation When Needed +Use Context7 MCP to fetch up-to-date Panda CSS documentation: +- Ask it to resolve the library ID for "panda-css" +- Then fetch relevant docs for specific topics (setup, tokens, recipes, etc.) +- Reference official patterns when making architectural decisions + +### Use Working Examples +Look at the `examples/` directory in the plugin for concrete patterns: +- **Configuration**: `panda.config.ts` - Full config with preset integration +- **Preset architecture**: `preset.ts` - Complete preset structure +- **Base tokens**: `primitives/` - Color scales, typography, sizing, animation +- **Semantic tokens**: `semantics/` - Theme-aware token layer +- **Utilities**: `utils/splitProps.ts`, `utils/ThemeContext.tsx` +- **Text styles**: `textStyles.ts` +- **Conditions**: `conditions.ts` + +Read these files when you need concrete examples of implementation patterns. + +### Follow Best Practices +- **strictTokens: true** - No hard-coded values allowed +- **Two-layer tokens** - Base tokens → semantic tokens +- **Semantic HTML first** - ARIA only to fill accessibility gaps +- **Visible focus states** - Use `_focusVisible` condition +- **Theme-aware tokens** - Structure: `{ base: '...', _dark: '...' }` +- **Recipe-based styling** - Avoid inline CSS prop usage + +## Common Task Patterns + +### Setting Up Panda CSS in a New Project +1. Verify React + Vite environment +2. Create TodoWrite checklist with setup steps +3. Install `@pandacss/dev` as dev dependency +4. Run `npx panda init --postcss` +5. Configure `panda.config.ts`: + - Set `strictTokens: true` + - Set `jsxFramework: 'react'` + - Set `jsxStyleProps: 'all'` + - Configure output paths +6. Update `vite.config.ts` with path aliases +7. Update `tsconfig.json` with path mappings +8. Add Panda build scripts to package.json +9. Create initial token structure (colors, spacing, typography) +10. Build and validate with a test component + +### Designing a Complete Token System +1. Create TodoWrite checklist for token architecture +2. Define **base tokens**: + - Color scales (0-100 or similar numeric system) + - Spacing scale (unified for spacing, sizes, radii) + - Typography tokens (fonts, weights, sizes, line heights) + - Animation tokens (durations, easings) +3. Create **semantic token layer**: + - Reference base tokens via `{colors.name.shade}` syntax + - Define theme-aware tokens: `{ base: '...', _dark: '...' }` + - Organize by purpose (backgrounds, borders, text, etc.) +4. Set up **text styles** for typography presets +5. Configure responsive breakpoints +6. Create example components/recipes to validate tokens +7. Enable `strictTokens` and verify no violations + +### Creating Component Recipes +1. Determine recipe type (regular vs slot) +2. Define base styles (shared foundations) +3. Create variants (size, variant, visual state) +4. Add compound variants for complex combinations +5. Set default variants +6. Extract shared bases if multiple related recipes +7. Test all variant combinations +8. Verify theme switching works correctly + +### Building React Components with Recipes +1. Import recipe and variant types +2. Use Box component as polymorphic base if needed +3. Apply recipe with variant props +4. Use splitProps to separate CSS from HTML props +5. Add TypeScript types (props + variant types) +6. Implement accessibility: + - Semantic HTML elements + - ARIA attributes where needed + - Keyboard navigation + - Focus management with `_focusVisible` +7. Test in light and dark themes + +### Refactoring Existing CSS to Panda +1. Create TodoWrite checklist for migration +2. Audit existing styles and extract all design values +3. Create Panda token system from extracted values +4. Convert component styles to recipes (one component at a time) +5. Update component implementations to use recipes +6. Enable `strictTokens: true` and fix violations +7. Run visual regression tests to validate parity +8. Remove old CSS-in-JS dependencies + +## Key Architecture Decisions + +**Config Structure**: +```typescript +// Separate concerns into modules +import { tokens } from './tokens' +import { semanticTokens } from './semantic-tokens' +import { recipes } from './recipes' +import { textStyles } from './text-styles' + +export default defineConfig({ + strictTokens: true, + jsxFramework: 'react', + theme: { extend: { tokens, semanticTokens, recipes, textStyles } } +}) +``` + +**Token Organization**: +```typescript +// Base tokens: numeric scales +colors: { + brand: { 0: '#fff', 10: '#...', ..., 100: '#000' } +} + +// Semantic tokens: reference base +colors: { + bg: { + primary: { base: '{colors.brand.10}', _dark: '{colors.brand.90}' } + } +} +``` + +**Recipe Structure**: +```typescript +// Extract shared bases +const buttonBase = { ... } + +export const button = defineRecipe({ + base: buttonBase, + variants: { size: {...}, variant: {...} }, + defaultVariants: { size: 'md', variant: 'solid' } +}) +``` + +**Component Pattern**: +```typescript +import { button, type ButtonVariants } from '../styled-system/recipes' +import { splitProps } from '../utils/splitProps' + +type ButtonProps = ButtonVariants & React.ComponentProps<'button'> + +export const Button = (props: ButtonProps) => { + const [variantProps, htmlProps] = splitProps(props, ['size', 'variant']) + return +``` + +### Avoid: Over-wrapping + +```typescript +// BAD: Unnecessary div wrappers + + + Content + + + +// GOOD: Minimal, semantic structure +Content +``` + +## 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 diff --git a/skills/panda-create-stories.md b/skills/panda-create-stories.md new file mode 100644 index 0000000..cbfb312 --- /dev/null +++ b/skills/panda-create-stories.md @@ -0,0 +1,1323 @@ +--- +name: panda-create-stories +description: Create Storybook stories that document and demonstrate Panda CSS components with variants, accessibility testing, and interactive controls +--- + +# Panda CSS Component Stories + +## When to Use This Skill + +Use this skill when: +- Creating Storybook documentation for Panda CSS components +- Demonstrating component variants and props +- Building a component library with visual documentation +- Testing component accessibility interactively +- Showcasing responsive behavior and theme switching +- Creating a design system playground +- Onboarding developers to component APIs + +This skill assumes you have: +- An existing component (use **panda-component-impl** skill to create one) +- Storybook installed and configured in your project +- Basic understanding of component props and variants + +## Storybook Setup Prerequisites + +### Quick Storybook Check + +Before creating stories, verify Storybook is installed: + +```bash +# Check for Storybook dependencies +ls -la .storybook/ + +# Look for Storybook scripts in package.json +cat package.json | grep storybook +``` + +### If Storybook Not Installed + +Install Storybook with modern defaults: + +```bash +npx storybook@latest init +``` + +**Recommended addons for Panda CSS projects**: +```bash +npm install --save-dev @storybook/addon-a11y @storybook/addon-themes @storybook/test +``` + +### Storybook Configuration for Panda CSS + +Ensure `.storybook/main.ts` includes your Panda CSS output: + +```typescript +import type { StorybookConfig } from '@storybook/react-vite' + +const config: StorybookConfig = { + stories: ['../src/**/*.stories.@(ts|tsx)'], + addons: [ + '@storybook/addon-essentials', + '@storybook/addon-a11y', // Accessibility testing + '@storybook/addon-themes', // Theme switching + '@storybook/addon-interactions', // Interactive testing + ], + framework: { + name: '@storybook/react-vite', + options: {}, + }, +} + +export default config +``` + +Configure theme switching in `.storybook/preview.tsx`: + +```typescript +import type { Preview } from '@storybook/react' +import { withThemeByClassName } from '@storybook/addon-themes' + +// Import Panda CSS output +import '../styled-system/styles.css' + +const preview: Preview = { + decorators: [ + withThemeByClassName({ + themes: { + light: '', // No class for light (default) + dark: 'dark', // Add 'dark' class for dark mode + }, + defaultTheme: 'light', + }), + ], + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, + }, +} + +export default preview +``` + +## Story File Structure + +### File Naming Convention + +Stories are **colocated** with components: + +``` +src/ + components/ + Button/ + Button.tsx # Component implementation + Button.stories.tsx # Stories file + index.tsx # Public exports + CheckBox/ + CheckBox.tsx + CheckBox.stories.tsx + index.tsx +``` + +**Pattern**: `ComponentName.stories.tsx` in same directory as component. + +### Basic Story Structure (CSF3 Format) + +CSF3 (Component Story Format 3) is the modern, concise format. + +Create: `src/components/Button/Button.stories.tsx` + +```typescript +import type { Meta, StoryObj } from '@storybook/react' +import { Button } from './Button' + +// Meta defines default component configuration +const meta = { + title: 'Components/Button', + component: Button, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], + argTypes: { + variant: { + control: 'select', + options: ['primary', 'secondary', 'outline', 'ghost'], + description: 'Visual style variant', + }, + size: { + control: 'radio', + options: ['small', 'medium', 'large'], + description: 'Size variant', + }, + disabled: { + control: 'boolean', + description: 'Disable the button', + }, + loading: { + control: 'boolean', + description: 'Show loading state', + }, + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +// Default story - most common usage +export const Primary: Story = { + args: { + children: 'Button', + variant: 'primary', + size: 'medium', + }, +} + +// Additional variant stories +export const Secondary: Story = { + args: { + children: 'Button', + variant: 'secondary', + size: 'medium', + }, +} + +export const Outline: Story = { + args: { + children: 'Button', + variant: 'outline', + size: 'medium', + }, +} + +export const Ghost: Story = { + args: { + children: 'Button', + variant: 'ghost', + size: 'medium', + }, +} + +// Size variants +export const Small: Story = { + args: { + children: 'Button', + variant: 'primary', + size: 'small', + }, +} + +export const Large: Story = { + args: { + children: 'Button', + variant: 'primary', + size: 'large', + }, +} + +// State stories +export const Disabled: Story = { + args: { + children: 'Button', + variant: 'primary', + disabled: true, + }, +} + +export const Loading: Story = { + args: { + children: 'Button', + variant: 'primary', + loading: true, + }, +} + +// With icons (if component supports) +export const WithLeftIcon: Story = { + args: { + children: 'Button', + variant: 'primary', + leftIcon: '←', + }, +} +``` + +## Story Creation Process with TodoWrite + +When creating stories for a component, use TodoWrite to track systematic coverage: + +### Create Story Creation Checklist + +```typescript +TodoWrite({ + todos: [ + { content: "Read component file and inspect actual props", status: "pending", activeForm: "Reading component file and inspecting actual props" }, + { content: "Create story file with meta configuration", status: "pending", activeForm: "Creating story file with meta configuration" }, + { content: "Document props with argTypes (only actual component props)", status: "pending", activeForm: "Documenting props with argTypes" }, + { content: "Add Default story showing primary usage", status: "pending", activeForm: "Adding Default story showing primary usage" }, + { content: "Add All States story showing all variants/states together", status: "pending", activeForm: "Adding All States story showing all variants/states together" }, + { content: "Add example stories with 'Ex:' prefix (interactive, patterns)", status: "pending", activeForm: "Adding example stories with 'Ex:' prefix" }, + { content: "Add accessibility stories with 'A11y:' prefix (keyboard, focus)", status: "pending", activeForm: "Adding accessibility stories with 'A11y:' prefix" }, + { content: "Test stories in Storybook UI", status: "pending", activeForm: "Testing stories in Storybook UI" }, + ] +}) +``` + +### CRITICAL: Inspect Component Props First + +**Before creating argTypes**, read the component file to understand its actual props: + +```typescript +// Read the component file +const componentCode = await Read({ file_path: 'src/components/Button/Button.tsx' }) + +// Identify the prop type definition +// Example: export type ButtonProps = BoxProps & ButtonVariantProps & { ... } + +// Only include props that are: +// 1. Explicitly defined in the component's prop type +// 2. Relevant for user control (not internal implementation details) +// 3. Actually used by the component + +// DO NOT include props from base components (like 'as' from Box) unless the component explicitly exposes them +``` + +## Meta Configuration Patterns + +### Basic Meta + +```typescript +const meta = { + title: 'Components/Button', // Sidebar organization + component: Button, // Component reference + tags: ['autodocs'], // Generate docs automatically +} satisfies Meta +``` + +### Meta with Parameters + +```typescript +const meta = { + title: 'Components/Button', + component: Button, + parameters: { + layout: 'centered', // Center in canvas + docs: { + description: { + component: 'A versatile button component with multiple variants and states.', + }, + }, + }, + tags: ['autodocs'], +} satisfies Meta +``` + +### Meta with ArgTypes (Control Panel) + +```typescript +const meta = { + title: 'Components/Button', + component: Button, + tags: ['autodocs'], + argTypes: { + variant: { + control: 'select', + options: ['primary', 'secondary', 'outline', 'ghost'], + description: 'Visual style variant', + table: { + type: { summary: 'string' }, + defaultValue: { summary: 'primary' }, + }, + }, + size: { + control: 'radio', + options: ['small', 'medium', 'large'], + description: 'Size variant', + }, + onClick: { + action: 'clicked', // Log to Actions panel + }, + disabled: { + control: 'boolean', + }, + // Hide props that shouldn't be controlled + className: { + table: { disable: true }, + }, + }, +} satisfies Meta +``` + +## Story Patterns for Panda CSS Components + +### Recommended Story Structure + +**Prefer this structure** for better Storybook organization: + +1. **Default** - Most common usage (1 story) +2. **All States** - All variants/states shown together (1 story) +3. **Ex: [Pattern Name]** - Example patterns and use cases (multiple stories) +4. **A11y: [Test Name]** - Accessibility testing stories (multiple stories) + +**Why this approach?** +- **Reduces clutter**: One "All States" story instead of 6+ individual state stories +- **Better scanning**: Prefixes group related stories together in sidebar +- **Easier comparison**: See all states side-by-side +- **Clearer purpose**: "Ex:" for examples, "A11y:" for accessibility tests + +### Pattern 1: All States Story (Preferred) + +Show all variant combinations and states in one comprehensive view: + +```typescript +export const AllVariants: Story = { + render: () => ( + + {/* Primary variants */} + + + + + + + {/* Secondary variants */} + + + + + + + {/* Outline variants */} + + + + + + + {/* Ghost variants */} + + + + + + + ), +} +``` + +### Pattern 2: Example Stories (Use "Ex:" Prefix) + +Show interactive patterns and real-world use cases. Use "Ex:" prefix in the story name for better organization: + +```typescript +// Note: Story names in sidebar will show as "Ex: Interactive Toggle" +export const ExInteractiveToggle: Story = { + name: 'Ex: Interactive Toggle', + render: () => { + const [isOn, setIsOn] = useState(false) + return ( + + ) + }, +} + +export const ExFormIntegration: Story = { + name: 'Ex: Form Integration', + render: () => ( +
{ e.preventDefault(); alert('Submitted!') }}> + +
+ ), +} + +export const ExLoadingSimulation: Story = { + name: 'Ex: Loading Simulation', + render: () => { + const [loading, setLoading] = useState(false) + const handleClick = () => { + setLoading(true) + setTimeout(() => setLoading(false), 2000) + } + return ( + + ) + }, +} +``` + +**Why "Ex:" prefix?** +- Groups all example stories together in Storybook sidebar +- Clearly indicates these are usage examples, not baseline states +- Easier to scan and find relevant patterns + +### Pattern 3: Theme Comparison + +Show light and dark themes side by side: + +```typescript +export const ThemeComparison: Story = { + render: () => ( + + {/* Light theme */} + + Light Theme + + + + + + + + {/* Dark theme */} + + Dark Theme + + + + + + + + ), +} +``` + +### Pattern 4: Responsive Behavior + +Show responsive props: + +```typescript +export const Responsive: Story = { + render: () => ( + + + + + ), + parameters: { + viewport: { + defaultViewport: 'mobile1', + }, + }, +} +``` + +## Accessibility Stories with Play Functions (Use "A11y:" Prefix) + +Play functions enable automated accessibility testing. Use "A11y:" prefix for these stories to group them together in the sidebar. + +### Basic Click Interaction + +```typescript +import { userEvent, within, expect } from '@storybook/test' + +export const A11yClickInteraction: Story = { + name: 'A11y: Click Interaction', + args: { + children: 'Click Me', + variant: 'primary', + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement) + + // Find the button + const button = canvas.getByRole('button', { name: /click me/i }) + + // Click it + await userEvent.click(button) + + // Verify it's clickable (no errors thrown) + }, +} +``` + +### Keyboard Navigation Test + +```typescript +export const A11yKeyboardNavigation: Story = { + name: 'A11y: Keyboard Navigation', + render: () => ( + + + + + + ), + play: async ({ canvasElement }) => { + const canvas = within(canvasElement) + + const firstButton = canvas.getByRole('button', { name: /first/i }) + + // Focus first button + firstButton.focus() + + // Tab to next button + await userEvent.tab() + + // Verify second button is focused + const secondButton = canvas.getByRole('button', { name: /second/i }) + expect(secondButton).toHaveFocus() + }, +} +``` + +### Form Interaction Test + +```typescript +export const A11yFormSubmission: Story = { + name: 'A11y: Form Submission', + render: () => { + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + console.log('Form submitted') + } + + return ( +
+ + + + +
+ ) + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement) + + // Type in input + const input = canvas.getByPlaceholderText('Name') + await userEvent.type(input, 'John Doe') + + // Click submit + const submitButton = canvas.getByRole('button', { name: /submit/i }) + await userEvent.click(submitButton) + }, +} +``` + +### Accessibility Validation + +```typescript +import { expect } from '@storybook/test' + +export const A11yAccessibilityCheck: Story = { + name: 'A11y: Accessibility Check', + args: { + children: 'Accessible Button', + variant: 'primary', + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement) + + const button = canvas.getByRole('button') + + // Verify button has accessible name + expect(button).toHaveAccessibleName() + + // Verify focus visible on keyboard interaction + button.focus() + expect(button).toHaveFocus() + + // Verify disabled buttons are not clickable + }, + parameters: { + a11y: { + config: { + rules: [ + { + id: 'color-contrast', + enabled: true, + }, + ], + }, + }, + }, +} +``` + +## Slot Recipe Component Stories + +Multi-part components require special story patterns. + +### CheckBox Stories Example + +Create: `src/components/CheckBox/CheckBox.stories.tsx` + +```typescript +import type { Meta, StoryObj } from '@storybook/react' +import { CheckBox } from './CheckBox' + +const meta = { + title: 'Components/CheckBox', + component: CheckBox, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], + argTypes: { + size: { + control: 'radio', + options: ['small', 'medium', 'large'], + }, + checked: { + control: 'boolean', + }, + disabled: { + control: 'boolean', + }, + indeterminate: { + control: 'boolean', + }, + error: { + control: 'boolean', + }, + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: { + label: 'Accept terms', + }, +} + +export const Checked: Story = { + args: { + label: 'Checked', + checked: true, + }, +} + +export const Indeterminate: Story = { + args: { + label: 'Indeterminate', + indeterminate: true, + }, +} + +export const Disabled: Story = { + args: { + label: 'Disabled', + disabled: true, + }, +} + +export const Error: Story = { + args: { + label: 'Has error', + error: true, + }, +} + +// Show all states together +export const AllStates: Story = { + render: () => ( + + + + + + + + + ), +} + +// Interactive checkbox group +export const CheckboxGroup: Story = { + render: () => { + const [checked, setChecked] = React.useState({ + option1: false, + option2: true, + option3: false, + }) + + return ( + + setChecked({ ...checked, option1: e.target.checked })} + /> + setChecked({ ...checked, option2: e.target.checked })} + /> + setChecked({ ...checked, option3: e.target.checked })} + /> + + ) + }, +} +``` + +## ArgTypes Configuration Reference + +### Control Types + +```typescript +argTypes: { + // Select dropdown + variant: { + control: 'select', + options: ['primary', 'secondary', 'outline'], + }, + + // Radio buttons (for fewer options) + size: { + control: 'radio', + options: ['small', 'medium', 'large'], + }, + + // Text input + label: { + control: 'text', + }, + + // Number input with range + opacity: { + control: { type: 'range', min: 0, max: 1, step: 0.1 }, + }, + + // Boolean checkbox + disabled: { + control: 'boolean', + }, + + // Color picker + backgroundColor: { + control: 'color', + }, + + // Date picker + publishDate: { + control: 'date', + }, + + // Object editor + config: { + control: 'object', + }, + + // Action logger (for event handlers) + onClick: { + action: 'clicked', + }, + + // Hide from controls + className: { + table: { disable: true }, + }, +} +``` + +### ArgType Documentation + +```typescript +argTypes: { + variant: { + control: 'select', + options: ['primary', 'secondary', 'outline', 'ghost'], + description: 'The visual style variant of the button', + table: { + type: { summary: 'string' }, + defaultValue: { summary: 'primary' }, + category: 'Appearance', + }, + }, + size: { + control: 'radio', + options: ['small', 'medium', 'large'], + description: 'Controls the size and padding of the button', + table: { + type: { summary: 'string' }, + defaultValue: { summary: 'medium' }, + category: 'Appearance', + }, + }, + disabled: { + control: 'boolean', + description: 'Disables the button and prevents interaction', + table: { + type: { summary: 'boolean' }, + defaultValue: { summary: 'false' }, + category: 'State', + }, + }, +} +``` + +## Best Practices Checklist + +Create TodoWrite items when creating stories: + +- [ ] Read component file and inspect actual props before creating argTypes +- [ ] Story file is colocated with component (same directory) +- [ ] Using CSF3 format (modern, concise) +- [ ] Default story shows most common usage +- [ ] All States story shows all variants/states together (preferred over individual state stories) +- [ ] ArgTypes document only actual component props (not inherited props like 'as' from Box) +- [ ] Example stories use "Ex:" prefix for better sidebar organization +- [ ] Accessibility stories use "A11y:" prefix for better sidebar organization +- [ ] Interactive examples use play functions +- [ ] Accessibility checked with @storybook/addon-a11y +- [ ] Theme switching works (light/dark) if component is theme-aware +- [ ] Responsive behavior demonstrated if applicable +- [ ] Stories follow consistent naming convention +- [ ] Stories don't include implementation details +- [ ] Meta title follows hierarchy (Components/Category/Name) + +## Common Story Patterns + +### Composition Story + +Show how component composes with others: + +```typescript +export const ComposedWithIcon: Story = { + render: () => ( + + ), +} +``` + +### Custom Styling Override + +Show how to override with Panda CSS props: + +```typescript +export const CustomStyling: Story = { + render: () => ( + + ), +} +``` + +### Loading State Simulation + +Demonstrate async behavior: + +```typescript +export const LoadingSimulation: Story = { + render: () => { + const [loading, setLoading] = React.useState(false) + + const handleClick = () => { + setLoading(true) + setTimeout(() => setLoading(false), 2000) + } + + return ( + + ) + }, +} +``` + +## Story Organization + +### Title Hierarchy + +Organize stories in logical groups: + +```typescript +// Button variations +title: 'Components/Button' // Basic components +title: 'Components/Inputs/TextInput' // Nested categories +title: 'Patterns/Forms/LoginForm' // Pattern examples +title: 'Layouts/Grid' // Layout components +``` + +### Parameters for Layout + +```typescript +parameters: { + layout: 'centered', // Center in canvas (default for small components) + layout: 'fullscreen', // Full viewport (for pages/layouts) + layout: 'padded', // Add padding around component +} +``` + +### Tags + +```typescript +tags: [ + 'autodocs', // Generate documentation automatically + 'dev', // Only show in dev environment + 'test', // Mark as test story +] +``` + +## Accessing Storybook Documentation + +For advanced patterns and latest best practices: + +```typescript +// Resolve Storybook library ID +mcp__MCP_DOCKER__resolve-library-id({ libraryName: "storybook" }) + +// Fetch relevant documentation +mcp__MCP_DOCKER__get-library-docs({ + context7CompatibleLibraryID: "/storybookjs/storybook", + topic: "writing-stories" // or "args", "play-function", "theming" +}) +``` + +**Topics to reference**: +- `writing-stories` - CSF3 format and story patterns +- `args` - ArgTypes and controls +- `play-function` - Interactive testing +- `theming` - Theme switching and decoration + +## Common Pitfalls + +### Avoid: Complex Logic in Stories + +```typescript +// BAD: Business logic in story +export const BadStory: Story = { + render: () => { + const data = fetchDataFromAPI() // Don't fetch real data + const processed = complexProcessing(data) // Don't do heavy computation + return + }, +} + +// GOOD: Simple, declarative stories +export const GoodStory: Story = { + args: { + children: 'Button Text', + variant: 'primary', + }, +} +``` + +### Avoid: Testing Implementation Details + +```typescript +// BAD: Testing internal state +export const BadTest: Story = { + play: async ({ canvasElement }) => { + const button = canvasElement.querySelector('.button-internal-class') + expect(button.state.isActive).toBe(true) // Don't access internal state + }, +} + +// GOOD: Testing user-visible behavior +export const GoodTest: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement) + const button = canvas.getByRole('button') + await userEvent.click(button) + expect(button).toHaveAttribute('aria-pressed', 'true') + }, +} +``` + +### Avoid: Not Using ArgTypes + +```typescript +// BAD: No controls +const meta = { + title: 'Components/Button', + component: Button, +} satisfies Meta + +// GOOD: Document with argTypes +const meta = { + title: 'Components/Button', + component: Button, + argTypes: { + variant: { + control: 'select', + options: ['primary', 'secondary'], + description: 'Visual style variant', + }, + }, +} satisfies Meta +``` + +## Integration with Panda CSS Workflows + +This skill works with: + +- **panda-component-impl**: Reference component patterns for story examples +- **panda-recipe-patterns**: Use recipe variants in story demonstrations +- **panda-review-component**: Stories help validate component accessibility + +## Complete Example: Button Stories + +This example demonstrates all the recommended patterns: + +```typescript +import type { Meta, StoryObj } from '@storybook/react' +import { useState } from 'react' +import { userEvent, within, expect } from '@storybook/test' +import { Button } from './Button' +import { Box } from '../Box/Box' + +const meta = { + title: 'Components/Button', + component: Button, + parameters: { + layout: 'centered', + docs: { + description: { + component: 'A versatile button component with multiple variants, sizes, and states. Built with Panda CSS recipes.', + }, + }, + }, + tags: ['autodocs'], + argTypes: { + // IMPORTANT: Only include props that are explicitly defined in ButtonProps + // Do NOT include inherited props like 'as' from BoxProps unless Button explicitly exposes them + variant: { + control: 'select', + options: ['primary', 'secondary', 'outline', 'ghost'], + description: 'Visual style variant', + table: { + defaultValue: { summary: 'primary' }, + }, + }, + size: { + control: 'radio', + options: ['small', 'medium', 'large'], + description: 'Size variant', + table: { + defaultValue: { summary: 'medium' }, + }, + }, + disabled: { + control: 'boolean', + description: 'Disable the button', + }, + loading: { + control: 'boolean', + description: 'Show loading state', + }, + onClick: { + action: 'clicked', + }, + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +// 1. Default - Most common usage +export const Default: Story = { + args: { + children: 'Button', + variant: 'primary', + }, +} + +// 2. All States - Show everything together (PREFERRED over individual state stories) +export const AllStates: Story = { + name: 'All States', + render: () => ( + + + + + + + + + + + + + + + + + + + + + + + ), +} + +// 3. Example Stories - Use "Ex:" prefix for patterns and use cases +export const ExInteractiveToggle: Story = { + name: 'Ex: Interactive Toggle', + render: () => { + const [isOn, setIsOn] = useState(false) + return ( + + ) + }, +} + +export const ExLoadingSimulation: Story = { + name: 'Ex: Loading Simulation', + render: () => { + const [loading, setLoading] = useState(false) + const handleClick = () => { + setLoading(true) + setTimeout(() => setLoading(false), 2000) + } + return ( + + ) + }, +} + +export const ExFormIntegration: Story = { + name: 'Ex: Form Integration', + render: () => ( +
{ e.preventDefault(); alert('Submitted!') }}> + + + + +
+ ), +} + +// 4. Accessibility Stories - Use "A11y:" prefix for accessibility tests +export const A11yAccessibilityCheck: Story = { + name: 'A11y: Accessibility Check', + args: { + children: 'Accessible Button', + variant: 'primary', + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement) + const button = canvas.getByRole('button') + + // Verify accessible name + expect(button).toHaveAccessibleName('Accessible Button') + + // Test keyboard focus + button.focus() + expect(button).toHaveFocus() + + // Test click + await userEvent.click(button) + }, + parameters: { + a11y: { + config: { + rules: [ + { id: 'color-contrast', enabled: true }, + { id: 'button-name', enabled: true }, + ], + }, + }, + }, +} + +export const A11yKeyboardNavigation: Story = { + name: 'A11y: Keyboard Navigation', + render: () => ( + + + + + + ), + play: async ({ canvasElement }) => { + const canvas = within(canvasElement) + + const firstButton = canvas.getByRole('button', { name: /first/i }) + firstButton.focus() + + await userEvent.tab() + + const secondButton = canvas.getByRole('button', { name: /second/i }) + expect(secondButton).toHaveFocus() + }, +} +``` + +**Story Organization in Storybook Sidebar:** +- Default +- All States +- Ex: Form Integration +- Ex: Interactive Toggle +- Ex: Loading Simulation +- A11y: Accessibility Check +- A11y: Keyboard Navigation + +## Skill Usage Summary + +When creating stories for a Panda CSS component: + +1. **Read component file** - Inspect actual props and types before creating stories +2. **Verify Storybook setup** - Check installation and configuration +3. **Create story file** - Colocate with component: `ComponentName.stories.tsx` +4. **Define meta** - Configure title, component, argTypes (only actual component props) +5. **Create Default story** - Show most common usage +6. **Create All States story** - Show all variants/states together (preferred over individual stories) +7. **Add example stories** - Use "Ex:" prefix for patterns and use cases +8. **Add accessibility stories** - Use "A11y:" prefix for accessibility tests with play functions +9. **Test in Storybook** - Run `npm run storybook` and verify all stories +10. **Use TodoWrite** - Track systematic coverage throughout + +**Key Improvements from User Feedback:** +- ✅ Only use appropriate props (inspect component first) +- ✅ Prefer "All States" over individual state stories +- ✅ Use "Ex:" prefix for example/pattern stories +- ✅ Use "A11y:" prefix for accessibility testing stories +- ✅ Better sidebar organization and scanning + +Stories provide living documentation and enable visual regression testing. They're essential for component libraries and design systems built with Panda CSS. diff --git a/skills/panda-form-architecture.md b/skills/panda-form-architecture.md new file mode 100644 index 0000000..55dbcc6 --- /dev/null +++ b/skills/panda-form-architecture.md @@ -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 + // Polymorphic base (any HTML element) +