04 — Storybook-First Development
Dev Guide — Savoy Signature Hotels
PRD refs:05_Design_System_and_Theming.md,07_Modules_and_Templates.md,18_QA_Pipeline_and_Testing.md
1. Purpose
Section titled “1. Purpose”Storybook is the primary development environment for the Savoy platform. Every UI component and CMS module is developed, tested, and validated in Storybook before integration with the Next.js application.
2. Why Storybook-First
Section titled “2. Why Storybook-First”| Benefit | Explanation |
|---|---|
| Isolation | Develop components without CMS data, routing, or server dependencies |
| Multi-theme validation | Instantly switch between 8 hotel themes |
| Responsive testing | Viewport controls for mobile/tablet/desktop |
| Visual regression | Chromatic/Percy runs against stories, not the full app |
| Design review | Designers compare Figma link directly in Storybook |
| AI development | Claude Code can validate components visually via screenshots |
3. Storybook Architecture
Section titled “3. Storybook Architecture”apps/storybook/ .storybook/ main.ts # Config: references packages/ui + packages/modules preview.ts # Global decorators + theme setup theme-decorator.tsx # Theme switcher for toolbar package.json tsconfig.jsonStories live alongside their components:
packages/modules/src/m05-hero-slider/HeroSlider.stories.tsxpackages/ui/src/Button/Button.stories.tsx4. Story Writing Convention
Section titled “4. Story Writing Convention”4.1 Meta Configuration
Section titled “4.1 Meta Configuration”import type { Meta, StoryObj } from '@storybook/react';import { ComponentName } from './ComponentName';
const meta: Meta<typeof ComponentName> = { title: 'Category/ID — Component Name', // e.g., 'Modules/M08 — Card Grid' component: ComponentName, tags: ['autodocs'], parameters: { layout: 'fullscreen', // 'fullscreen' for modules, 'centered' for UI design: { type: 'figma', url: 'https://www.figma.com/file/...', // REQUIRED: link to Figma design }, },};
export default meta;type Story = StoryObj<typeof ComponentName>;4.2 Title Convention
Section titled “4.2 Title Convention”| Component Type | Title Pattern | Example |
|---|---|---|
| UI Component | UI/{ComponentName} | UI/Button |
| Module | Modules/{ID} — {Name} | Modules/M05 — Hero Slider |
| Widget | Widgets/{Name} | Widgets/BookingBar |
| Template | Templates/{Name} | Templates/RoomDetail |
4.3 Required Story Variants
Section titled “4.3 Required Story Variants”Every module must include stories for:
| Variant | Purpose |
|---|---|
Default | Standard rendering with typical content |
WithOptionalProps | All optional props filled |
MinimalContent | Minimum viable content (edge case) |
LongContent | Overflow / text wrapping test |
EmptyState | What happens with empty arrays or no content |
4.4 Mock Data Quality
Section titled “4.4 Mock Data Quality”Use realistic mock data that matches what the CMS provides:
// GOOD: realistic hotel contentconst mockSlide = { imageDesktop: { url: '/storybook/hero-pool-desktop.jpg', width: 1920, height: 1080 }, imageMobile: { url: '/storybook/hero-pool-mobile.jpg', width: 750, height: 1000 }, altText: 'Infinity pool overlooking the Atlantic Ocean', caption: 'Infinity Pool at Savoy Palace',};
// BAD: useless placeholderconst mockSlide = { imageDesktop: { url: '/test.jpg', width: 100, height: 100 }, imageMobile: { url: '/test.jpg', width: 100, height: 100 }, altText: 'test',};5. Theme Decorator
Section titled “5. Theme Decorator”The global theme decorator allows switching between all 8 themes:
import type { Preview } from '@storybook/react';import { ThemeDecorator } from './theme-decorator';
const preview: Preview = { decorators: [ThemeDecorator], globalTypes: { theme: { description: 'Hotel theme', toolbar: { title: 'Theme', icon: 'paintbrush', items: [ { value: 'savoy-palace', title: 'Savoy Palace' }, { value: 'royal-savoy', title: 'Royal Savoy' }, { value: 'saccharum', title: 'Saccharum' }, { value: 'the-reserve', title: 'The Reserve' }, { value: 'calheta-beach', title: 'Calheta Beach' }, { value: 'gardens', title: 'Gardens' }, { value: 'hotel-next', title: 'Hotel Next' }, { value: 'savoy-signature', title: 'Savoy Signature (Group)' }, ], dynamicTitle: true, }, }, },};
export default preview;import React from 'react';import '@savoy/themes/src/index.css';
export const ThemeDecorator = (Story: any, context: any) => { const theme = context.globals.theme || 'savoy-palace'; return ( <div data-theme={theme}> <Story /> </div> );};6. Viewport Testing
Section titled “6. Viewport Testing”Configure standard viewports:
// preview.ts parametersparameters: { viewport: { viewports: { mobile: { name: 'Mobile', styles: { width: '375px', height: '812px' } }, tablet: { name: 'Tablet', styles: { width: '768px', height: '1024px' } }, desktop: { name: 'Desktop', styles: { width: '1440px', height: '900px' } }, }, },},Minimum viewport validation per module: Mobile (375px), Tablet (768px), Desktop (1440px)
7. Storybook Addons
Section titled “7. Storybook Addons”| Addon | Purpose |
|---|---|
@storybook/addon-essentials | Controls, actions, viewport, docs |
@storybook/addon-a11y | Accessibility panel (axe-core in-browser) |
storybook-addon-designs | Embed Figma frames for side-by-side comparison |
@storybook/test | Interaction testing within stories |
| Pixel Perfect (custom) | Visual regression + Figma fidelity testing panel |
The a11y addon runs axe-core on every story. Zero violations allowed before merging.
The Pixel Perfect addon is a custom panel that validates rendered stories against Figma baselines (initial development) and regression snapshots (ongoing). Every module MUST pass visual tests before merging. See section 11 below.
8. Development Workflow
Section titled “8. Development Workflow”For Claude Code:
Section titled “For Claude Code:”- Read the Figma spec (from PRD or story’s
design.urlparameter) - Create the story file with realistic mock data
- Implement the component
- Validate: does Storybook rendering match Figma?
- Switch themes: verify the component adapts correctly
- Check a11y panel: fix any violations
- Run Pixel Perfect visual tests (
pnpm --filter storybook visual:test) - Iterate until diff % is within threshold (view diffs in Pixel Perfect panel)
- Promote regression baselines when Figma validation passes
For Human Developers:
Section titled “For Human Developers:”- Run
pnpm storybookfrom root - Navigate to the component in the sidebar
- Use Controls panel to test prop combinations
- Use Theme toolbar to cycle through themes
- Use Viewport toolbar for responsive behavior
- Check the a11y panel for violations
- Click the Figma link in Design tab to compare
- Open Pixel Perfect tab to see visual test results and diff overlays
- Run
pnpm --filter storybook visual:testto capture and compare screenshots
9. Placeholder Images
Section titled “9. Placeholder Images”Place in apps/storybook/public/storybook/:
hero-pool-desktop.jpg (1920x1080)hero-pool-mobile.jpg (750x1000)room-desktop.jpg (800x600)room-mobile.jpg (400x300)restaurant-desktop.jpg (800x600)restaurant-mobile.jpg (400x300)gallery-01-desktop.jpg (1200x800)gallery-01-mobile.jpg (600x400)11. Pixel Perfect Visual Testing (MANDATORY)
Section titled “11. Pixel Perfect Visual Testing (MANDATORY)”Every module MUST pass Pixel Perfect visual tests. This is an absolute rule — no module is merged without visual validation.
Initial Development (Figma Fidelity)
Section titled “Initial Development (Figma Fidelity)”When building a new module, compare the rendered story against the Figma design:
- Fetch Figma baselines — Use Figma MCP
get_screenshotto capture desktop (1440x900) and mobile (375x812) screenshots of the design - Save baselines —
apps/storybook/__figma_baselines__/{storyId}/desktop-1440x900.pngandmobile-375x812.png - Register mapping — Add story ID → Figma node in
.storybook/visual-testing/figma-mapping.ts - Run tests —
pnpm --filter storybook visual:test(Storybook must be running on port 6006) - Review — Open Pixel Perfect panel, view diff overlay (red pixels = differences)
- Iterate — Adjust CSS, re-run tests until diff is within threshold (default: 0.5%)
Ongoing Development (Regression Testing)
Section titled “Ongoing Development (Regression Testing)”After initial approval, subsequent changes are tested against regression baselines:
- Run tests —
pnpm --filter storybook visual:test - If failing — Open Pixel Perfect panel, compare current screenshot with baseline
- Intentional change — Update baseline via panel (“Baseline” button) or
pnpm --filter storybook visual:update - Unintentional change — Fix the regression before merging
Pixel Perfect Panel
Section titled “Pixel Perfect Panel”The panel shows:
- KPI bar: Pass Rate, Passed, Failed, Figma count, Regression count, Average Diff %
- Results table: story name, viewport, comparison type, diff %, commit hash
- Actions: View screenshot, view Figma golden, view diff overlay, promote baseline, copy CLI command
File Locations
Section titled “File Locations”| Directory | Purpose | Git tracked |
|---|---|---|
__figma_baselines__/ | Figma golden images | Yes |
__visual_snapshots__/ | Regression baselines | Yes |
__visual_diffs__/ | Diff overlays | No |
Commands
Section titled “Commands”pnpm --filter storybook visual:test # Run all visual testspnpm --filter storybook visual:test -- --story {id} # Test specific storypnpm --filter storybook visual:update # Update all baselinespnpm --filter storybook visual:status # Check baseline status12. Storybook Deployment
Section titled “12. Storybook Deployment”Storybook is deployed to Cloudflare Pages (not Azure). The CI/CD pipeline builds Storybook in the CI stage and deploys via wrangler pages deploy in the CD stage.
| Environment | URL | Hosting | Trigger |
|---|---|---|---|
| DEV | savoy-dev-storybook.wycreative.com | Cloudflare Pages (savoy-storybook-dev) | PR merge to deploy/dev |
| STAGE | savoy-stage-storybook.wycreative.com | Cloudflare Pages | PR merge to deploy/stage |
Storybook is NOT deployed to QA or PROD — internal development tool only.
Auth gate: DEV/STAGE Storybook is protected by the Dev Auth Gate. A Cloudflare Pages Function (apps/storybook/functions/_middleware.js) checks the __dev_gate cookie before serving content. Users must log in on the Next.js site first — the cookie is shared across *.wycreative.com.