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;