Skip to main content

Frontend UI Workflow Reference

Shared reference for frontend UI skills. Loaded on demand via inline > **Reference**: pointers in SKILL.md.


CSF3 Story Format Spec

Standard Component Story Format v3 pattern. Used by frontend-story-generate, frontend-scaffold, and frontend-design.

Base Template

import type { Meta, StoryObj } from '@storybook/react';
import { {ComponentName} } from './{ComponentName}';

const meta: Meta<typeof {ComponentName}> = {
component: {ComponentName},
tags: ['autodocs'],
argTypes: {
// Map props to controls
},
};

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

export const Default: Story = {
args: {
// Default prop values
},
};

Rules

  • Use declarative args objects, not render functions
  • Export meta as default (CSF3 requirement)
  • Type stories with StoryObj<typeof meta> (not StoryObj<typeof Component>)
  • Use includeStories / excludeStories to filter data exports from story list
  • Add tags: ['autodocs'] for automatic documentation
  • One story per meaningful state (Default, Loading, Error, Empty, each variant)

CVA Variant Detection

When a component uses class-variance-authority (CVA), generate one story per variant combination.

Detection patterns (grep in component source):

PatternIndicates
cva(CVA base call
variants:Variant definitions
defaultVariants:Default variant values
compoundVariants:Compound variant rules

Story generation for CVA:

  1. Extract variant keys from variants: block
  2. Generate Default story with defaultVariants values
  3. Generate one story per variant value (e.g., Destructive, Outline, Ghost)
  4. Generate compound variant stories if compoundVariants exist

argTypes Controls

Map TypeScript prop types to Storybook controls:

Prop TypeControl
string{ control: 'text' }
boolean{ control: 'boolean' }
number{ control: 'number' }
enum / union{ control: 'select', options: [...] }
ReactNode{ control: 'text' }
() => void{ action: 'clicked' }

Component State Coverage Matrix

Every generated story set must cover these states. Mark N/A if the component does not support a state.

StateRequiredDescription
DefaultYesComponent with default props
LoadingIf asyncSkeleton or spinner state
ErrorIf fallibleError message or boundary
EmptyIf data-drivenNo-data / zero state
DisabledIf interactiveDisabled form elements
Each variantIf CVA/variantsOne story per variant value
Edge: long textIf text contentOverflow / truncation behavior
Edge: RTLIf i18nRight-to-left layout

MCP Graceful Degradation Patterns

All frontend UI skills must work without MCP servers. MCP enhances, never gates.

Pattern: Try MCP, Fallback to Source

IF MCP tool available:
→ Use MCP tool for richer context
ELSE:
→ Read source files directly (Glob + Grep + Read)
→ Log: "MCP server not available — using source file analysis"

Storybook MCP

MCP ToolPurposeFallback
mcp__storybook-mcp__get-ui-building-instructionsCSF conventions for projectRead .storybook/main.ts + existing *.stories.tsx
mcp__storybook-mcp__list-all-componentsComponent inventoryGlob: src/**/*.tsx minus stories/tests
mcp__storybook-mcp__get-component-documentationComponent docsRead component source + JSDoc
mcp__storybook-mcp__get-story-urlsStory URLs for verificationConstruct from localhost:6006/?path=/story/{id}

shadcn MCP

MCP ToolPurposeFallback
mcp__shadcn__* (registry tools)Real prop types from registryRead component source files in components/ui/

shadcn MCP command: npx shadcn@latest mcp

Playwright MCP

MCP ToolPurposeFallback
mcp__playwright__browser_snapshotAccessibility tree (primary, fast, deterministic)Static code analysis only (static-mode)
mcp__playwright__browser_take_screenshotVisual capture (secondary)No visual capture in static-mode
mcp__playwright__browser_navigateNavigate to story URLN/A in static-mode
mcp__playwright__browser_tabsManage browser tabsN/A in static-mode

Playwright MCP command: npx @playwright/mcp@latest Config file: npx @playwright/mcp@latest --config path/to/config.json


Visual Scoring Rubric

Two output modes based on Playwright MCP availability.

visual-mode (Playwright available)

Score 0-10 based on accessibility tree + visual capture.

ScoreMeaningCriteria
9-10ExcellentAll states render correctly, responsive, accessible, no visual regressions
7-8GoodMinor issues (spacing, alignment) that do not affect usability
5-6AcceptableFunctional but noticeable visual issues
3-4PoorSignificant visual problems affecting usability
1-2BrokenComponent fails to render or is unusable
0CriticalComponent crashes or blocks interaction

static-mode (No Playwright)

  • Visual score: N/A
  • Report header: "Static analysis only — visual verification requires Playwright MCP."
  • Blocks visual pass/fail conclusions
  • Can still assess: code structure, prop types, accessibility attributes, CSS classes

Playwright Network Policy

Default: localhost-only. New network capabilities require explicit user consent.

{
"browser": {
"launchOptions": { "headless": true }
},
"network": {
"allowedOrigins": ["http://localhost:*", "http://127.0.0.1:*"],
"blockedOrigins": ["*"]
}
}

URL Validation Rules

URL PatternAction
localhost:*Allow (default)
127.0.0.1:*Allow (default)
Any external domainRequire explicit per-URL user confirmation

Skills must validate URLs before passing to browser_navigate.


dev-output-integrate Readme Format

Every integratable skill must emit {id}-{slug}-readme.md for dev-output-integrate compatibility.

Required Format

# {Skill Name} Output

## Integration Map

| Source | Destination |
|--------|-------------|
| `{id}-{slug}-stories.tsx` | `src/components/{component}/{Component}.stories.tsx` |
| `{id}-{slug}-patch-{name}.tsx` | `src/components/{component}/{Component}.tsx` |

## Notes

- {Any special integration instructions}

dev-output-integrate parses Source/Destination tables and cp commands to map output files to project locations.


Storybook Detection

Pattern for detecting Storybook in a project (used by multiple skills).

CheckMethodIndicates
.storybook/ directoryGlob: .storybook/main.*Storybook installed
storybook in devDependenciesGrep: "storybook" package.jsonStorybook as dependency
*.stories.tsx filesGlob: src/**/*.stories.tsxExisting stories
@storybook/addon-mcpGrep: "addon-mcp" .storybook/main.*MCP addon configured
experimentalRSC: trueGrep: "experimentalRSC" .storybook/main.*RSC support enabled
components.jsonGlob: components.jsonshadcn/ui configured

MSW + Storybook Integration

Prerequisites

  • msw (v2.x) and msw-storybook-addon in devDependencies
  • .storybook/preview.ts initialized with initialize() and mswLoader

Story pattern for API-dependent components

Each API-dependent component gets 4 stories with MSW handlers:

StoryHandler BehaviorTests
DefaultReturns spec-conformant success responseHappy path rendering
Loadingdelay('infinite')Loading/skeleton states
ErrorReturns RFC 9457 error (status 500)Error boundary/fallback
EmptyReturns empty collection { data: [] }Empty state UI

Handler format in stories

export const Default: Story = {
parameters: {
msw: {
handlers: [
http.get('/api/endpoint', () => {
return HttpResponse.json({ data: [...] });
}),
],
},
},
};

Storybook preview setup

// .storybook/preview.ts
import { initialize, mswLoader } from 'msw-storybook-addon';
initialize({ onUnhandledRequest: 'warn' });
const preview: Preview = { loaders: [mswLoader] };
export default preview;