Implementing Storybook in a MoonRepo Monorepo with Tailwind v4

storybookmonorepotailwindcomponentstesting

Component-driven development has become the standard for modern web applications, and Storybook stands as the industry-leading tool for building, documenting, and testing UI components in isolation. Integrating Storybook into a MoonRepo monorepo with Tailwind CSS v4 presents unique challenges, but when configured correctly, creates a powerful development environment that scales across multiple frameworks.

Why Storybook in a Monorepo?

In a monorepo architecture, you typically have a shared component library (like @workspace/ui) consumed by multiple applications. Without Storybook, developers must spin up entire applications to view component changes, navigate through multiple pages, and contort UI states to test edge cases. This workflow is slow and breaks developer focus.

Storybook solves this by providing an isolated development environment where components can be developed, tested, and documented independently. Every component variation becomes a "story" that can be viewed instantly, tested with different props, and verified across light and dark modes—all without running a full application.

The Tailwind v4 Challenge

Tailwind CSS v4's CSS-first configuration approach introduces complexity when setting up Storybook. Unlike traditional JavaScript configs, Tailwind v4 uses @import, @source, and @plugin directives directly in CSS. This creates three critical requirements for Storybook integration:

  1. PostCSS Configuration: Storybook must use the same PostCSS setup as your apps
  2. CSS Import Strategy: Workspace aliases work differently in Vite (Storybook's builder)
  3. Source Directives: Tailwind needs to know which files to scan for utility classes

The Three-Step Pattern

After analyzing how working apps in the monorepo handle Tailwind, a clear pattern emerges. This same pattern must be replicated in Storybook:

Step 1: Import Shared PostCSS Config

Instead of creating a custom PostCSS configuration, import the shared config from your Tailwind package:

// packages/storybook/postcss.config.mjs
export { default } from "@workspace/tailwind/postcss.config";

This ensures consistency across all packages and eliminates configuration drift.

Step 2: Create Local CSS with Source Directives

Create a local styles/globals.css file that imports from the shared package and defines source directives:

@import "@workspace/tailwind/globals.css";
@plugin "@tailwindcss/typography";

@source "../../ui/src/**/*.{ts,tsx}";
@source "../stories/**/*.{ts,tsx,mdx}";
@source "../.storybook/**/*.{ts,tsx}";

The @source directives are critical—they tell Tailwind which files to scan for utility classes. Without them, Tailwind won't generate styles for your Storybook components.

Step 3: Import Local CSS in Preview

Import your local CSS file (not the workspace one directly) in Storybook's preview configuration:

// .storybook/preview.tsx
import "../styles/globals.css";

This pattern works because CSS @import directives (processed by PostCSS) support workspace aliases, while JavaScript imports in Vite don't reliably resolve workspace CSS paths.

MoonRepo Integration

MoonRepo's task orchestration automatically handles Storybook's dependencies:

# packages/storybook/moon.yml
tasks:
  dev:
    command: 'storybook dev -p 6006'
    local: true
    inputs:
      - '@globs(stories/**/*.{ts,tsx})'
      - '@in(group:ui)'

The @in(group:ui) dependency means Storybook automatically rebuilds when UI components change. MoonRepo's caching ensures subsequent builds are fast, while its dependency graph guarantees components are available before Storybook starts.

Story Organization

Organizing stories mirrors your component architecture:

stories/
├── foundations/    # Colors, Typography, Spacing
├── components/     # Button, Card, Input, etc.
└── patterns/       # Navbar, Forms, Layouts

This three-tier hierarchy makes components discoverable and creates clear documentation for both developers and designers.

Benefits Realized

With Storybook properly integrated, several immediate benefits emerge:

Faster Development: Components can be developed in isolation without spinning up entire applications. Changes are visible instantly with hot module replacement.

Visual Documentation: Every component variant becomes self-documenting. New team members can browse components, see all variants, and copy implementation examples.

Accessibility Testing: Built-in accessibility audits run on every story, catching issues before they reach production.

Dark Mode Verification: Toggle between light and dark themes with a single click, ensuring consistent theming.

Visual Regression Testing: Integration with tools like Chromatic enables automated screenshot comparisons, catching unintended visual changes.

Common Pitfalls

The most common mistake is trying to import CSS directly from the workspace package:

// ❌ Wrong - doesn't work in Vite
import "@workspace/tailwind/globals.css";

// ✅ Correct - import local CSS with @source directives
import "../styles/globals.css";

Another common issue is forgetting to restart Storybook's dev server after changing CSS files. Unlike component changes, CSS modifications often require a full restart to take effect.

Conclusion

Implementing Storybook in a MoonRepo monorepo with Tailwind v4 requires understanding the relationship between PostCSS processing, CSS imports, and Tailwind's source directives. By following the three-step pattern used by working apps—importing shared PostCSS config, creating local CSS with source directives, and importing that local CSS in preview configuration—you create a robust development environment that scales across your entire component library.

The result is a single source of truth for UI components, accessible at localhost:6006, where developers and designers can collaborate effectively. Combined with MoonRepo's intelligent caching and dependency management, Storybook becomes not just a documentation tool, but a performance-enhancing part of your development workflow.