# Design System Repository Setup PRD
## Project Overview

Create a design system repository called `@discourser/design-system` using **Panda CSS** and **Ark UI**. This system uses an aesthetic-agnostic architecture where design languages (like Material Design 3) can be swapped by changing a single import.

## Technical Stack

- **Panda CSS** - Zero-runtime CSS-in-JS with token-first architecture
- **Ark UI** - Headless, accessible components built on Zag.js state machines
- **TypeScript** - Strict mode, full type safety
- **tsup** - Build tool for ESM/CJS output
- **Storybook** - Component documentation and testing
- **Vitest** - Unit testing
- **@material/material-color-utilities** - M3 color palette generation
- **pnpm** - Package manager

## Architecture: Three-Layer System

```
Layer 1: Infrastructure (Unchanging)
├── Token pipeline
├── Build system (tsup, Storybook)
├── Component logic (Ark UI)
└── Type contracts

Layer 2: Design Language (Swappable)
├── Token values (colors, spacing, radii)
├── Semantic mappings
└── Motion patterns

Layer 3: Component Recipes (Derived)
├── Visual styling via Panda recipes
└── Variant definitions
```

## Repository Structure

Create this exact folder structure:

```
design-system/
├── .github/
│   └── workflows/
│       ├── ci.yml
│       └── release.yml
├── .storybook/
│   ├── main.ts
│   ├── preview.ts
│   └── manager.ts
├── scripts/
│   ├── generate-palette.ts
│   ├── sync-tokens.ts
│   └── build.ts
├── src/
│   ├── contracts/
│   │   └── design-language.contract.ts
│   ├── languages/
│   │   ├── material3.language.ts
│   │   ├── transform.ts
│   │   └── index.ts
│   ├── tokens/
│   │   ├── typography.ts
│   │   ├── shadows.ts
│   │   ├── motion.ts
│   │   └── index.ts
│   ├── recipes/
│   │   ├── button.recipe.ts
│   │   ├── card.recipe.ts
│   │   ├── icon-button.recipe.ts
│   │   ├── input.recipe.ts
│   │   ├── dialog.recipe.ts
│   │   └── index.ts
│   ├── components/
│   │   ├── Button/
│   │   │   ├── Button.tsx
│   │   │   ├── Button.stories.tsx
│   │   │   └── index.ts
│   │   ├── Card/
│   │   │   ├── Card.tsx
│   │   │   ├── Card.stories.tsx
│   │   │   └── index.ts
│   │   ├── Dialog/
│   │   │   ├── Dialog.tsx
│   │   │   ├── Dialog.stories.tsx
│   │   │   └── index.ts
│   │   └── index.ts
│   ├── utils/
│   │   └── cn.ts
│   └── index.ts
├── tokens/
│   └── figma-export.json (placeholder)
├── styled-system/           # Generated by Panda (gitignore)
├── dist/                    # Build output (gitignore)
├── panda.config.ts
├── tsconfig.json
├── tsup.config.ts
├── vitest.config.ts
├── package.json
├── .gitignore
├── .npmrc
└── README.md
```

## File Contents

### package.json

```json
{
  "name": "@discourser/design-system",
  "version": "0.1.0",
  "description": "Aesthetic-agnostic design system with Panda CSS and Ark UI",
  "type": "module",
  "main": "./dist/index.cjs",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/index.cjs",
      "types": "./dist/index.d.ts"
    },
    "./styled-system": {
      "import": "./styled-system/index.mjs",
      "require": "./styled-system/index.js"
    },
    "./styled-system/css": {
      "import": "./styled-system/css/index.mjs",
      "require": "./styled-system/css/index.js"
    },
    "./styled-system/tokens": {
      "import": "./styled-system/tokens/index.mjs",
      "require": "./styled-system/tokens/index.js"
    },
    "./styled-system/recipes": {
      "import": "./styled-system/recipes/index.mjs",
      "require": "./styled-system/recipes/index.js"
    }
  },
  "files": [
    "dist",
    "styled-system"
  ],
  "scripts": {
    "dev": "storybook dev -p 6006",
    "build": "pnpm build:panda && pnpm build:lib",
    "build:panda": "panda codegen",
    "build:lib": "tsup",
    "build:storybook": "storybook build",
    "prepare": "panda codegen",
    "lint": "eslint src --ext .ts,.tsx",
    "test": "vitest",
    "test:ui": "vitest --ui",
    "tokens:generate": "tsx scripts/generate-palette.ts",
    "tokens:sync": "tsx scripts/sync-tokens.ts",
    "tokens:full": "pnpm tokens:generate && pnpm build:panda",
    "typecheck": "tsc --noEmit"
  },
  "peerDependencies": {
    "react": ">=19.0.0",
    "react-dom": ">=19.0.0"
  },
  "dependencies": {
    "@ark-ui/react": "^4.4.0",
    "clsx": "^2.1.1"
  },
  "devDependencies": {
    "@material/material-color-utilities": "^0.3.0",
    "@pandacss/dev": "^0.52.0",
    "@storybook/addon-a11y": "^8.5.0",
    "@storybook/addon-essentials": "^8.5.0",
    "@storybook/react": "^8.5.0",
    "@storybook/react-vite": "^8.5.0",
    "@types/node": "^22.0.0",
    "@types/react": "^19.0.0",
    "@types/react-dom": "^19.0.0",
    "@vitejs/plugin-react": "^4.3.0",
    "eslint": "^9.0.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "storybook": "^8.5.0",
    "tsup": "^8.3.0",
    "tsx": "^4.19.0",
    "typescript": "^5.7.0",
    "vite": "^6.0.0",
    "vitest": "^2.1.0"
  },
  "keywords": [
    "design-system",
    "panda-css",
    "ark-ui",
    "material-design-3",
    "react",
    "components"
  ],
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/Tasty-Maker-Studio/Discourser-Design-System.git"
  }
}
```

### tsconfig.json

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "strict": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "outDir": "dist",
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "styled-system/*": ["./styled-system/*"]
    },
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true
  },
  "include": ["src/**/*", "scripts/**/*", "panda.config.ts"],
  "exclude": ["node_modules", "dist", "styled-system", "storybook-static"]
}
```

### tsup.config.ts

```typescript
import { defineConfig } from 'tsup';

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  dts: true,
  splitting: true,
  sourcemap: true,
  clean: true,
  external: ['react', 'react-dom'],
  esbuildOptions(options) {
    options.banner = {
      js: '"use client"',
    };
  },
});
```

### panda.config.ts

```typescript
import { defineConfig } from '@pandacss/dev';
import { activeLanguage, transformToPandaTheme } from './src/languages';
import { buttonRecipe, cardRecipe, iconButtonRecipe } from './src/recipes';

const theme = transformToPandaTheme(activeLanguage);

export default defineConfig({
  preflight: true,

  include: [
    './src/**/*.{js,jsx,ts,tsx}',
    './stories/**/*.{js,jsx,ts,tsx}'
  ],

  exclude: [],

  outdir: 'styled-system',

  jsxFramework: 'react',

  layers: {
    reset: 'reset',
    base: 'base',
    tokens: 'tokens',
    recipes: 'recipes',
    utilities: 'utilities'
  },

  theme: {
    tokens: theme.tokens,
    semanticTokens: theme.semanticTokens,
    textStyles: theme.textStyles,
    extend: {
      recipes: {
        button: buttonRecipe,
        card: cardRecipe,
        iconButton: iconButtonRecipe,
      }
    }
  },

  conditions: {
    light: '[data-theme=light] &, .light &',
    dark: '[data-theme=dark] &, .dark &'
  },

  globalCss: {
    html: {
      colorScheme: 'light dark'
    },
    body: {
      fontFamily: 'body',
      bg: 'surface',
      color: 'onSurface',
      textStyle: 'bodyMedium'
    }
  }
});
```

### src/contracts/design-language.contract.ts

```typescript
/**
 * Design Language Contract
 *
 * Any aesthetic (M3, Carbon, Fluent, custom) must implement this interface.
 * This enables swapping aesthetics by changing one import.
 */

export interface DesignLanguageContract {
  name: string;
  version: string;
  colors: ColorPalettes;
  semantic: SemanticColors;
  semanticDark?: SemanticColors; // Optional dark theme overrides
  typography: TypographyConfig;
  spacing: SpacingScale;
  shape: ShapeConfig;
  elevation: ElevationConfig;
  motion: MotionConfig;
}

// Color Types
export interface ColorPalettes {
  primary: TonalPalette;
  secondary: TonalPalette;
  tertiary: TonalPalette;
  neutral: TonalPalette;
  neutralVariant: TonalPalette;
  error: TonalPalette;
}

export interface TonalPalette {
  0: string;
  10: string;
  20: string;
  30: string;
  40: string;
  50: string;
  60: string;
  70: string;
  80: string;
  90: string;
  95: string;
  99: string;
  100: string;
}

export interface SemanticColors {
  primary: string;
  onPrimary: string;
  primaryContainer: string;
  onPrimaryContainer: string;
  secondary: string;
  onSecondary: string;
  secondaryContainer: string;
  onSecondaryContainer: string;
  tertiary: string;
  onTertiary: string;
  tertiaryContainer: string;
  onTertiaryContainer: string;
  error: string;
  onError: string;
  errorContainer: string;
  onErrorContainer: string;
  surface: string;
  onSurface: string;
  surfaceVariant: string;
  onSurfaceVariant: string;
  surfaceContainerLowest: string;
  surfaceContainerLow: string;
  surfaceContainer: string;
  surfaceContainerHigh: string;
  surfaceContainerHighest: string;
  outline: string;
  outlineVariant: string;
  inverseSurface: string;
  inverseOnSurface: string;
  inversePrimary: string;
  background: string;
  onBackground: string;
  scrim: string;
  shadow: string;
}

// Typography Types
export interface TypographyConfig {
  fonts: {
    display: string;
    body: string;
    mono: string;
  };
  scale: TypographyScale;
}

export interface TypographyScale {
  displayLarge: TypeStyle;
  displayMedium: TypeStyle;
  displaySmall: TypeStyle;
  headlineLarge: TypeStyle;
  headlineMedium: TypeStyle;
  headlineSmall: TypeStyle;
  titleLarge: TypeStyle;
  titleMedium: TypeStyle;
  titleSmall: TypeStyle;
  bodyLarge: TypeStyle;
  bodyMedium: TypeStyle;
  bodySmall: TypeStyle;
  labelLarge: TypeStyle;
  labelMedium: TypeStyle;
  labelSmall: TypeStyle;
}

export interface TypeStyle {
  fontSize: string;
  lineHeight: string;
  fontWeight: string;
  letterSpacing: string;
  fontFamily?: 'display' | 'body' | 'mono';
}

// Spacing Types
export interface SpacingScale {
  none: string;
  xxs: string;
  xs: string;
  sm: string;
  md: string;
  lg: string;
  xl: string;
  xxl: string;
  xxxl: string;
}

// Shape Types
export interface ShapeConfig {
  radii: RadiiScale;
  style: 'sharp' | 'rounded' | 'soft' | 'organic';
}

export interface RadiiScale {
  none: string;
  extraSmall: string;
  small: string;
  medium: string;
  large: string;
  extraLarge: string;
  full: string;
}

// Elevation Types
export interface ElevationConfig {
  levels: ElevationScale;
  style: 'shadow' | 'border' | 'blur' | 'flat';
}

export interface ElevationScale {
  level0: string;
  level1: string;
  level2: string;
  level3: string;
  level4: string;
  level5: string;
}

// Motion Types
export interface MotionConfig {
  durations: DurationScale;
  easings: EasingScale;
  style: 'expressive' | 'productive' | 'minimal';
}

export interface DurationScale {
  instant: string;
  fast: string;
  normal: string;
  slow: string;
  slower: string;
}

export interface EasingScale {
  standard: string;
  standardDecelerate: string;
  standardAccelerate: string;
  emphasized: string;
  emphasizedDecelerate: string;
  emphasizedAccelerate: string;
}
```

### src/languages/transform.ts

```typescript
import type { DesignLanguageContract, TonalPalette, SemanticColors } from '../contracts/design-language.contract';

/**
 * Transforms a DesignLanguageContract into Panda CSS theme configuration
 */
export function transformToPandaTheme(language: DesignLanguageContract) {
  return {
    tokens: transformTokens(language),
    semanticTokens: transformSemanticTokens(language),
    textStyles: transformTextStyles(language)
  };
}

function transformTokens(language: DesignLanguageContract) {
  return {
    colors: transformColorPalettes(language.colors),
    fonts: {
      display: { value: language.typography.fonts.display },
      body: { value: language.typography.fonts.body },
      mono: { value: language.typography.fonts.mono }
    },
    fontSizes: extractFontSizes(language.typography.scale),
    lineHeights: extractLineHeights(language.typography.scale),
    fontWeights: extractFontWeights(language.typography.scale),
    letterSpacings: extractLetterSpacings(language.typography.scale),
    spacing: objectToTokens(language.spacing),
    radii: objectToTokens(language.shape.radii),
    shadows: objectToTokens(language.elevation.levels),
    durations: objectToTokens(language.motion.durations),
    easings: objectToTokens(language.motion.easings)
  };
}

function transformSemanticTokens(language: DesignLanguageContract) {
  const light = language.semantic;
  const dark = language.semanticDark || light; // Fallback to light if no dark

  return {
    colors: Object.fromEntries(
      Object.entries(light).map(([key, lightValue]) => [
        key,
        {
          value: {
            base: lightValue,
            _dark: dark[key as keyof SemanticColors] || lightValue
          }
        }
      ])
    )
  };
}

function transformTextStyles(language: DesignLanguageContract) {
  const scale = language.typography.scale;

  return Object.fromEntries(
    Object.entries(scale).map(([name, style]) => [
      name,
      {
        value: {
          fontFamily: style.fontFamily || 'body',
          fontSize: style.fontSize,
          lineHeight: style.lineHeight,
          fontWeight: style.fontWeight,
          letterSpacing: style.letterSpacing
        }
      }
    ])
  );
}

function transformColorPalettes(palettes: Record<string, TonalPalette>) {
  return Object.fromEntries(
    Object.entries(palettes).map(([name, palette]) => [
      name,
      Object.fromEntries(
        Object.entries(palette).map(([tone, value]) => [
          tone,
          { value }
        ])
      )
    ])
  );
}

function objectToTokens<T extends Record<string, string>>(obj: T) {
  return Object.fromEntries(
    Object.entries(obj).map(([key, value]) => [key, { value }])
  );
}

function extractFontSizes(scale: Record<string, { fontSize: string }>) {
  return Object.fromEntries(
    Object.entries(scale).map(([name, style]) => [
      name,
      { value: style.fontSize }
    ])
  );
}

function extractLineHeights(scale: Record<string, { lineHeight: string }>) {
  return Object.fromEntries(
    Object.entries(scale).map(([name, style]) => [
      name,
      { value: style.lineHeight }
    ])
  );
}

function extractFontWeights(scale: Record<string, { fontWeight: string }>) {
  const weights = new Map<string, string>();
  Object.values(scale).forEach(style => {
    weights.set(style.fontWeight, style.fontWeight);
  });
  return Object.fromEntries(
    Array.from(weights.entries()).map(([key, value]) => [
      key,
      { value }
    ])
  );
}

function extractLetterSpacings(scale: Record<string, { letterSpacing: string }>) {
  return Object.fromEntries(
    Object.entries(scale).map(([name, style]) => [
      name,
      { value: style.letterSpacing }
    ])
  );
}
```

### src/languages/material3.language.ts

```typescript
import type { DesignLanguageContract } from '../contracts/design-language.contract';

/**
 * Material Design 3 Language Implementation
 * 
 * Source color: #63A002 (TastyMakers green)
 * Generated via Material Theme Builder plugin 2024-12-24
 */
export const material3Language: DesignLanguageContract = {
  name: 'material3',
  version: '1.0.0',
  
  colors: {
    // From Material Theme Builder export
    primary: {
      0: '#000000',
      10: '#102000',
      20: '#1F3700',
      30: '#2F4F00',
      40: '#3F6900',
      50: '#518500',
      60: '#64A104',
      70: '#7DBD2A',
      80: '#97D945',
      90: '#B2F65F',
      95: '#D2FF9B',
      99: '#F9FFE9',
      100: '#FFFFFF'
    },
    secondary: {
      0: '#000000',
      10: '#121F04',
      20: '#263515',
      30: '#3C4C2A',
      40: '#54643F',
      50: '#6C7D56',
      60: '#85976E',
      70: '#A0B187',
      80: '#BBCDA1',
      90: '#D7E9BB',
      95: '#E5F7C9',
      99: '#F9FFE9',
      100: '#FFFFFF'
    },
    tertiary: {
      0: '#000000',
      10: '#00201E',
      20: '#003735',
      30: '#00504C',
      40: '#046A66',
      50: '#30837F',
      60: '#4D9D98',
      70: '#69B8B3',
      80: '#85D4CF',
      90: '#A1F1EB',
      95: '#B0FFF9',
      99: '#F2FFFD',
      100: '#FFFFFF'
    },
    neutral: {
      0: '#000000',
      10: '#1B1C18',
      20: '#30312C',
      30: '#464742',
      40: '#5E5F59',
      50: '#777771',
      60: '#91918B',
      70: '#ABACA5',
      80: '#C7C7C0',
      90: '#E3E3DB',
      95: '#F2F1E9',
      99: '#FDFCF5',
      100: '#FFFFFF'
    },
    neutralVariant: {
      0: '#000000',
      10: '#191D14',
      20: '#2E3228',
      30: '#44483D',
      40: '#5C6054',
      50: '#75796C',
      60: '#8F9285',
      70: '#A9AD9F',
      80: '#C5C8BA',
      90: '#E1E4D5',
      95: '#EFF2E3',
      99: '#FBFEEE',
      100: '#FFFFFF'
    },
    error: {
      0: '#000000',
      10: '#410E0B',
      20: '#601410',
      30: '#8C1D18',
      40: '#B3261E',
      50: '#DC362E',
      60: '#E46962',
      70: '#EC928E',
      80: '#F2B8B5',
      90: '#F9DEDC',
      95: '#FCEEEE',
      99: '#FFFBF9',
      100: '#FFFFFF'
    }
  },
  
  semantic: {
    // Light theme from Material Theme Builder
    primary: '#4C662B',
    onPrimary: '#FFFFFF',
    primaryContainer: '#CDEDA3',
    onPrimaryContainer: '#354E16',
    
    secondary: '#586249',
    onSecondary: '#FFFFFF',
    secondaryContainer: '#DCE7C8',
    onSecondaryContainer: '#404A33',
    
    tertiary: '#386663',
    onTertiary: '#FFFFFF',
    tertiaryContainer: '#BCECE7',
    onTertiaryContainer: '#1F4E4B',
    
    error: '#BA1A1A',
    onError: '#FFFFFF',
    errorContainer: '#FFDAD6',
    onErrorContainer: '#93000A',
    
    surface: '#F9FAEF',
    onSurface: '#1A1C16',
    surfaceVariant: '#E1E4D5',
    onSurfaceVariant: '#44483D',
    
    surfaceContainerLowest: '#FFFFFF',
    surfaceContainerLow: '#F3F4E9',
    surfaceContainer: '#EEEFE3',
    surfaceContainerHigh: '#E8E9DE',
    surfaceContainerHighest: '#E2E3D8',
    
    outline: '#75796C',
    outlineVariant: '#C5C8BA',
    
    inverseSurface: '#2F312A',
    inverseOnSurface: '#F1F2E6',
    inversePrimary: '#B1D18A',
    
    background: '#F9FAEF',
    onBackground: '#1A1C16',
    
    scrim: '#000000',
    shadow: '#000000'
  },
  
  // Dark theme semantic colors (for reference/dark mode implementation)
  semanticDark: {
    primary: '#B1D18A',
    onPrimary: '#1F3701',
    primaryContainer: '#354E16',
    onPrimaryContainer: '#CDEDA3',
    
    secondary: '#BFCBAD',
    onSecondary: '#2A331E',
    secondaryContainer: '#404A33',
    onSecondaryContainer: '#DCE7C8',
    
    tertiary: '#A0D0CB',
    onTertiary: '#003735',
    tertiaryContainer: '#1F4E4B',
    onTertiaryContainer: '#BCECE7',
    
    error: '#FFB4AB',
    onError: '#690005',
    errorContainer: '#93000A',
    onErrorContainer: '#FFDAD6',
    
    surface: '#12140E',
    onSurface: '#E2E3D8',
    surfaceVariant: '#44483D',
    onSurfaceVariant: '#C5C8BA',
    
    surfaceContainerLowest: '#0C0F09',
    surfaceContainerLow: '#1A1C16',
    surfaceContainer: '#1E201A',
    surfaceContainerHigh: '#282B24',
    surfaceContainerHighest: '#33362E',
    
    outline: '#8F9285',
    outlineVariant: '#44483D',
    
    inverseSurface: '#E2E3D8',
    inverseOnSurface: '#2F312A',
    inversePrimary: '#4C662B',
    
    background: '#12140E',
    onBackground: '#E2E3D8',
    
    scrim: '#000000',
    shadow: '#000000'
  },
  
  typography: {
    fonts: {
      display: 'Georgia, "Times New Roman", serif',
      body: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
      mono: '"JetBrains Mono", "Fira Code", Consolas, monospace'
    },
    scale: {
      displayLarge: {
        fontSize: '57px',
        lineHeight: '64px',
        fontWeight: '400',
        letterSpacing: '-0.25px',
        fontFamily: 'display'
      },
      displayMedium: {
        fontSize: '45px',
        lineHeight: '52px',
        fontWeight: '400',
        letterSpacing: '0px',
        fontFamily: 'display'
      },
      displaySmall: {
        fontSize: '36px',
        lineHeight: '44px',
        fontWeight: '400',
        letterSpacing: '0px',
        fontFamily: 'display'
      },
      headlineLarge: {
        fontSize: '32px',
        lineHeight: '40px',
        fontWeight: '400',
        letterSpacing: '0px',
        fontFamily: 'display'
      },
      headlineMedium: {
        fontSize: '28px',
        lineHeight: '36px',
        fontWeight: '400',
        letterSpacing: '0px',
        fontFamily: 'display'
      },
      headlineSmall: {
        fontSize: '24px',
        lineHeight: '32px',
        fontWeight: '400',
        letterSpacing: '0px',
        fontFamily: 'display'
      },
      titleLarge: {
        fontSize: '22px',
        lineHeight: '28px',
        fontWeight: '500',
        letterSpacing: '0px',
        fontFamily: 'body'
      },
      titleMedium: {
        fontSize: '16px',
        lineHeight: '24px',
        fontWeight: '500',
        letterSpacing: '0.15px',
        fontFamily: 'body'
      },
      titleSmall: {
        fontSize: '14px',
        lineHeight: '20px',
        fontWeight: '500',
        letterSpacing: '0.1px',
        fontFamily: 'body'
      },
      bodyLarge: {
        fontSize: '16px',
        lineHeight: '24px',
        fontWeight: '400',
        letterSpacing: '0.5px',
        fontFamily: 'body'
      },
      bodyMedium: {
        fontSize: '14px',
        lineHeight: '20px',
        fontWeight: '400',
        letterSpacing: '0.25px',
        fontFamily: 'body'
      },
      bodySmall: {
        fontSize: '12px',
        lineHeight: '16px',
        fontWeight: '400',
        letterSpacing: '0.4px',
        fontFamily: 'body'
      },
      labelLarge: {
        fontSize: '14px',
        lineHeight: '20px',
        fontWeight: '500',
        letterSpacing: '0.1px',
        fontFamily: 'body'
      },
      labelMedium: {
        fontSize: '12px',
        lineHeight: '16px',
        fontWeight: '500',
        letterSpacing: '0.5px',
        fontFamily: 'body'
      },
      labelSmall: {
        fontSize: '11px',
        lineHeight: '16px',
        fontWeight: '500',
        letterSpacing: '0.5px',
        fontFamily: 'body'
      }
    }
  },
  
  spacing: {
    none: '0px',
    xxs: '2px',
    xs: '4px',
    sm: '8px',
    md: '16px',
    lg: '24px',
    xl: '32px',
    xxl: '48px',
    xxxl: '64px'
  },
  
  shape: {
    radii: {
      none: '0px',
      extraSmall: '4px',
      small: '8px',
      medium: '12px',
      large: '16px',
      extraLarge: '28px',
      full: '9999px'
    },
    style: 'rounded'
  },
  
  elevation: {
    levels: {
      level0: 'none',
      level1: '0px 1px 2px rgba(0, 0, 0, 0.3), 0px 1px 3px 1px rgba(0, 0, 0, 0.15)',
      level2: '0px 1px 2px rgba(0, 0, 0, 0.3), 0px 2px 6px 2px rgba(0, 0, 0, 0.15)',
      level3: '0px 4px 8px 3px rgba(0, 0, 0, 0.15), 0px 1px 3px rgba(0, 0, 0, 0.3)',
      level4: '0px 6px 10px 4px rgba(0, 0, 0, 0.15), 0px 2px 3px rgba(0, 0, 0, 0.3)',
      level5: '0px 8px 12px 6px rgba(0, 0, 0, 0.15), 0px 4px 4px rgba(0, 0, 0, 0.3)'
    },
    style: 'shadow'
  },
  
  motion: {
    durations: {
      instant: '0ms',
      fast: '100ms',
      normal: '200ms',
      slow: '300ms',
      slower: '500ms'
    },
    easings: {
      standard: 'cubic-bezier(0.2, 0, 0, 1)',
      standardDecelerate: 'cubic-bezier(0, 0, 0, 1)',
      standardAccelerate: 'cubic-bezier(0.3, 0, 1, 1)',
      emphasized: 'cubic-bezier(0.2, 0, 0, 1)',
      emphasizedDecelerate: 'cubic-bezier(0.05, 0.7, 0.1, 1)',
      emphasizedAccelerate: 'cubic-bezier(0.3, 0, 0.8, 0.15)'
    },
    style: 'expressive'
  }
};
```

### src/languages/index.ts

```typescript
// Export the active language
// Change this import to switch aesthetics
export { material3Language as activeLanguage } from './material3.language';

// Re-export transformer
export { transformToPandaTheme } from './transform';

// Re-export types
export type { DesignLanguageContract } from '../contracts/design-language.contract';
```

### src/recipes/button.recipe.ts

```typescript
import { defineRecipe } from '@pandacss/dev';

export const buttonRecipe = defineRecipe({
  className: 'button',
  description: 'Material Design 3 button component',
  base: {
    display: 'inline-flex',
    alignItems: 'center',
    justifyContent: 'center',
    gap: 'sm',
    fontFamily: 'body',
    fontWeight: '500',
    borderRadius: 'full',
    cursor: 'pointer',
    transition: 'all',
    transitionDuration: 'fast',
    transitionTimingFunction: 'standard',
    outline: 'none',
    border: 'none',
    textDecoration: 'none',
    _disabled: {
      opacity: 0.38,
      cursor: 'not-allowed',
      pointerEvents: 'none'
    },
    _focusVisible: {
      outline: '2px solid',
      outlineColor: 'primary',
      outlineOffset: '2px'
    }
  },
  variants: {
    variant: {
      filled: {
        bg: 'primary',
        color: 'onPrimary',
        _hover: {
          opacity: 0.92,
          shadow: 'level1'
        },
        _active: {
          opacity: 0.88
        }
      },
      outlined: {
        bg: 'transparent',
        color: 'primary',
        borderWidth: '1px',
        borderStyle: 'solid',
        borderColor: 'outline',
        _hover: {
          bg: 'primary',
          bgOpacity: 0.08
        },
        _active: {
          bg: 'primary',
          bgOpacity: 0.12
        }
      },
      text: {
        bg: 'transparent',
        color: 'primary',
        _hover: {
          bg: 'primary',
          bgOpacity: 0.08
        },
        _active: {
          bg: 'primary',
          bgOpacity: 0.12
        }
      },
      elevated: {
        bg: 'surfaceContainerLow',
        color: 'primary',
        shadow: 'level1',
        _hover: {
          shadow: 'level2',
          bg: 'surfaceContainerLow'
        },
        _active: {
          shadow: 'level1'
        }
      },
      tonal: {
        bg: 'secondaryContainer',
        color: 'onSecondaryContainer',
        _hover: {
          shadow: 'level1'
        },
        _active: {
          shadow: 'none'
        }
      }
    },
    size: {
      sm: {
        height: '32px',
        px: 'md',
        fontSize: 'labelMedium',
        lineHeight: 'labelMedium'
      },
      md: {
        height: '40px',
        px: 'lg',
        fontSize: 'labelLarge',
        lineHeight: 'labelLarge'
      },
      lg: {
        height: '48px',
        px: 'xl',
        fontSize: 'labelLarge',
        lineHeight: 'labelLarge'
      }
    }
  },
  defaultVariants: {
    variant: 'filled',
    size: 'md'
  }
});
```

### src/recipes/card.recipe.ts

```typescript
import { defineRecipe } from '@pandacss/dev';

export const cardRecipe = defineRecipe({
  className: 'card',
  description: 'Material Design 3 card component',
  base: {
    display: 'flex',
    flexDirection: 'column',
    borderRadius: 'medium',
    overflow: 'hidden',
    transition: 'all',
    transitionDuration: 'fast',
    transitionTimingFunction: 'standard'
  },
  variants: {
    variant: {
      elevated: {
        bg: 'surfaceContainerLow',
        shadow: 'level1',
        _hover: {
          shadow: 'level2'
        }
      },
      filled: {
        bg: 'surfaceContainerHighest'
      },
      outlined: {
        bg: 'surface',
        borderWidth: '1px',
        borderStyle: 'solid',
        borderColor: 'outlineVariant'
      }
    },
    interactive: {
      true: {
        cursor: 'pointer',
        _hover: {
          opacity: 0.96
        },
        _active: {
          opacity: 0.92
        }
      }
    }
  },
  defaultVariants: {
    variant: 'elevated',
    interactive: false
  }
});
```

### src/recipes/icon-button.recipe.ts

```typescript
import { defineRecipe } from '@pandacss/dev';

export const iconButtonRecipe = defineRecipe({
  className: 'icon-button',
  description: 'Material Design 3 icon button component',
  base: {
    display: 'inline-flex',
    alignItems: 'center',
    justifyContent: 'center',
    borderRadius: 'full',
    cursor: 'pointer',
    transition: 'all',
    transitionDuration: 'fast',
    transitionTimingFunction: 'standard',
    outline: 'none',
    border: 'none',
    p: '0',
    _disabled: {
      opacity: 0.38,
      cursor: 'not-allowed',
      pointerEvents: 'none'
    },
    _focusVisible: {
      outline: '2px solid',
      outlineColor: 'primary',
      outlineOffset: '2px'
    }
  },
  variants: {
    variant: {
      standard: {
        bg: 'transparent',
        color: 'onSurfaceVariant',
        _hover: {
          bg: 'onSurfaceVariant',
          bgOpacity: 0.08
        }
      },
      filled: {
        bg: 'primary',
        color: 'onPrimary',
        _hover: {
          opacity: 0.92
        }
      },
      tonal: {
        bg: 'secondaryContainer',
        color: 'onSecondaryContainer',
        _hover: {
          opacity: 0.92
        }
      },
      outlined: {
        bg: 'transparent',
        color: 'onSurfaceVariant',
        borderWidth: '1px',
        borderStyle: 'solid',
        borderColor: 'outline',
        _hover: {
          bg: 'onSurfaceVariant',
          bgOpacity: 0.08
        }
      }
    },
    size: {
      sm: {
        width: '32px',
        height: '32px',
        '& svg': {
          width: '18px',
          height: '18px'
        }
      },
      md: {
        width: '40px',
        height: '40px',
        '& svg': {
          width: '24px',
          height: '24px'
        }
      },
      lg: {
        width: '48px',
        height: '48px',
        '& svg': {
          width: '24px',
          height: '24px'
        }
      }
    }
  },
  defaultVariants: {
    variant: 'standard',
    size: 'md'
  }
});
```

### src/recipes/index.ts

```typescript
export { buttonRecipe } from './button.recipe';
export { cardRecipe } from './card.recipe';
export { iconButtonRecipe } from './icon-button.recipe';
```

### src/utils/cn.ts

```typescript
import { clsx, type ClassValue } from 'clsx';

/**
 * Utility function to merge class names
 */
export function cn(...inputs: ClassValue[]) {
  return clsx(inputs);
}
```

### src/components/Button/Button.tsx

```typescript
import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from 'react';
import { button, type ButtonVariantProps } from 'styled-system/recipes';
import { cn } from '../../utils/cn';

export interface ButtonProps
  extends ButtonHTMLAttributes<HTMLButtonElement>,
    ButtonVariantProps {
  children: ReactNode;
  leftIcon?: ReactNode;
  rightIcon?: ReactNode;
}

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  (
    { children, variant, size, leftIcon, rightIcon, className, ...props },
    ref
  ) => {
    return (
      <button
        ref={ref}
        className={cn(button({ variant, size }), className)}
        {...props}
      >
        {leftIcon}
        {children}
        {rightIcon}
      </button>
    );
  }
);

Button.displayName = 'Button';
```

### src/components/Button/Button.stories.tsx

```typescript
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
  title: 'Components/Button',
  component: Button,
  parameters: {
    layout: 'centered',
  },
  tags: ['autodocs'],
  argTypes: {
    variant: {
      control: 'select',
      options: ['filled', 'outlined', 'text', 'elevated', 'tonal'],
    },
    size: {
      control: 'select',
      options: ['sm', 'md', 'lg'],
    },
  },
};

export default meta;
type Story = StoryObj<typeof meta>;

export const Filled: Story = {
  args: {
    children: 'Filled Button',
    variant: 'filled',
  },
};

export const Outlined: Story = {
  args: {
    children: 'Outlined Button',
    variant: 'outlined',
  },
};

export const Text: Story = {
  args: {
    children: 'Text Button',
    variant: 'text',
  },
};

export const Elevated: Story = {
  args: {
    children: 'Elevated Button',
    variant: 'elevated',
  },
};

export const Tonal: Story = {
  args: {
    children: 'Tonal Button',
    variant: 'tonal',
  },
};

export const Sizes: Story = {
  render: () => (
    <div style={{ display: 'flex', gap: '16px', alignItems: 'center' }}>
      <Button size="sm">Small</Button>
      <Button size="md">Medium</Button>
      <Button size="lg">Large</Button>
    </div>
  ),
};

export const Disabled: Story = {
  args: {
    children: 'Disabled Button',
    disabled: true,
  },
};
```

### src/components/Button/index.ts

```typescript
export { Button, type ButtonProps } from './Button';
```

### src/components/index.ts

```typescript
export * from './Button';
// export * from './Card';
// export * from './Dialog';
```

### src/index.ts

```typescript
// Components
export * from './components';

// Recipes (for direct usage)
export * from './recipes';

// Language system
export * from './languages';

// Contracts
export type * from './contracts/design-language.contract';

// Utilities
export { cn } from './utils/cn';
```

### scripts/generate-palette.ts

```typescript
import {
  argbFromHex,
  hexFromArgb,
  TonalPalette,
  Hct,
  themeFromSourceColor
} from '@material/material-color-utilities';
import fs from 'fs';
import path from 'path';

// TastyMakers source color from Material Theme Builder
const SOURCE_COLOR = '#63A002';

function generatePalette() {
  const theme = themeFromSourceColor(argbFromHex(SOURCE_COLOR));
  
  const tones = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99, 100];
  
  const palettes = {
    primary: extractTones(theme.palettes.primary, tones),
    secondary: extractTones(theme.palettes.secondary, tones),
    tertiary: extractTones(theme.palettes.tertiary, tones),
    neutral: extractTones(theme.palettes.neutral, tones),
    neutralVariant: extractTones(theme.palettes.neutralVariant, tones),
    error: extractTones(theme.palettes.error, tones),
  };
  
  const schemes = {
    light: formatScheme(theme.schemes.light),
    dark: formatScheme(theme.schemes.dark),
  };
  
  const output = {
    sourceColor: SOURCE_COLOR,
    palettes,
    schemes,
  };
  
  const outputPath = path.join(process.cwd(), 'tokens/generated-palette.json');
  fs.writeFileSync(outputPath, JSON.stringify(output, null, 2));
  
  console.log('✅ Palette generated!');
  console.log(`   Source: ${SOURCE_COLOR}`);
  console.log(`   Output: ${outputPath}`);
  console.log('');
  console.log('🎨 Primary tones:');
  Object.entries(palettes.primary).forEach(([tone, hex]) => {
    console.log(`   ${tone}: ${hex}`);
  });
}

function extractTones(palette: TonalPalette, tones: number[]) {
  return Object.fromEntries(
    tones.map(tone => [tone.toString(), hexFromArgb(palette.tone(tone))])
  );
}

function formatScheme(scheme: any) {
  const result: Record<string, string> = {};
  for (const [key, value] of Object.entries(scheme)) {
    if (typeof value === 'number') {
      result[key] = hexFromArgb(value);
    }
  }
  return result;
}

generatePalette();
```

### .storybook/main.ts

```typescript
import type { StorybookConfig } from '@storybook/react-vite';

const config: StorybookConfig = {
  stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  addons: [
    '@storybook/addon-essentials',
    '@storybook/addon-a11y',
  ],
  framework: {
    name: '@storybook/react-vite',
    options: {},
  },
  docs: {
    autodocs: 'tag',
  },
};

export default config;
```

### .storybook/preview.ts

```typescript
import type { Preview } from '@storybook/react';
import '../styled-system/styles.css';

const preview: Preview = {
  parameters: {
    actions: { argTypesRegex: '^on[A-Z].*' },
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i,
      },
    },
    backgrounds: {
      default: 'surface',
      values: [
        { name: 'surface', value: '#F9FAEF' },
        { name: 'dark', value: '#12140E' },
      ],
    },
  },
};

export default preview;
```

### .npmrc

```
auto-install-peers=true
strict-peer-dependencies=false
```

### .gitignore

```
# Dependencies
node_modules/

# Build outputs
dist/
styled-system/
storybook-static/

# IDE
.vscode/
.idea/
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# Logs
*.log
npm-debug.log*
pnpm-debug.log*

# Environment
.env
.env.local
.env.*.local

# Testing
coverage/

# Misc
*.tgz
```

### vitest.config.ts

```typescript
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  test: {
    environment: 'jsdom',
    globals: true,
    setupFiles: ['./src/test/setup.ts'],
  },
  resolve: {
    alias: {
      '@': './src',
      'styled-system': './styled-system',
    },
  },
});
```

### README.md

```markdown
# @discourser/design-system

An aesthetic-agnostic design system built with Panda CSS and Ark UI.

## Features

- 🎨 **Swappable aesthetics** - Change the entire look by swapping one import
- 🎯 **Zero runtime CSS** - SSR-safe with Panda CSS
- ♿ **Accessible** - WAI-ARIA compliant via Ark UI
- 📦 **Tree-shakeable** - Only import what you need
- 🌙 **Dark mode** - Built-in light/dark theme support
- 🎭 **Material Design 3** - M3 Expressive as default aesthetic

## Installation

\`\`\`bash
pnpm add @discourser/design-system
\`\`\`

## Quick Start

\`\`\`tsx
import { Button } from '@discourser/design-system';
import '@discourser/design-system/styled-system/styles.css';

function App() {
  return (
    <Button variant="filled" size="md">
      Click me
    </Button>
  );
}
\`\`\`

## Development

\`\`\`bash
# Install dependencies
pnpm install

# Start Storybook
pnpm dev

# Build
pnpm build

# Generate color palette from source color
pnpm tokens:generate

# Run tests
pnpm test
\`\`\`

## Architecture

This design system uses a three-layer architecture:

1. **Infrastructure** - Token pipeline, build system, component logic
2. **Design Language** - Swappable aesthetic (colors, typography, spacing)
3. **Component Recipes** - Visual styling derived from the language

To change the aesthetic, edit `src/languages/index.ts` and point to a different language file.

## License

MIT
```

## Post-Setup Commands

After Copilot creates all files, run:

```bash
# Install dependencies
pnpm install

# Generate Panda CSS output
pnpm build:panda

# Generate M3 palette from source color (optional - values already in language file)
pnpm tokens:generate

# Start Storybook to verify everything works
pnpm dev
```

## Success Criteria

1. ✅ `pnpm install` completes without errors
2. ✅ `pnpm build:panda` generates `styled-system/` folder
3. ✅ `pnpm dev` launches Storybook
4. ✅ Button component renders with M3 styling
5. ✅ All 5 button variants visible in Storybook
6. ✅ `pnpm build` creates `dist/` with ESM/CJS outputs

## Notes

- The color values in `material3.language.ts` are placeholders based on chartreuse (#CDDC39)
- Run `pnpm tokens:generate` to create accurate M3 tonal palettes
- Typography uses Georgia (display) and Inter (body) - ensure fonts are loaded in consuming apps

---

## Implementation Phases

This section outlines the phased approach to implementing the design system. Each phase builds on the previous one and includes validation gates.

### Phase 0: Context Engineering Foundation

**Goal:** Set up Claude Code Skills and CLAUDE.md for efficient AI-assisted development.

**Deliverables:**
```
.claude/
├── commands/
│   ├── fix-foundation.md       # Phase 1 automation
│   ├── implement-architecture.md # Phase 2 automation  
│   └── new-component.md        # Component scaffolding
└── skills/
    ├── design-language/
    │   └── SKILL.md            # Contract architecture knowledge
    ├── panda-recipes/
    │   └── SKILL.md            # Recipe patterns with M3
    ├── m3-tokens/
    │   └── SKILL.md            # M3 color values reference
    └── component-patterns/
        └── SKILL.md            # forwardRef, Ark UI patterns

CLAUDE.md                       # Slim (<300 lines), pointer-based project context
```

**Validation:**
- [ ] `/fix-foundation` command recognized in Claude Code
- [ ] Skills auto-load when relevant tasks are requested

---

### Phase 1: Fix Foundation

**Goal:** Establish correct dependencies, configuration, and build pipeline.

**Tasks:**
1. Update `package.json` with correct versions:
    - `@ark-ui/react` ^4.4.0
    - `@pandacss/dev` ^0.52.0
    - `@material/material-color-utilities` ^0.3.0
    - React 19, Storybook 8.5, Vitest 2.x
2. Update `tsconfig.json` with path aliases
3. Update `.gitignore` (add `styled-system/`, `storybook-static/`)
4. Update `.npmrc` for peer dependency handling

**Validation:**
```bash
pnpm install           # ✅ No errors
pnpm build:panda       # ✅ Generates styled-system/
pnpm dev               # ✅ Storybook starts
```

---

### Phase 2: Architecture Implementation

**Goal:** Implement the three-layer Contract → Language → Transform architecture.

**Tasks:**
1. Create `src/contracts/design-language.contract.ts`
    - Full `DesignLanguageContract` interface
    - All supporting types (ColorPalettes, SemanticColors, Typography, etc.)

2. Create `src/languages/material3.language.ts`
    - Implement `DesignLanguageContract`
    - Use values from `docs/material-theme.json`
    - Include `semantic` (light) and `semanticDark` (dark)

3. Create `src/languages/transform.ts`
    - `transformToPandaTheme(language)` function
    - Returns `{ tokens, semanticTokens, textStyles }`

4. Create `src/languages/index.ts`
    - Export `material3Language as activeLanguage`
    - Re-export transformer

5. Update `panda.config.ts` to use the transform

**Validation:**
```bash
pnpm build:panda       # ✅ No errors
# Check styled-system/tokens/ contains M3 semantic colors
```

---

### Phase 3: M3 Token Integration

**Goal:** Integrate full M3 token set from Material Theme Builder export.

**Tasks:**
1. Verify `docs/material-theme.json` contains complete export
2. Ensure all tonal palettes (primary, secondary, tertiary, neutral, neutralVariant, error) are in language file
3. Ensure all semantic tokens are mapped for both light and dark
4. Add typography scale (display, headline, title, body, label)
5. Add spacing, shape (radii), elevation (shadows), motion (durations, easings)

**Validation:**
```bash
pnpm build:panda
# Verify in Storybook:
# - Colors render correctly
# - Typography scale works
# - Dark mode toggles properly (data-theme="dark")
```

---

### Phase 4: Complete Recipe Coverage + Testing

**Goal:** Ensure all components use the recipe pattern with full M3 variant support and comprehensive test coverage.

**Tasks:**

#### 4.1 Input Recipe & Component
```typescript
// src/recipes/input.recipe.ts
import { defineRecipe } from '@pandacss/dev';

export const inputRecipe = defineRecipe({
  className: 'input',
  description: 'Material Design 3 text field component',
  base: {
    display: 'flex',
    flexDirection: 'column',
    gap: 'xs',
  },
  variants: {
    variant: {
      filled: {
        // M3 filled text field styling
      },
      outlined: {
        // M3 outlined text field styling
      },
    },
    size: {
      sm: { /* ... */ },
      md: { /* ... */ },
    },
    state: {
      error: { /* ... */ },
      disabled: { /* ... */ },
    },
  },
  defaultVariants: {
    variant: 'outlined',
    size: 'md',
  },
});
```

```typescript
// src/components/Input/Input.tsx
import { forwardRef } from 'react';
import { Field } from '@ark-ui/react';
import { input, type InputVariantProps } from 'styled-system/recipes';
import { cn } from '../../utils/cn';

export interface InputProps
  extends React.InputHTMLAttributes<HTMLInputElement>,
    InputVariantProps {
  label?: string;
  helperText?: string;
  errorText?: string;
}

export const Input = forwardRef<HTMLInputElement, InputProps>(
  ({ label, helperText, errorText, variant, size, state, className, ...props }, ref) => {
    const hasError = !!errorText || state === 'error';
    
    return (
      <Field.Root invalid={hasError}>
        {label && <Field.Label>{label}</Field.Label>}
        <Field.Input
          ref={ref}
          className={cn(input({ variant, size, state: hasError ? 'error' : state }), className)}
          {...props}
        />
        {helperText && !hasError && <Field.HelperText>{helperText}</Field.HelperText>}
        {errorText && <Field.ErrorText>{errorText}</Field.ErrorText>}
      </Field.Root>
    );
  }
);

Input.displayName = 'Input';
```

#### 4.2 Dialog Recipe & Component (Compound Pattern)
```typescript
// src/recipes/dialog.recipe.ts
import { defineSlotRecipe } from '@pandacss/dev';

export const dialogRecipe = defineSlotRecipe({
  className: 'dialog',
  description: 'Material Design 3 dialog component',
  slots: ['backdrop', 'positioner', 'content', 'title', 'description', 'closeTrigger'],
  base: {
    backdrop: {
      position: 'fixed',
      inset: 0,
      bg: 'scrim',
      opacity: 0.32,
      zIndex: 'modal',
    },
    positioner: {
      position: 'fixed',
      inset: 0,
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      zIndex: 'modal',
    },
    content: {
      bg: 'surfaceContainerHigh',
      borderRadius: 'extraLarge',
      p: 'lg',
      shadow: 'level3',
      maxWidth: '560px',
      minWidth: '280px',
    },
    title: {
      textStyle: 'headlineSmall',
      color: 'onSurface',
      mb: 'md',
    },
    description: {
      textStyle: 'bodyMedium',
      color: 'onSurfaceVariant',
    },
    closeTrigger: {
      position: 'absolute',
      top: 'md',
      right: 'md',
    },
  },
  variants: {
    size: {
      sm: {
        content: { maxWidth: '400px' },
      },
      md: {
        content: { maxWidth: '560px' },
      },
      lg: {
        content: { maxWidth: '720px' },
      },
      fullscreen: {
        content: {
          maxWidth: '100vw',
          maxHeight: '100vh',
          borderRadius: 'none',
          m: 0,
        },
      },
    },
  },
  defaultVariants: {
    size: 'md',
  },
});
```

```typescript
// src/components/Dialog/Dialog.tsx
import { forwardRef } from 'react';
import { Dialog as ArkDialog } from '@ark-ui/react';
import { dialog, type DialogVariantProps } from 'styled-system/recipes';
import { cn } from '../../utils/cn';

const styles = dialog();

export interface DialogProps extends ArkDialog.RootProps, DialogVariantProps {}

const DialogRoot = (props: DialogProps) => <ArkDialog.Root {...props} />;

const DialogTrigger = forwardRef<HTMLButtonElement, ArkDialog.TriggerProps>(
  ({ className, ...props }, ref) => (
    <ArkDialog.Trigger ref={ref} className={className} {...props} />
  )
);
DialogTrigger.displayName = 'DialogTrigger';

const DialogBackdrop = forwardRef<HTMLDivElement, ArkDialog.BackdropProps>(
  ({ className, ...props }, ref) => (
    <ArkDialog.Backdrop ref={ref} className={cn(styles.backdrop, className)} {...props} />
  )
);
DialogBackdrop.displayName = 'DialogBackdrop';

const DialogPositioner = forwardRef<HTMLDivElement, ArkDialog.PositionerProps>(
  ({ className, ...props }, ref) => (
    <ArkDialog.Positioner ref={ref} className={cn(styles.positioner, className)} {...props} />
  )
);
DialogPositioner.displayName = 'DialogPositioner';

const DialogContent = forwardRef<HTMLDivElement, ArkDialog.ContentProps>(
  ({ className, ...props }, ref) => (
    <ArkDialog.Content ref={ref} className={cn(styles.content, className)} {...props} />
  )
);
DialogContent.displayName = 'DialogContent';

const DialogTitle = forwardRef<HTMLHeadingElement, ArkDialog.TitleProps>(
  ({ className, ...props }, ref) => (
    <ArkDialog.Title ref={ref} className={cn(styles.title, className)} {...props} />
  )
);
DialogTitle.displayName = 'DialogTitle';

const DialogDescription = forwardRef<HTMLParagraphElement, ArkDialog.DescriptionProps>(
  ({ className, ...props }, ref) => (
    <ArkDialog.Description ref={ref} className={cn(styles.description, className)} {...props} />
  )
);
DialogDescription.displayName = 'DialogDescription';

const DialogCloseTrigger = forwardRef<HTMLButtonElement, ArkDialog.CloseTriggerProps>(
  ({ className, ...props }, ref) => (
    <ArkDialog.CloseTrigger ref={ref} className={cn(styles.closeTrigger, className)} {...props} />
  )
);
DialogCloseTrigger.displayName = 'DialogCloseTrigger';

export const Dialog = {
  Root: DialogRoot,
  Trigger: DialogTrigger,
  Backdrop: DialogBackdrop,
  Positioner: DialogPositioner,
  Content: DialogContent,
  Title: DialogTitle,
  Description: DialogDescription,
  CloseTrigger: DialogCloseTrigger,
};
```

#### 4.3 Register All Recipes
```typescript
// panda.config.ts - update theme.extend.recipes
recipes: {
  button: buttonRecipe,
  card: cardRecipe,
  iconButton: iconButtonRecipe,
  input: inputRecipe,
},
slotRecipes: {
  dialog: dialogRecipe,
},
```

#### 4.4 Component Testing Requirements

Every component MUST have accompanying tests. Create test files alongside components.

**Install test dependencies:**
```bash
pnpm add -D @testing-library/react @testing-library/jest-dom @testing-library/user-event jest-axe
```

**Test Setup:**
```typescript
// src/test/setup.ts
import '@testing-library/jest-dom';
import { vi } from 'vitest';

// Mock matchMedia for components that use media queries
Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: vi.fn().mockImplementation(query => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: vi.fn(),
    removeListener: vi.fn(),
    addEventListener: vi.fn(),
    removeEventListener: vi.fn(),
    dispatchEvent: vi.fn(),
  })),
});
```

**Input Component Tests:**
```typescript
// src/components/Input/Input.test.tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi } from 'vitest';
import { Input } from './Input';

describe('Input', () => {
  it('renders with label', () => {
    render(<Input label="Email" />);
    expect(screen.getByLabelText('Email')).toBeInTheDocument();
  });

  it('shows error state', () => {
    render(<Input label="Email" errorText="Invalid email" />);
    expect(screen.getByText('Invalid email')).toBeInTheDocument();
  });

  it('calls onChange when typing', async () => {
    const user = userEvent.setup();
    const handleChange = vi.fn();
    render(<Input label="Email" onChange={handleChange} />);
    
    await user.type(screen.getByLabelText('Email'), 'test@example.com');
    expect(handleChange).toHaveBeenCalled();
  });

  it('renders all variants', () => {
    const { rerender } = render(<Input variant="outlined" label="Test" />);
    expect(screen.getByLabelText('Test')).toBeInTheDocument();
    
    rerender(<Input variant="filled" label="Test" />);
    expect(screen.getByLabelText('Test')).toBeInTheDocument();
  });

  it('is disabled when disabled prop is true', () => {
    render(<Input label="Email" disabled />);
    expect(screen.getByLabelText('Email')).toBeDisabled();
  });
});
```

**Dialog Component Tests:**
```typescript
// src/components/Dialog/Dialog.test.tsx
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect } from 'vitest';
import { Dialog } from './Dialog';
import { Button } from '../Button';

describe('Dialog', () => {
  it('opens when trigger is clicked', async () => {
    const user = userEvent.setup();
    render(
      <Dialog.Root>
        <Dialog.Trigger asChild>
          <Button>Open</Button>
        </Dialog.Trigger>
        <Dialog.Backdrop />
        <Dialog.Positioner>
          <Dialog.Content>
            <Dialog.Title>Test Dialog</Dialog.Title>
            <Dialog.Description>Test content</Dialog.Description>
          </Dialog.Content>
        </Dialog.Positioner>
      </Dialog.Root>
    );

    await user.click(screen.getByText('Open'));
    await waitFor(() => {
      expect(screen.getByText('Test Dialog')).toBeInTheDocument();
    });
  });

  it('closes when close trigger is clicked', async () => {
    const user = userEvent.setup();
    render(
      <Dialog.Root defaultOpen>
        <Dialog.Backdrop />
        <Dialog.Positioner>
          <Dialog.Content>
            <Dialog.Title>Test Dialog</Dialog.Title>
            <Dialog.CloseTrigger>Close</Dialog.CloseTrigger>
          </Dialog.Content>
        </Dialog.Positioner>
      </Dialog.Root>
    );

    await user.click(screen.getByText('Close'));
    await waitFor(() => {
      expect(screen.queryByText('Test Dialog')).not.toBeInTheDocument();
    });
  });

  it('traps focus within dialog', async () => {
    const user = userEvent.setup();
    render(
      <Dialog.Root defaultOpen>
        <Dialog.Backdrop />
        <Dialog.Positioner>
          <Dialog.Content>
            <Dialog.Title>Test Dialog</Dialog.Title>
            <Button>First</Button>
            <Button>Second</Button>
            <Dialog.CloseTrigger>Close</Dialog.CloseTrigger>
          </Dialog.Content>
        </Dialog.Positioner>
      </Dialog.Root>
    );

    await user.tab();
    expect(screen.getByText('First')).toHaveFocus();
    
    await user.tab();
    expect(screen.getByText('Second')).toHaveFocus();
  });
});
```

**Validation:**
```bash
pnpm build:panda       # ✅ All recipes generate
pnpm dev               # ✅ All components render in Storybook
pnpm test              # ✅ All component tests pass
```

---

### Phase 5: Ark UI Integration + Accessibility Testing

**Goal:** Ensure all components properly leverage Ark UI for accessibility and state management, with comprehensive accessibility tests.

**Tasks:**

#### 5.1 Audit Current Components
Review each component and ensure it uses Ark UI primitives where applicable:

| Component | Current | Target |
|-----------|---------|--------|
| Button | `<button>` | `<Ark.Button>` or keep native (simple) |
| Card | `<div>` | Keep native (no interaction state) |
| IconButton | `<button>` | `<Ark.Button>` |
| Input | Native | `<Field.Root>`, `<Field.Input>`, etc. |
| Dialog | N/A | Full Ark Dialog compound |

#### 5.2 Implement Missing Ark UI Patterns

**Button with Ark UI:**
```typescript
// src/components/Button/Button.tsx
import { Button as ArkButton } from '@ark-ui/react';

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  ({ children, variant, size, leftIcon, rightIcon, className, ...props }, ref) => {
    return (
      <ArkButton
        ref={ref}
        className={cn(button({ variant, size }), className)}
        {...props}
      >
        {leftIcon}
        {children}
        {rightIcon}
      </ArkButton>
    );
  }
);
```

#### 5.3 Add Additional Ark UI Components

Consider adding these M3 components using Ark UI:
- **Menu** - `@ark-ui/react` Menu
- **Tabs** - `@ark-ui/react` Tabs
- **Tooltip** - `@ark-ui/react` Tooltip
- **Switch** - `@ark-ui/react` Switch
- **Checkbox** - `@ark-ui/react` Checkbox
- **Select** - `@ark-ui/react` Select

#### 5.4 Accessibility Testing

Every component MUST include accessibility tests using jest-axe:

```typescript
// src/components/Button/Button.a11y.test.tsx
import { axe, toHaveNoViolations } from 'jest-axe';
import { render } from '@testing-library/react';
import { Button } from './Button';

expect.extend(toHaveNoViolations);

describe('Button accessibility', () => {
  it('has no accessibility violations', async () => {
    const { container } = render(<Button>Click me</Button>);
    const results = await axe(container);
    expect(results).toHaveNoViolations();
  });

  it('has no violations when disabled', async () => {
    const { container } = render(<Button disabled>Disabled</Button>);
    const results = await axe(container);
    expect(results).toHaveNoViolations();
  });

  it('has no violations for all variants', async () => {
    const variants = ['filled', 'outlined', 'text', 'elevated', 'tonal'] as const;
    
    for (const variant of variants) {
      const { container } = render(<Button variant={variant}>Test</Button>);
      const results = await axe(container);
      expect(results).toHaveNoViolations();
    }
  });
});
```

```typescript
// src/components/Dialog/Dialog.a11y.test.tsx
import { axe, toHaveNoViolations } from 'jest-axe';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Dialog } from './Dialog';
import { Button } from '../Button';

expect.extend(toHaveNoViolations);

describe('Dialog accessibility', () => {
  it('has no accessibility violations when open', async () => {
    const { container } = render(
      <Dialog.Root defaultOpen>
        <Dialog.Backdrop />
        <Dialog.Positioner>
          <Dialog.Content>
            <Dialog.Title>Accessible Dialog</Dialog.Title>
            <Dialog.Description>This dialog should be accessible.</Dialog.Description>
            <Dialog.CloseTrigger>Close</Dialog.CloseTrigger>
          </Dialog.Content>
        </Dialog.Positioner>
      </Dialog.Root>
    );
    
    const results = await axe(container);
    expect(results).toHaveNoViolations();
  });

  it('has proper aria attributes', async () => {
    render(
      <Dialog.Root defaultOpen>
        <Dialog.Backdrop />
        <Dialog.Positioner>
          <Dialog.Content>
            <Dialog.Title>Test Title</Dialog.Title>
            <Dialog.Description>Test Description</Dialog.Description>
          </Dialog.Content>
        </Dialog.Positioner>
      </Dialog.Root>
    );

    const dialog = screen.getByRole('dialog');
    expect(dialog).toHaveAttribute('aria-labelledby');
    expect(dialog).toHaveAttribute('aria-describedby');
  });
});
```

**Validation:**
```bash
pnpm dev               # ✅ Components work correctly
# Test keyboard navigation in Storybook
# Test screen reader announcements
pnpm test              # ✅ All tests pass (unit + a11y)
```

---

### Phase 6: Build & Package (Figma Make Compatible)

**Goal:** Create a publishable npm package compatible with Figma Make (Vite-based) with proper exports, types, and documentation.

**Requirements from Figma Make (https://developers.figma.com/docs/code/bring-your-design-system-package/):**
- ✅ React 18+ (we use React 19)
- ✅ Compatible with Vite
- ✅ Published as npm package (public or private)

**Tasks:**

#### 6.1 Verify Vite Compatibility

Figma Make uses Vite as its build system. Test package compatibility:

```bash
# Create a test Vite project
npm create vite@latest make-test-app -- --template react-ts
cd make-test-app

# Install your local package
pnpm add ../path/to/tastymakers-design-system-0.1.0.tgz

# Test the import
cat > src/App.tsx << 'EOF'
import { Button, Card } from '@discourser/design-system';
import '@discourser/design-system/styles.css';

function App() {
  return (
    <Card>
      <Button variant="filled">Test Button</Button>
    </Card>
  );
}
export default App;
EOF

# Build - must succeed for Figma Make compatibility
pnpm build
```

#### 6.2 Verify tsup Configuration
```typescript
// tsup.config.ts
import { defineConfig } from 'tsup';

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  dts: true,
  splitting: true,
  sourcemap: true,
  clean: true,
  external: ['react', 'react-dom'],
  esbuildOptions(options) {
    options.banner = {
      js: '"use client"',
    };
  },
});
```

#### 6.3 Verify Package Exports
```json
// package.json exports field
{
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/index.cjs",
      "types": "./dist/index.d.ts"
    },
    "./styles.css": "./styled-system/styles.css",
    "./styled-system": {
      "import": "./styled-system/index.mjs",
      "require": "./styled-system/index.js"
    },
    "./styled-system/css": {
      "import": "./styled-system/css/index.mjs",
      "require": "./styled-system/css/index.js"
    },
    "./styled-system/tokens": {
      "import": "./styled-system/tokens/index.mjs",
      "require": "./styled-system/tokens/index.js"
    },
    "./styled-system/recipes": {
      "import": "./styled-system/recipes/index.mjs",
      "require": "./styled-system/recipes/index.js"
    }
  }
}
```

#### 6.4 Add CI/CD Workflows

```yaml
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
        with:
          version: 8
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'
      - run: pnpm install
      - run: pnpm typecheck
      - run: pnpm build:panda
      - run: pnpm build
      - run: pnpm test

  vite-compatibility:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
        with:
          version: 8
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'
      - run: pnpm install
      - run: pnpm build
      - run: pnpm pack
      - name: Test Vite Compatibility
        run: |
          npm create vite@latest test-app -- --template react-ts
          cd test-app
          npm install ../tastymakers-design-system-*.tgz
          npm run build
```

```yaml
# .github/workflows/publish.yml
name: Publish

on:
  release:
    types: [created]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
        with:
          version: 8
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          registry-url: 'https://registry.npmjs.org'
      - run: pnpm install
      - run: pnpm build
      - run: pnpm publish --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.
             }}
```

#### 6.5 Create npm Organization

Before publishing, create the `@discourser` npm organization:

1. Go to https://www.npmjs.com/org/create
2. Create organization with name `discourser`
3. Choose "Unlimited public packages" (free tier)
4. Verify organization exists at https://www.npmjs.com/org/discourser

**Note:** npm allows multiple organizations per user account.

#### 6.6 Configure npm Authentication Locally

```bash
# Login to npm (if not already)
npm login

# Verify you can publish to the org
npm access ls-packages @discourser
```

#### 6.7 Install & Configure Changesets

Changesets provides automated versioning and changelog generation.

**Install dependencies:**
```bash
pnpm add -D @changesets/cli @changesets/changelog-github
pnpm changeset init
```

**Configure `.changeset/config.json`:**
```json
{
  "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
  "changelog": [
    "@changesets/changelog-github",
    { "repo": "Tasty-Maker-Studio/Discourser-Design-System" }
  ],
  "commit": false,
  "fixed": [],
  "linked": [],
  "access": "public",
  "baseBranch": "main",
  "updateInternalDependencies": "patch",
  "ignore": []
}
```

**Create `.changeset/README.md`:**
```markdown
# Changesets

Hello and welcome! This folder has been automatically generated by `@changesets/cli`.

## How to add a changeset

1. Run `pnpm changeset`
2. Select the type of change (patch/minor/major)
3. Write a summary of the change
4. Commit the generated changeset file

Changesets are automatically consumed when the "Version Packages" PR is merged.
```

**Create `CHANGELOG.md` (root):**
```markdown
# @discourser/design-system

## Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
```

**Update `package.json`:**
```json
{
  "name": "@discourser/design-system",
  "version": "0.0.0",
  "publishConfig": {
    "access": "public",
    "registry": "https://registry.npmjs.org"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/Tasty-Maker-Studio/Discourser-Design-System.git"
  },
  "scripts": {
    "changeset": "changeset",
    "version": "changeset version",
    "release": "pnpm build && changeset publish"
  }
}
```

**Validation:**
```bash
pnpm changeset          # ✅ Interactive prompt works
pnpm changeset status   # ✅ Shows no changesets (or lists pending)
```

#### 6.8 Create CI Workflow

Create `.github/workflows/ci.yml`:

```yaml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  build:
    name: Build & Test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 9

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Typecheck
        run: pnpm typecheck

      - name: Build Panda CSS
        run: pnpm build:panda

      - name: Build library
        run: pnpm build:lib

      - name: Run tests
        run: pnpm test

      - name: Upload build artifacts
        uses: actions/upload-artifact@v4
        with:
          name: build-output
          path: |
            dist/
            styled-system/
          retention-days: 1

  vite-compatibility:
    name: Vite Compatibility
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 9

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Build package
        run: pnpm build

      - name: Create tarball
        run: pnpm pack

      - name: Test Vite compatibility
        run: |
          # Create test Vite app
          npm create vite@latest test-app -- --template react-ts
          cd test-app
          
          # Install the local package
          npm install ../discourser-design-system-*.tgz
          
          # Create test file
          cat > src/App.tsx << 'EOF'
          import { Button } from '@discourser/design-system';
          import '@discourser/design-system/styles.css';
          
          function App() {
            return <Button variant="filled">Test</Button>;
          }
          export default App;
          EOF
          
          # Build must succeed
          npm run build
```

#### 6.9 Create Release Workflow

Create `.github/workflows/release.yml`:

```yaml
name: Release

on:
  push:
    branches: [main]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: false

permissions:
  contents: write
  pull-requests: write

jobs:
  release:
    name: Release
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 9

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'
          registry-url: 'https://registry.npmjs.org'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Build
        run: pnpm build

      - name: Create Release Pull Request or Publish
        id: changesets
        uses: changesets/action@v1
        with:
          version: pnpm changeset version
          publish: pnpm release
          commit: "chore: version packages"
          title: "chore: version packages"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

      - name: Create Git Tag
        if: steps.changesets.outputs.published == 'true'
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          
          # Get the version from package.json
          VERSION=$(node -p "require('./package.json').version")
          
          # Create and push tag
          git tag -a "v$VERSION" -m "Release v$VERSION"
          git push origin "v$VERSION"
```

#### 6.10 Configure GitHub Secrets & Permissions (MANUAL)

This step requires manual configuration in the GitHub repository settings.

**Step 1: Generate npm Token**

1. Go to https://www.npmjs.com/settings/YOUR_USERNAME/tokens
2. Click "Generate New Token" → "Classic Token"
3. Select "Automation" type
4. Copy the generated token (starts with `npm_`)

**Step 2: Add Secret to GitHub**

1. Go to repository: https://github.com/Tasty-Maker-Studio/Discourser-Design-System
2. Navigate to Settings → Secrets and variables → Actions
3. Click "New repository secret"
4. Name: `NPM_TOKEN`
5. Value: Paste the npm token
6. Click "Add secret"

**Step 3: Configure Workflow Permissions**

1. Go to Settings → Actions → General
2. Scroll to "Workflow permissions"
3. Select "Read and write permissions"
4. Check "Allow GitHub Actions to create and approve pull requests"
5. Click "Save"

**Validation Checklist:**
- [ ] NPM_TOKEN secret is configured
- [ ] Workflow permissions allow write access
- [ ] Workflow permissions allow PR creation

#### 6.11 Validate Full Release Pipeline

Test the complete release workflow end-to-end.

**Step 1: Create a Test Changeset**
```bash
pnpm changeset

# Select: patch
# Summary: "Initial release - Button, Card, IconButton components"
# This creates a file in .changeset/ like `funny-dogs-dance.md`
```

**Step 2: Commit and Push**
```bash
git add .
git commit -m "chore: add changeset for initial release"
git push origin main
```

**Step 3: Verify CI Workflow**
1. Go to https://github.com/Tasty-Maker-Studio/Discourser-Design-System/actions
2. Verify "CI" workflow passes (build, test, vite-compatibility)

**Step 4: Verify Release Workflow**
1. After CI passes, "Release" workflow should run
2. It should create a PR titled "chore: version packages"

**Step 5: Review Version Packages PR**
The PR should contain:
- `package.json`: version bumped from `0.0.0` to `0.0.1`
- `CHANGELOG.md`: Updated with changeset content
- Changeset file deleted from `.changeset/`

**Step 6: Merge and Publish**
1. Review the PR
2. Merge to main
3. Release workflow runs again and publishes to npm

**Step 7: Verify npm Publication**
```bash
# Check package exists
npm view @discourser/design-system

# Or visit
# https://www.npmjs.com/package/@discourser/design-system
```

**Complete Release Flow Diagram:**
```
Developer: pnpm changeset → Creates .changeset/*.md
     ↓
Git push to main
     ↓
CI Workflow: Build, Test, Vite Compatibility
     ↓
Release Workflow: Detects changesets → Creates "Version Packages" PR
     ↓
Human: Reviews and merges PR
     ↓
Release Workflow: Runs again → Publishes to npm + Creates git tag
     ↓
Package available at npmjs.com
```

**Validation:**
```bash
# After full pipeline test:
npm view @discourser/design-system          # ✅ Package exists
npm view @discourser/design-system versions # ✅ Shows 0.0.1
```

#### 6.12 Test Local Package (Manual Verification)

For local testing before committing:

```bash
# Build the package
pnpm build

# Create a tarball
pnpm pack

# In a separate test Vite project
npm create vite@latest make-test-app -- --template react-ts
cd make-test-app
pnpm add ../path/to/discourser-design-system-0.0.1.tgz
pnpm build  # Must succeed for Figma Make compatibility
```

**Phase 6 Validation Summary:**
```bash
pnpm build             # ✅ Creates dist/ with ESM, CJS, and .d.ts
pnpm pack              # ✅ Creates tarball
pnpm test              # ✅ All tests pass
pnpm changeset         # ✅ Interactive prompt works
# CI workflow passes    # ✅ Build + Test + Vite compatibility
# Release workflow      # ✅ Creates Version Packages PR
# npm publish           # ✅ Package available on npmjs.com
```

---

### Phase 7: Figma Make Guidelines

**Goal:** Create guidelines that teach Figma Make how to use the design system package, following the [Figma documentation](https://developers.figma.com/docs/code/write-design-system-guidelines/).

**Background:** Figma Make's AI can inspect your package but works best with explicit guidelines. This is similar to documentation you'd give a new engineer.

**Tasks:**

#### 7.1 Create Guidelines Structure

```
guidelines/
├── Guidelines.md              # Top-level entry point (ALWAYS read first)
├── overview-components.md     # Component catalog and usage patterns
├── overview-icons.md          # Icon usage (if applicable)
├── design-tokens/
│   ├── colors.md              # Semantic color tokens
│   ├── typography.md          # Typography scale
│   ├── spacing.md             # Spacing tokens
│   └── elevation.md           # Shadow/elevation tokens
└── components/
    ├── button.md
    ├── card.md
    ├── icon-button.md
    ├── input.md
    ├── dialog.md
    └── [additional components].md
```

#### 7.2 Create Guidelines.md (Entry Point)

```markdown
<!-- guidelines/Guidelines.md -->
# TastyMakers Design System Guidelines

This project uses the `@discourser/design-system` package, a Material Design 3 implementation built with Panda CSS and Ark UI.

## IMPORTANT: Always Read These First

Before writing any code, follow these steps IN ORDER:

### Step 1: Read Overview Files (REQUIRED)
Read ALL files with a name that starts with "overview-":
- `overview-components.md` - Available components and usage patterns
- `overview-icons.md` - Icon usage (if applicable)

### Step 2: Read Design Token Files (REQUIRED)
Read ALL files in the `design-tokens/` folder:
- `design-tokens/colors.md`
- `design-tokens/typography.md`
- `design-tokens/spacing.md`
- `design-tokens/elevation.md`

### Step 3: Plan Components Needed (REQUIRED)
Identify which components you need to use.

### Step 4: Read Component Guidelines BEFORE Using Components (REQUIRED)
BEFORE using ANY component, you MUST read its guidelines file first:
- Using Button? → Read `components/button.md` FIRST
- Using Dialog? → Read `components/dialog.md` FIRST
- Using Input? → Read `components/input.md` FIRST
- Using Card? → Read `components/card.md` FIRST

DO NOT write code using a component until you have read its specific guidelines.

## Core Principles

- **Always prefer design system components** over native HTML elements
- **Use semantic tokens** (e.g., `primary`, `onPrimary`) not raw colors
- **Follow M3 patterns** for variants, sizing, and state layers
- **Do not override styles** unless absolutely necessary

## Package Imports

\`\`\`typescript
// Components
import { Button, Card, Dialog, Input, IconButton } from '@discourser/design-system';

// Styles (REQUIRED - must be imported)
import '@discourser/design-system/styles.css';
\`\`\`

## Quick Reference

| Component | Variants | Sizes | Guidelines |
|-----------|----------|-------|------------|
| Button | filled, outlined, text, elevated, tonal | sm, md, lg | `components/button.md` |
| Card | elevated, filled, outlined | - | `components/card.md` |
| IconButton | standard, filled, tonal, outlined | sm, md, lg | `components/icon-button.md` |
| Input | filled, outlined | sm, md | `components/input.md` |
| Dialog | - | sm, md, lg, fullscreen | `components/dialog.md` |
```

#### 7.3 Create overview-components.md

```markdown
<!-- guidelines/overview-components.md -->
# Components Overview

Always prefer components from `@discourser/design-system` if available. Do not use native HTML elements when a design system component exists.

## Available Components

| Component | Purpose | Guidelines |
|-----------|---------|------------|
| Button | Primary interactive element for actions | [button.md](components/button.md) |
| Card | Container for related content | [card.md](components/card.md) |
| IconButton | Icon-only interactive element | [icon-button.md](components/icon-button.md) |
| Input | Text input with label and validation | [input.md](components/input.md) |
| Dialog | Modal overlay for focused tasks | [dialog.md](components/dialog.md) |

## Common Props

Most components accept:
- `variant` - Visual style variant (e.g., filled, outlined)
- `size` - Size variant (sm, md, lg)
- `disabled` - Disable interaction
- `className` - Additional CSS classes (use sparingly)

## Styling Guidelines

**✅ DO:**
\`\`\`typescript
<Button variant="filled" size="md">Submit</Button>
<Card variant="elevated">Content</Card>
<Input variant="outlined" label="Email" />
\`\`\`

**❌ DO NOT:**
\`\`\`typescript
// Don't override styles with inline styles
<Button style={{ backgroundColor: 'blue' }}>Submit</Button>

// Don't use raw HTML when components exist
<button className="...">Submit</button>
<input type="text" />

// Don't use raw color values
<div style={{ backgroundColor: '#4C662B' }}>...</div>
\`\`\`

## Controlled vs Uncontrolled

- **Controlled**: Component receives `value` and `onChange` props
- **Uncontrolled**: Component manages own state, use `defaultValue`
- Prefer controlled for form components
```

#### 7.4 Create Design Token Guidelines

```markdown
<!-- guidelines/design-tokens/colors.md -->
# Color Tokens

The design system uses Material Design 3 semantic color tokens. Always use semantic tokens, never raw hex values.

## Semantic Colors (Light Theme)

### Primary
| Token | Usage | Example Value |
|-------|-------|---------------|
| `primary` | Primary actions, buttons, links | #4C662B |
| `onPrimary` | Text/icons on primary backgrounds | #FFFFFF |
| `primaryContainer` | Primary container backgrounds | #CDEDA3 |
| `onPrimaryContainer` | Text/icons on primary container | #354E16 |

### Secondary
| Token | Usage |
|-------|-------|
| `secondary` | Secondary actions |
| `onSecondary` | Text/icons on secondary |
| `secondaryContainer` | Secondary container backgrounds |
| `onSecondaryContainer` | Text/icons on secondary container |

### Surface
| Token | Usage |
|-------|-------|
| `surface` | Default background |
| `onSurface` | Default text color |
| `surfaceVariant` | Alternate surface |
| `onSurfaceVariant` | Text on variant surfaces |
| `surfaceContainerLowest` | Lowest elevation |
| `surfaceContainerLow` | Low elevation (cards) |
| `surfaceContainer` | Default containers |
| `surfaceContainerHigh` | Dialogs, elevated content |
| `surfaceContainerHighest` | Highest elevation |

### Error
| Token | Usage |
|-------|-------|
| `error` | Error states |
| `onError` | Text/icons on error |
| `errorContainer` | Error container backgrounds |
| `onErrorContainer` | Text/icons on error container |

### Other
| Token | Usage |
|-------|-------|
| `outline` | Borders, dividers |
| `outlineVariant` | Subtle borders |
| `scrim` | Modal overlays |
| `shadow` | Shadow color |

## Usage in Components

Colors are applied automatically through component variants:

\`\`\`typescript
// ✅ Correct - uses semantic tokens internally
<Button variant="filled">Primary Action</Button>

// ❌ Wrong - don't apply colors directly
<Button style={{ backgroundColor: '#4C662B' }}>Primary</Button>
\`\`\`

## Dark Mode

The design system supports dark mode via `data-theme="dark"` on a parent element. Semantic tokens automatically adjust.
```

```markdown
<!-- guidelines/design-tokens/typography.md -->
# Typography Tokens

The design system uses M3 typography scale with semantic naming.

## Font Families

| Token | Font | Usage |
|-------|------|-------|
| `display` | Georgia, serif | Display text, headings |
| `body` | Inter, sans-serif | Body text, UI elements |
| `mono` | JetBrains Mono | Code snippets |

## Type Scale

### Display (for hero sections, large text)
- `displayLarge` - 57px
- `displayMedium` - 45px
- `displaySmall` - 36px

### Headline (for page/section headers)
- `headlineLarge` - 32px
- `headlineMedium` - 28px
- `headlineSmall` - 24px

### Title (for card titles, dialogs)
- `titleLarge` - 22px
- `titleMedium` - 16px
- `titleSmall` - 14px

### Body (for content)
- `bodyLarge` - 16px (primary body text)
- `bodyMedium` - 14px (default body text)
- `bodySmall` - 12px (secondary text)

### Label (for buttons, form labels)
- `labelLarge` - 14px (button text)
- `labelMedium` - 12px (form labels)
- `labelSmall` - 11px (badges)

## Usage

Typography is applied through `textStyle` in Panda CSS:

\`\`\`typescript
import { css } from 'styled-system/css';

const heading = css({ textStyle: 'headlineMedium' });
const body = css({ textStyle: 'bodyMedium' });
\`\`\`
```

#### 7.5 Create Component Guidelines

```markdown
<!-- guidelines/components/button.md -->
# Button

**Purpose:** Primary interactive element for user actions.

## Import

\`\`\`typescript
import { Button } from '@discourser/design-system';
\`\`\`

## Variants

| Variant | Usage | When to Use |
|---------|-------|-------------|
| `filled` | Primary actions | Submit, Confirm, Main CTA |
| `outlined` | Secondary actions | Cancel, Back, Alternative options |
| `text` | Tertiary actions | Links, Less prominent actions |
| `elevated` | Floating actions | FAB-like buttons |
| `tonal` | Medium emphasis | Secondary CTA, Soft highlight |

## Sizes

| Size | Height | Usage |
|------|--------|-------|
| `sm` | 32px | Compact UI, dense layouts |
| `md` | 40px | Default, most use cases |
| `lg` | 48px | Touch targets, mobile emphasis |

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `variant` | `'filled' \| 'outlined' \| 'text' \| 'elevated' \| 'tonal'` | `'filled'` | Visual style |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Button size |
| `disabled` | `boolean` | `false` | Disable button |
| `leftIcon` | `ReactNode` | - | Icon before text |
| `rightIcon` | `ReactNode` | - | Icon after text |

## Examples

\`\`\`typescript
// Primary action
<Button variant="filled">Submit</Button>

// Secondary action
<Button variant="outlined">Cancel</Button>

// With icon
<Button variant="filled" leftIcon={<PlusIcon />}>Add Item</Button>

// Disabled
<Button variant="filled" disabled>Unavailable</Button>

// Different sizes
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
\`\`\`

## DO NOT

\`\`\`typescript
// ❌ Don't use native button when Button component exists
<button>Submit</button>

// ❌ Don't override button colors with inline styles
<Button style={{ backgroundColor: 'red' }}>Delete</Button>

// ❌ Don't combine conflicting sizes
<Button size="lg" style={{ height: '24px' }}>Small</Button>

// ❌ Don't add className to override core styles
<Button className="bg-blue-500">Custom</Button>
\`\`\`
```

```markdown
<!-- guidelines/components/dialog.md -->
# Dialog

**Purpose:** Modal overlay for focused tasks requiring user attention.

## Import

\`\`\`typescript
import { Dialog } from '@discourser/design-system';
\`\`\`

## Structure

Dialog uses a compound component pattern. All parts are required for proper accessibility:

\`\`\`typescript
<Dialog.Root>
  <Dialog.Trigger asChild>
    <Button>Open Dialog</Button>
  </Dialog.Trigger>
  <Dialog.Backdrop />
  <Dialog.Positioner>
    <Dialog.Content>
      <Dialog.Title>Dialog Title</Dialog.Title>
      <Dialog.Description>Dialog content goes here.</Dialog.Description>
      <Dialog.CloseTrigger asChild>
        <Button variant="text">Close</Button>
      </Dialog.CloseTrigger>
    </Dialog.Content>
  </Dialog.Positioner>
</Dialog.Root>
\`\`\`

## Parts

| Part | Required | Description |
|------|----------|-------------|
| `Dialog.Root` | Yes | Container, manages open/close state |
| `Dialog.Trigger` | No | Opens dialog when clicked |
| `Dialog.Backdrop` | Yes | Semi-transparent overlay behind dialog |
| `Dialog.Positioner` | Yes | Centers the content |
| `Dialog.Content` | Yes | The dialog panel |
| `Dialog.Title` | Yes | Accessible title (required for a11y) |
| `Dialog.Description` | No | Supporting text |
| `Dialog.CloseTrigger` | No | Closes dialog when clicked |

## Sizes

| Size | Max Width | Usage |
|------|-----------|-------|
| `sm` | 400px | Confirmations, simple prompts |
| `md` | 560px | Default, forms |
| `lg` | 720px | Complex content, tables |
| `fullscreen` | 100vw | Mobile, immersive experiences |

## Accessibility

- Focus is automatically trapped within dialog when open
- ESC key closes dialog
- Title is announced to screen readers
- Background content is marked as inert

## Examples

\`\`\`typescript
// Confirmation dialog
<Dialog.Root>
  <Dialog.Trigger asChild>
    <Button variant="outlined">Delete Item</Button>
  </Dialog.Trigger>
  <Dialog.Backdrop />
  <Dialog.Positioner>
    <Dialog.Content size="sm">
      <Dialog.Title>Delete Item?</Dialog.Title>
      <Dialog.Description>
        This action cannot be undone. The item will be permanently deleted.
      </Dialog.Description>
      <div style={{ display: 'flex', gap: '8px', justifyContent: 'flex-end', marginTop: '24px' }}>
        <Dialog.CloseTrigger asChild>
          <Button variant="text">Cancel</Button>
        </Dialog.CloseTrigger>
        <Button variant="filled">Delete</Button>
      </div>
    </Dialog.Content>
  </Dialog.Positioner>
</Dialog.Root>

// Controlled dialog
function ControlledDialog() {
  const [open, setOpen] = useState(false);
  
  return (
    <Dialog.Root open={open} onOpenChange={({ open }) => setOpen(open)}>
      <Dialog.Trigger asChild>
        <Button>Open</Button>
      </Dialog.Trigger>
      <Dialog.Backdrop />
      <Dialog.Positioner>
        <Dialog.Content>
          <Dialog.Title>Controlled Dialog</Dialog.Title>
          <Dialog.Description>State is managed externally.</Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Dialog.Root>
  );
}
\`\`\`

## DO NOT

\`\`\`typescript
// ❌ Don't omit required parts
<Dialog.Root>
  <Dialog.Content>  {/* Missing Backdrop, Positioner, Title */}
    Content here
  </Dialog.Content>
</Dialog.Root>

// ❌ Don't omit Title (accessibility violation)
<Dialog.Root>
  <Dialog.Backdrop />
  <Dialog.Positioner>
    <Dialog.Content>
      <Dialog.Description>No title!</Dialog.Description>
    </Dialog.Content>
  </Dialog.Positioner>
</Dialog.Root>

// ❌ Don't use native modal elements
<dialog open>
  <p>Native dialog</p>
</dialog>
\`\`\`
```

```markdown
<!-- guidelines/components/input.md -->
# Input

**Purpose:** Text input field with label, helper text, and validation.

## Import

\`\`\`typescript
import { Input } from '@discourser/design-system';
\`\`\`

## Variants

| Variant | Usage |
|---------|-------|
| `outlined` | Default, most use cases |
| `filled` | Alternative style, denser layouts |

## Sizes

| Size | Height | Usage |
|------|--------|-------|
| `sm` | 40px | Compact forms |
| `md` | 56px | Default |

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `variant` | `'outlined' \| 'filled'` | `'outlined'` | Visual style |
| `size` | `'sm' \| 'md'` | `'md'` | Input size |
| `label` | `string` | - | Field label |
| `helperText` | `string` | - | Helper text below input |
| `errorText` | `string` | - | Error message (shows error state) |
| `disabled` | `boolean` | `false` | Disable input |

## Examples

\`\`\`typescript
// Basic input with label
<Input label="Email" placeholder="you@example.com" />

// With helper text
<Input 
  label="Password" 
  type="password"
  helperText="Must be at least 8 characters"
/>

// Error state
<Input 
  label="Email" 
  errorText="Please enter a valid email address"
/>

// Disabled
<Input label="Username" disabled value="johndoe" />

// Controlled
const [email, setEmail] = useState('');
<Input 
  label="Email" 
  value={email}
  onChange={(e) => setEmail(e.target.value)}
/>
\`\`\`

## DO NOT

\`\`\`typescript
// ❌ Don't use native input without the component
<input type="text" />

// ❌ Don't omit label (accessibility)
<Input placeholder="Enter email" />  // Missing label!

// ❌ Don't override input styles
<Input label="Email" style={{ border: '2px solid red' }} />
\`\`\`
```

#### 7.6 Publish Package to npm

**Public Package:**
```bash
# Ensure logged into npm
npm login

# Publish
pnpm publish --access public
```

**Private Package (for Figma organization):**
1. In Figma Make, go to ⚙️ Make settings → Figma npm registry
2. Click "Get started" and enter organization scope (e.g., `@discourser`)
3. Click "Generate key" (requires org admin)
4. Add to `.npmrc`:
```
@discourser:registry=https://npm.figma.com/
//npm.figma.com/:_authToken=YOUR_TOKEN
```
5. Publish: `npm publish`

#### 7.7 Create Figma Make Template (Optional)

After publishing, create a Make template for your team:
1. Create new Figma Make file
2. Install package: "Install @discourser/design-system"
3. Add guidelines folder with all markdown files from 7.1-7.5
4. Publish as template for team use

**Validation:**
```bash
# In Figma Make file:
# 1. Install package
# 2. Ask: "Create a form with Button and Input components"
# 3. Verify Make uses design system components correctly
# 4. Verify semantic tokens are applied
# 5. Ask: "Create a confirmation dialog"
# 6. Verify Dialog compound pattern is used correctly
```

---

### Phase Summary

| Phase | Description | Status |
|-------|-------------|--------|
| 0 | Context Engineering (Skills, CLAUDE.md) | ✅ Complete |
| 1 | Fix Foundation (deps, config) | ✅ Complete |
| 2 | Architecture (Contract/Language/Transform) | ✅ Complete |
| 3 | M3 Token Integration | ✅ Complete |
| 4 | Complete Recipe Coverage + Testing | 🔄 In Progress |
| 5 | Ark UI Integration + A11y Testing | ⬜ Not Started |
| 6 | Build & Package (Figma Make compatible) | ⬜ Not Started |
| 7 | Figma Make Guidelines | ⬜ Not Started |

---

### Slash Commands Reference

These commands are available in Claude Code when working in this repository:

```bash
/fix-foundation          # Run Phase 1 tasks
/implement-architecture  # Run Phase 2 tasks
/new-component <name>    # Scaffold a new component with recipe + stories + tests
```

### Skills Reference

These skills auto-load when relevant:

| Skill | Triggers On |
|-------|-------------|
| `design-language` | Contract, language, transform work |
| `panda-recipes` | Recipe creation, variant patterns |
| `m3-tokens` | Color values, semantic tokens |
| `component-patterns` | Component creation, forwardRef, Ark UI |
