--- name: dev-storybook description: Comprehensive Storybook skill for Vue 3 - story creation, auditing, component discovery, visual testing, and CI integration. Merged from dev-storybook, storybook-audit, and storybook-master. --- # dev-storybook BUILD, AUDIT, and AUTOMATE Storybook stories for Vue 3 components with TypeScript. This comprehensive skill covers story creation, auditing existing stories for issues, component discovery and inventory, and CI/CD integration. Use when creating component documentation, fixing story compilation errors, auditing for display issues, or setting up automated testing workflows. ## Core Responsibilities 1. **Story Creation**: Write well-structured Storybook stories for Vue 3 components 2. **Error Resolution**: Fix TypeScript and Vue compilation errors in stories 3. **Styling Patterns**: Apply CSS correctly in Storybook without runtime template errors 4. **Component Props**: Ensure correct prop types and event handlers 5. **Story Auditing**: Detect and fix cutoff modals, store dependencies, design token violations 6. **Component Discovery**: Scan codebase for components and generate inventory reports 7. **Automated Testing**: Visual regression testing and accessibility compliance 8. **Story Streamlining**: Ensure stories match the actual app appearance exactly --- ## Story Streamlining (CRITICAL) **Trigger Keywords**: "streamline", "streamlined", "match app", "looks different", "visual fidelity" When user asks to **"streamline"** a Storybook story, they mean: **Make the story look EXACTLY like the component appears in the actual app.** ### What "Streamlined" Means in This Project **CRITICAL**: In FlowState, "streamlined" specifically means using **glass morphism** instead of solid black backgrounds. - ❌ **NOT streamlined**: Solid black/opaque backgrounds (`--glass-bg-solid`, `rgba(0,0,0,0.95)`) - ✅ **Streamlined**: Semi-transparent backgrounds with blur so the purple gradient shows through ```css /* Streamlined = Glass Morphism */ background: rgba(30, 32, 45, 0.75); backdrop-filter: blur(24px) saturate(180%); ``` **The app's signature look is glass panels floating over a purple/indigo gradient. If a component looks like a flat black box, it's NOT streamlined.** ### Streamlining Checklist When streamlining a story, verify ALL of the following: | Check | Requirement | How to Fix | |-------|-------------|------------| | **1. Use Actual Components** | Story imports and renders the REAL Vue component, not a mockup | Import from `@/components/...` | | **2. App Background** | Story background matches app's purple/indigo gradient | Use `background: var(--app-background-gradient)` | | **3. Glass Morphism** | Modals/overlays use semi-transparent bg with blur, NOT solid black | Use `rgba(30, 32, 45, 0.75)` + `backdrop-filter: blur(24px)` | | **4. Design Tokens** | All styling uses CSS variables, no hardcoded values | Replace `#hex` and `rgba()` with `var(--token)` | | **5. Correct Props** | Story passes the same props the component expects | Check `defineProps` in component | | **6. Event Handlers** | All emitted events have handlers | Add `@event="handler"` | | **7. Mock Data** | Data looks realistic, matches production patterns | Use actual Task/Project types | ### Streamlining Workflow ``` 1. IDENTIFY the component being streamlined └── Find the actual .vue component file └── Read its props, emits, and slots 2. COMPARE story vs app └── What does the story currently show? └── What does the actual app show? └── List the differences 3. FIX each difference: a. Background: Use var(--app-background-gradient) b. Components: Import actual components, not mockups c. Tokens: Replace hardcoded colors with CSS variables d. Props: Match component's defineProps interface e. Data: Use realistic mock data 4. VERIFY with user └── Ask user to check Storybook matches app ``` ### Background Color Reference **CRITICAL**: The app uses a purple/indigo gradient, NOT neutral gray. ```typescript // ❌ WRONG - neutral gray (doesn't match app) background: var(--surface-primary); background: hsl(0, 0%, 12%); // ✅ CORRECT - app's purple/indigo gradient background: var(--app-background-gradient); ``` The `--app-background-gradient` is defined in `design-tokens.css`: ```css --app-background-gradient: linear-gradient(135deg, hsl(220, 13%, 9%) 0%, hsl(240, 21%, 15%) 25%, hsl(250, 24%, 12%) 50%, hsl(260, 20%, 14%) 75%, hsl(220, 13%, 11%) 100%); ``` ### Example: Before/After Streamlining **Before (mockup, wrong background):** ```typescript export const Default: Story = { render: () => ({ template: `

Task Title

` }) } ``` **After (streamlined, matches app):** ```typescript import QuickSortCard from '@/components/QuickSortCard.vue' import { useTaskStore } from '@/stores/tasks' export const Default: Story = { render: () => ({ components: { QuickSortCard }, setup() { const mockTask = { id: '1', title: 'Real Task', priority: 'high', ... } return { mockTask } }, template: `
` }) } ``` ### Glass Morphism vs Solid Black (CRITICAL) **Problem**: Components using `--glass-bg-solid` or opaque black backgrounds look wrong because they block the app's purple gradient from showing through. **Solution**: Use semi-transparent backgrounds with blur so the gradient is visible through the glass effect. ```css /* ❌ WRONG - Solid black, no glass effect */ background: var(--glass-bg-solid); /* rgba(0, 0, 0, 0.95) */ background: rgba(0, 0, 0, 0.95); background: #121214; /* ✅ CORRECT - Semi-transparent with blur (glass morphism) */ background: rgba(30, 32, 45, 0.75); backdrop-filter: blur(24px) saturate(180%); -webkit-backdrop-filter: blur(24px) saturate(180%); border: 1px solid var(--glass-border-medium); ``` **Glass Morphism Recipe**: | Property | Value | Purpose | |----------|-------|---------| | `background` | `rgba(30, 32, 45, 0.75)` | Semi-transparent dark with slight purple tint | | `backdrop-filter` | `blur(24px) saturate(180%)` | Blur + color boost for depth | | `border` | `1px solid var(--glass-border-medium)` | Subtle edge definition | | `box-shadow` | `inset 0 1px 0 rgba(255, 255, 255, 0.1)` | Top highlight for polish | **When to use glass morphism**: - Modals and overlays - Command palettes - Dropdown menus - Floating panels - Any component that appears over the app background ### Common Streamlining Issues | Issue | Symptom | Fix | |-------|---------|-----| | **Mockup instead of component** | Story shows different UI than app | Import actual component | | **Wrong background color** | Black/gray instead of purple gradient | Use `var(--app-background-gradient)` | | **Solid black modal** | Modal looks flat, no depth | Use glass morphism with `rgba()` + `backdrop-filter` | | **Hardcoded colors** | Colors don't match design system | Use CSS variables | | **Missing components** | Card missing buttons/badges | Import child components | | **Wrong spacing** | Elements too cramped/spread | Use `var(--space-X)` tokens | ## Critical Rules ### Vue 3 Template Restrictions **NEVER use ` ` // ✅ CORRECT - Apply styles globally or use inline styles template: `
` ``` ### Component Prop Verification **ALWAYS verify component props before writing stories**: ```bash # Check component interface grep -A 5 "interface Props" src/components/MyComponent.vue grep -A 5 "defineProps" src/components/MyComponent.vue ``` Example fix: ```typescript // Component expects: { isOpen: boolean, taskIds: string[] } // ❌ WRONG story args args: { isVisible: true, // Wrong prop name selectedTasks: [] // Wrong prop name } // ✅ CORRECT story args args: { isOpen: true, taskIds: ['1', '2', '3'] } ``` ### Import Requirements **ALWAYS include required Vue imports**: ```typescript import type { Meta, StoryObj } from '@storybook/vue3' import { ref, reactive, computed } from 'vue' // Include all Vue APIs you use import MyComponent from '@/components/MyComponent.vue' ``` Common missing imports: - `ref` - For reactive state - `reactive` - For reactive objects - `computed` - For computed properties - `watch` - For watchers - `onMounted`, `onUnmounted` - For lifecycle hooks ## Story Structure Pattern ### Basic Story Template ```typescript import type { Meta, StoryObj } from '@storybook/vue3' import { ref } from 'vue' import MyComponent from '@/components/MyComponent.vue' const meta = { component: MyComponent, title: '📁 Category/MyComponent', tags: ['autodocs'], parameters: { layout: 'centered', // or 'fullscreen' docs: { description: { component: 'Component description here' } } } } satisfies Meta export default meta type Story = StoryObj export const Default: Story = { args: { // Component props here isOpen: true, title: 'Example' }, render: (args) => ({ components: { MyComponent }, setup() { const isOpen = ref(args.isOpen) const handleClose = () => { isOpen.value = false } return { isOpen, handleClose, args } }, template: ` ` }) } ``` ### Modal/Overlay Story Pattern ```typescript export const ModalExample: Story = { parameters: { layout: 'fullscreen' // Modal needs full screen }, args: { isOpen: true, title: 'Modal Title' }, render: (args) => ({ components: { MyModal }, setup() { const isOpen = ref(args.isOpen) return { isOpen, args, handleClose: () => { isOpen.value = false }, handleConfirm: () => { console.log('Confirmed') } } }, template: `
` }) } ``` ## Story Organization Standards ### CRITICAL: Consolidate, Don't Duplicate **Problem**: Too many similar stories create confusion and make components harder to understand. **Solution**: Use focused, well-documented stories with clear purpose. ### Recommended Story Structure (5-Story Pattern) Every component story file should follow this organization: #### 1. **Default Story** (Primary with interactive controls) ```typescript export const Default: Story = { args: { variant: 'default', hoverable: false, // ... all props with defaults }, render: (args) => ({ components: { MyComponent }, setup() { return { args } }, template: `

Default Component

Usage guidance explaining when/where to use this component.

` }) } ``` **Purpose**: Interactive playground for testing all prop combinations via Controls panel. #### 2. **Variants Story** (Consolidated comparison) ```typescript export const Variants: Story = { parameters: { docs: { description: { story: `**Visual variants for different contexts:** - **Variant A**: When to use this variant - **Variant B**: When to use this variant - **Variant C**: When to use this variant` } } }, render: () => ({ components: { MyComponent }, template: `

Variant A

Use when [specific context]

Variant B

Use when [specific context]

` }) } ``` **Purpose**: Show all visual variants **side by side** with usage guidance. **Anti-pattern**: Don't create separate stories for each variant (VariantA, VariantB, VariantC). Consolidate! #### 3. **Effects/States Story** (Realistic contexts) ```typescript export const Effects: Story = { parameters: { layout: 'fullscreen', docs: { description: { story: `**Effects for interaction and visual hierarchy:** - **Hoverable**: Use when components are clickable - **Glass**: Use on colorful/gradient backgrounds - **Elevated**: Use to emphasize important content` } } }, render: () => ({ components: { MyComponent }, template: `

Hoverable Component

Use when components are clickable. Hover to see the effect.

Content here

Glass Effect

Use on colorful or gradient backgrounds.

Glass effect shown on actual gradient!
` }) } ``` **Purpose**: Show effects/states in **realistic visual contexts** (e.g., glass on gradient, not plain background). **Critical**: Always show effects where they actually make sense visually. #### 4. **WithSlots/Structure Story** (Slot patterns) ```typescript export const WithSlots: Story = { parameters: { docs: { description: { story: `**Component supports slots for structured content:** - **Header**: For titles, actions, or badges - **Footer**: For metadata, timestamps, or action buttons - Use slots to create consistent, structured layouts` } } }, render: () => ({ components: { MyComponent }, template: `
Main content Main content Main content
` }) } ``` **Purpose**: Show slot usage patterns side by side. #### 5. **Real-World Example** (Production-ready) ```typescript export const TaskCardExample: Story = { parameters: { docs: { description: { story: 'A realistic example showing how to combine variants, effects, and slots for production use.' } } }, render: () => ({ components: { MyComponent }, template: `
` }) } ``` **Purpose**: Show production-ready example combining multiple features. ### Usage Guidance Requirements **EVERY story must include usage guidance**. Never show a variant without explaining when to use it. #### Component-Level Documentation ```typescript const meta = { component: MyComponent, title: '🧩 Components/🔘 Base/MyComponent', tags: ['autodocs'], parameters: { layout: 'centered', docs: { description: { component: `Component description explaining its purpose. **When to use:** - Specific use case 1 - Specific use case 2 - Specific use case 3` } } }, argTypes: { variant: { control: 'select', options: ['default', 'outlined', 'filled'], description: 'Visual style variant', table: { type: { summary: 'string' }, defaultValue: { summary: 'default' } } } } } ``` #### Story-Level Documentation ```typescript export const Variants: Story = { parameters: { docs: { description: { story: `**Visual variants for different contexts:** - **Default**: Standard style (use for X) - **Outlined**: Transparent background (use for Y) - **Filled**: Solid background (use for Z)` } } } } ``` ### Visual Context Best Practices #### ✅ GOOD: Show effects in realistic contexts ```typescript // Glass effect on gradient
Now the glass effect actually makes sense!
// Elevated card on page
Extra shadow creates depth on the page
``` #### ❌ BAD: Show effects on plain backgrounds ```typescript // Glass effect on plain background (can't see blur!) Glass effect is invisible here // Elevated without context (shadow purpose unclear) Why is this elevated? ``` ### ArgTypes Standards Always include comprehensive argTypes with descriptions: ```typescript argTypes: { variant: { control: 'select', options: ['default', 'outlined', 'filled'], description: 'Visual style variant', table: { type: { summary: 'string' }, defaultValue: { summary: 'default' } } }, hoverable: { control: 'boolean', description: 'Add hover effects (elevation & transform)', table: { type: { summary: 'boolean' }, defaultValue: { summary: 'false' } } } } ``` ### Story Consolidation Checklist Before creating multiple similar stories, ask: - [ ] Can these variants be shown side by side in one story? - [ ] Does each story explain WHEN to use it? - [ ] Are effects shown in realistic visual contexts? - [ ] Is the Default story using args for interactivity? - [ ] Does the documentation include "When to use" guidance? **Target**: 5-7 focused stories per component, NOT 12+ redundant stories. ## CSS Styling Patterns ### Option 1: Inline Styles (Preferred for simple cases) ```typescript template: `
` ``` ### Option 2: CSS Variables (Use design tokens) ```typescript template: `
` ``` ### Option 3: Global Styling (For component-wide changes) Modify the component's scoped styles directly in the `.vue` file instead of trying to override in stories. ## Common Errors and Fixes ### Error: "Tags with side effect (