Tailwind Integration Handoff Document
Date: October 11, 2025
Branch: feat/tailwind-integration
Session Summary: Complete Tailwind v4 integration with ecosystem libraries, generic and division-specific component packages, two working web apps, and comprehensive documentation.
Version notice (2025-10-13): The active workspace standard is Tailwind CSS v3.4.10. Any mentions of Tailwind v4 in this historical document (e.g.,
@tailwindcss/postcss,@import "tailwindcss") should be ignored in favor of v3 patterns: CommonJStailwind.config.js,darkMode: 'class',content: { relative: true, files: [...] }, PostCSS{ tailwindcss: {}, autoprefixer: {} }, and CSS using@tailwind base; @tailwind components; @tailwind utilities;. Seedocs/tailwind-parity-checklist.md.
Chapter 1: Current Status & Git History
Section titled “Chapter 1: Current Status & Git History”Overview
Section titled “Overview”Successfully integrated Tailwind CSS v4 with a full ecosystem of supporting libraries. The large monolithic commit was refactored into 7 logical, focused commits for better reviewability.
Git Commit History (Most Recent First)
Section titled “Git Commit History (Most Recent First)”Commit 7: 4c44a102 - Policy Enforcement
Section titled “Commit 7: 4c44a102 - Policy Enforcement”feat(tooling): add app package policy enforcement- Created
scripts/check-app-package-policies.mjs - Integrated into
scripts/verify-repo.shas AppPackagePolicy stage - Enforces centralized dependency management (no app-level deps/devDeps)
- Files: 2 changed, 55 insertions
Commit 6: fb3e2631 - Documentation
Section titled “Commit 6: fb3e2631 - Documentation”docs(tailwind): add comprehensive Tailwind v4 integration documentationdocs/tailwind-ecosystem-guide.md- Complete library usage guidedocs/TAILWIND_ECOSYSTEM_SUMMARY.md- Implementation summarydocs/tailwind-integration-guide.md- Step-by-step integrationdocs/design-token-system.md- Design token architecturedocs/figma-tokens-studio-setup.md- Figma integrationdocs/onboarding_guide.md- Updated with Web (Vite) specificsdocs/ANALYSIS_REPORT_2025-10-11.md- Technical analysis- Files: 7 changed, 2,452 insertions
Commit 5: 0c6d4924 - Bonjour Locker Integration
Section titled “Commit 5: 0c6d4924 - Bonjour Locker Integration”feat(bonjour-locker-web): integrate Tailwind v4 with generic Hero and rotating taglines- PostCSS + Tailwind v4 configuration
- Vite resolve aliases for React Native safeguards
- Uses
@cloudalt-frontend/ui-web(generic cross-division) - Custom
useRotatingTaglinehook with 5 privacy-focused taglines - Hero image:
shared/assets/images/hero/hero-image.jpeg - Button showcase with all variants
- Running at: http://localhost:4202
- Files: 15 changed, 250 insertions
Commit 4: dab20284 - PinkGuest Integration
Section titled “Commit 4: dab20284 - PinkGuest Integration”feat(pinkguest-web): integrate Tailwind v4 with Hero component- PostCSS + Tailwind v4 configuration
- Extends
packages/config/tailwind-preset.js - Vite resolve aliases (react-native → react-native-web)
- optimizeDeps.exclude: [‘react-native’]
- Uses
@cloudalt-frontend/ui-stay-match(division-specific) - Shared assets directory structure created
- Cleaned package.json per policy
- Running at: http://localhost:4200
- Files: 22 changed, 679 insertions
Commit 3: 0edb5288 - Division-Specific UI Package
Section titled “Commit 3: 0edb5288 - Division-Specific UI Package”feat(ui-stay-match): create Stay Match division-specific web components- Created
packages/ui-stay-match/ - Hero with default gay couple beach image
- Button with Stay Match brand styling
- StayMatchNavigation component
- TypeScript types and asset declarations
- Purpose: Division-specific variants with customized defaults
- Files: 15 changed, 743 insertions
Commit 2: 1c683a59 - Generic UI Package
Section titled “Commit 2: 1c683a59 - Generic UI Package”feat(ui-web): create generic cross-division web component library- Created
packages/ui-web/ - Hero component with tailwind-merge for className merging
- Button component with tailwind-variants (4 colors, 3 sizes, loading, disabled)
- TypeScript types for component props
- Added
@cloudalt-frontend/ui-webpath alias to tsconfig.base.json - Purpose: Generic components for prototyping and shared use across all divisions
- Files: 12 changed, 607 insertions
Commit 1: 247225de - Core Infrastructure
Section titled “Commit 1: 247225de - Core Infrastructure”feat(tailwind): add Tailwind v4 ecosystem dependencies and shared configuration- Tailwind CSS v4.1.14 with
@tailwindcss/postcssplugin - Ecosystem libraries:
@headlessui/react(latest) - Unstyled, accessible UI componentstailwind-merge(latest) - Intelligent className mergingclsx(latest) - Conditional class constructiontailwind-variants(3.1.1) - Type-safe variant system
- Shared preset:
packages/config/tailwind-preset.js - Theme package updates: brands.ts, tokens.ts, types.ts
- Files: 8 changed, 1,486 insertions
What’s Working ✅
Section titled “What’s Working ✅”-
PinkGuest Web App (localhost:4200)
- Hero component rendering with default Stay Match beach image
- Navigation with mobile toggle
- Email signup form
- Button showcase with all variants
- Tailwind v4 styles applied correctly
- Zero bundling issues
-
Bonjour Locker Web App (localhost:4202)
- Hero component with generic ui-web package
- Rotating taglines (5 privacy-focused options)
- Hero image from app-specific shared/assets
- Button showcase demonstrating all variants
- Clean separation from division-specific code
-
Build System
- Vite HMR working perfectly
- React Native code excluded from web bundles
- No type errors
- Policy enforcement active
Recent Fix (Not Yet Committed)
Section titled “Recent Fix (Not Yet Committed)”Issue: PinkGuest Hero component not displaying
Root Cause: ui-stay-match Hero had default image file but wasn’t using it as fallback
Solution Applied:
- Added import:
import defaultHeroImage from './hero-13-gay-couple-visiting-beach.jpeg'; - Updated backgroundUrl logic to use default when no prop provided
- Asset type declarations already in place
Files Modified:
packages/ui-stay-match/src/components/Hero/Hero.tsx
Status: Working in dev server via HMR, needs to be committed
Pending Tasks
Section titled “Pending Tasks”-
Commit Recent Fix
- Stage and commit the ui-stay-match Hero default image fix
- Should be commit 8 in the sequence
-
Production Build Verification
- Run
yarn nx build pinkguest-web - Run
yarn nx build bonjour_locker-web - Verify no React Native code in bundles
- Check bundle sizes reasonable
- Confirm TypeScript compilation succeeds
- Run
-
Push to Remote
git push origin feat/tailwind-integration- All 8 commits (7 existing + 1 new fix)
-
Create Pull Request
- Reference commit messages
- Highlight architecture decisions
- Note breaking changes (new package structure)
-
Replicate to Other Brands
- OrangeGuest web app
- PurpleGuest web app
- RainbowHost web app
- Other Stay Match division brands
-
🆕 Storybook Integration (Next Major Milestone - See Chapter 2)
- Initialize Storybook package with React-Vite
- Configure Tailwind v4 + PostCSS
- Add Vite resolve aliases
- Write component stories (Hero, Button)
- Estimated Time: 2-3 hours
File Locations Reference
Section titled “File Locations Reference”Generic Components (ui-web):
packages/ui-web/src/components/Hero/Hero.tsxpackages/ui-web/src/components/Button/Button.tsxpackages/ui-web/src/types/index.ts
Division-Specific Components (ui-stay-match):
packages/ui-stay-match/src/components/Hero/Hero.tsxpackages/ui-stay-match/src/components/Button/Button.tsxpackages/ui-stay-match/src/components/Hero/hero-13-gay-couple-visiting-beach.jpeg
Web Apps:
apps/stay_match/pinkguest/web/- Port 4200, uses ui-stay-matchapps/bonjour_services/bonjour_locker/web/- Port 4202, uses ui-web
Configuration:
packages/config/tailwind-preset.js- Shared Tailwind presetpackages/theme/src/- Design tokens and brand definitions
Documentation:
docs/tailwind-ecosystem-guide.md- Main integration guidedocs/onboarding_guide.md- Developer onboarding (updated)docs/TAILWIND_ECOSYSTEM_SUMMARY.md- Implementation summary
Scripts:
scripts/check-app-package-policies.mjs- Policy enforcementscripts/verify-repo.sh- CI/verification pipeline
Chapter 2: Storybook Integration Plan
Section titled “Chapter 2: Storybook Integration Plan”Why Storybook is Critical Now
Section titled “Why Storybook is Critical Now”With Tailwind components established, Storybook becomes essential for:
- Component Documentation - Living examples of Hero, Button, and future components
- Variant Testing - Visualize all Button colors/sizes, Hero variants in isolation
- Design System Management - Maintain consistency across divisions
- Developer Experience - Develop components without running full apps
- Cross-Division Visibility - Teams see ui-web vs ui-stay-match differences
- Preventing Chaos - Without a component manager, we’ll quickly lose control
Current Storybook State
Section titled “Current Storybook State”Installed Dependencies (already in root package.json):
@nx/storybook: 20.8.0@storybook/addon-essentials: ^8.6.14@storybook/addon-interactions: ^8.6.14@storybook/addon-onboarding: ^8.6.14@storybook/blocks: ^8.6.14@storybook/react: ^8.6.14@storybook/react-vite: ^8.6.14@storybook/test: ^8.6.14storybook: ^8.6.14
Existing Package: packages/storybook/ (minimal, only package.json)
Recommended Architecture
Section titled “Recommended Architecture”Option A: Single Monorepo Storybook (RECOMMENDED)
Section titled “Option A: Single Monorepo Storybook (RECOMMENDED)”packages/storybook/├── .storybook/│ ├── main.ts # Stories from all ui-* packages│ ├── preview.ts # Tailwind CSS, global decorators│ └── manager.ts # Storybook UI customization├── stories/│ ├── Introduction.mdx # Architecture guide│ └── Tailwind.mdx # Tailwind usage guide└── project.json # Nx targets (storybook, build-storybook)Pros:
- Single source of truth for all components
- Easy to compare ui-web vs ui-stay-match
- Shared Tailwind configuration
- One dev server to manage
Cons:
- Slightly longer load times (all packages at once)
Option B: Per-Package Storybooks
Section titled “Option B: Per-Package Storybooks”packages/ui-web/.storybook/packages/ui-stay-match/.storybook/packages/ui-bonjour-services/.storybook/ (future)Pros:
- Faster isolated development
- Package-specific customization
Cons:
- Harder to compare across packages
- Multiple dev servers
- Configuration duplication
Recommended Stories to Create (Phase 1)
Section titled “Recommended Stories to Create (Phase 1)”Generic UI Stories (ui-web)
Section titled “Generic UI Stories (ui-web)”-
Hero.stories.tsx
- Default variant
- With/without navigation
- Dark mode toggle
- Custom className override demo
- Custom background image
- Email form interaction
-
Button.stories.tsx
- All color variants (primary, secondary, outline, ghost)
- All size variants (sm, md, lg)
- Loading state
- Disabled state
- Full width variant
- With icons (future)
Division-Specific Stories (ui-stay-match)
Section titled “Division-Specific Stories (ui-stay-match)”-
Hero.stories.tsx
- Default with Stay Match beach image
- PinkGuest brand styling
- OrangeGuest colors (future)
- Custom image override
-
Button.stories.tsx
- Stay Match brand colors
- Size variants
- States demonstration
Storybook Configuration Requirements
Section titled “Storybook Configuration Requirements”main.ts Configuration
Section titled “main.ts Configuration”import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = { stories: [ '../stories/**/*.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)', '../../ui-web/src/**/*.stories.@(js|jsx|ts|tsx)', '../../ui-stay-match/src/**/*.stories.@(js|jsx|ts|tsx)', ], addons: [ '@storybook/addon-essentials', '@storybook/addon-interactions', ], framework: { name: '@storybook/react-vite', options: {}, }, viteFinal: async (config) => { // Add Tailwind v4 PostCSS plugin // Add resolve aliases for React Native // Add optimizeDeps configuration return config; },};
export default config;preview.ts Configuration
Section titled “preview.ts Configuration”import type { Preview } from '@storybook/react';import '../styles/tailwind.css'; // Import Tailwind v4
const preview: Preview = { parameters: { controls: { matchers: { color: /(background|color)$/i, date: /Date$/, }, }, }, globalTypes: { darkMode: { description: 'Toggle dark mode', defaultValue: 'light', toolbar: { title: 'Dark Mode', icon: 'circle', items: ['light', 'dark'], }, }, },};
export default preview;Integration Steps (Next Session)
Section titled “Integration Steps (Next Session)”Estimated Time: 2-3 hours for complete setup
Step 1: Initialize Storybook Package (~15 min)
Section titled “Step 1: Initialize Storybook Package (~15 min)”cd packages/storybook
# Option A: Manual initializationnpx storybook@latest init --type react_vite --skip-install
# Option B: Use existing Storybook CLIyarn storybook initExpected Output:
.storybook/main.ts- Main configuration.storybook/preview.ts- Global decorators and parametersstories/- Example stories (can delete)
Step 2: Configure Nx Integration (~10 min)
Section titled “Step 2: Configure Nx Integration (~10 min)”Create/update packages/storybook/project.json:
{ "name": "storybook", "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "packages/storybook", "projectType": "library", "tags": ["type:tooling", "scope:shared"], "targets": { "storybook": { "executor": "@nx/storybook:storybook", "options": { "port": 6006, "configDir": "packages/storybook/.storybook" }, "configurations": { "ci": { "quiet": true } } }, "build-storybook": { "executor": "@nx/storybook:build", "outputs": ["{options.outputDir}"], "options": { "outputDir": "dist/storybook", "configDir": "packages/storybook/.storybook" }, "configurations": { "ci": { "quiet": true } } } }}Step 3: Configure Tailwind v4 Support (~15 min)
Section titled “Step 3: Configure Tailwind v4 Support (~15 min)”A. Create Tailwind CSS file:
packages/storybook/styles/tailwind.css
@import "tailwindcss";B. Create PostCSS config:
packages/storybook/.storybook/postcss.config.js
module.exports = { plugins: { '@tailwindcss/postcss': {}, autoprefixer: {}, },};C. Create Tailwind config:
packages/storybook/.storybook/tailwind.config.js
const sharedPreset = require('../../../packages/config/tailwind-preset.js');
module.exports = { presets: [sharedPreset], content: [ '../stories/**/*.{js,jsx,ts,tsx,mdx}', '../../ui-web/src/**/*.{js,jsx,ts,tsx}', '../../ui-stay-match/src/**/*.{js,jsx,ts,tsx}', ],};Step 4: Configure main.ts (~20 min)
Section titled “Step 4: Configure main.ts (~20 min)”packages/storybook/.storybook/main.ts:
import type { StorybookConfig } from '@storybook/react-vite';import { mergeConfig } from 'vite';
const config: StorybookConfig = { stories: [ '../stories/**/*.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)', '../../ui-web/src/**/*.stories.@(js|jsx|ts|tsx)', '../../ui-stay-match/src/**/*.stories.@(js|jsx|ts|tsx)', ],
addons: [ '@storybook/addon-essentials', '@storybook/addon-interactions', '@storybook/addon-links', ],
framework: { name: '@storybook/react-vite', options: {}, },
docs: { autodocs: 'tag', },
viteFinal: async (config) => { return mergeConfig(config, { resolve: { alias: { // React Native safeguards (same as web apps) 'react-native$': 'react-native-web', 'react-native-svg': 'react-native-svg-web', '@cloudalt-frontend/ui': '@cloudalt-frontend/ui/index.web', }, }, optimizeDeps: { exclude: ['react-native'], }, css: { postcss: './packages/storybook/.storybook/postcss.config.js', }, }); },};
export default config;Step 5: Configure preview.ts (~15 min)
Section titled “Step 5: Configure preview.ts (~15 min)”packages/storybook/.storybook/preview.ts:
import type { Preview } from '@storybook/react';import '../styles/tailwind.css';
const preview: Preview = { parameters: { controls: { matchers: { color: /(background|color)$/i, date: /Date$/i, }, }, backgrounds: { default: 'light', values: [ { name: 'light', value: '#ffffff' }, { name: 'dark', value: '#1a1a1a' }, { name: 'gray', value: '#f3f4f6' }, ], }, },
globalTypes: { darkMode: { description: 'Toggle dark mode', defaultValue: 'light', toolbar: { title: 'Dark Mode', icon: 'circlehollow', items: [ { value: 'light', icon: 'sun', title: 'Light' }, { value: 'dark', icon: 'moon', title: 'Dark' }, ], dynamicTitle: true, }, }, },
decorators: [ (Story, context) => { const darkMode = context.globals.darkMode === 'dark'; return ( <div className={darkMode ? 'dark' : ''}> <div className="min-h-screen bg-white dark:bg-gray-900"> <Story /> </div> </div> ); }, ],};
export default preview;Step 6: Create Introduction Stories (~20 min)
Section titled “Step 6: Create Introduction Stories (~20 min)”A. Architecture Introduction:
packages/storybook/stories/Introduction.mdx
import { Meta } from '@storybook/blocks';
<Meta title="Introduction/Architecture" />
# CloudAlt Component Library
Welcome to the CloudAlt monorepo component library! This Storybook showcasesall web components built with Tailwind CSS v4 and the Tailwind ecosystem.
## Package Architecture
### Generic Components (`ui-web`)Located in `packages/ui-web/`, these components are:- **Cross-division**: Used by all divisions (Stay Match, Bonjour Services, etc.)- **Prototyping-first**: No brand defaults, fully customizable- **Headless styling**: Accept className prop with tailwind-merge support
**When to use**: Prototypes, shared tools, apps needing full customization
### Division-Specific Components (`ui-stay-match`)Located in `packages/ui-stay-match/`, these components are:- **Branded by default**: Stay Match colors, imagery, and styling- **Quick to deploy**: Minimal props needed for brand-consistent apps- **Override-friendly**: Still accept className overrides
**When to use**: Stay Match division apps (PinkGuest, OrangeGuest, etc.)
## Tailwind Ecosystem
We use four essential libraries:
1. **@headlessui/react** - Accessible, unstyled UI primitives2. **tailwind-merge** - Intelligent className conflict resolution3. **clsx** - Conditional className construction4. **tailwind-variants** - Type-safe component variants
See the "Tailwind Ecosystem" section for detailed usage.B. Tailwind Usage Guide:
packages/storybook/stories/Tailwind.mdx
import { Meta } from '@storybook/blocks';
<Meta title="Introduction/Tailwind Ecosystem" />
# Tailwind Ecosystem Guide
## tailwind-merge
Use for components that accept className prop:
\`\`\`tsximport { twMerge } from 'tailwind-merge';
function Card({ className, children }) { return ( <div className={twMerge('p-4 rounded-lg bg-white', className)}> {children} </div> );}
// User can override: <Card className="p-8 bg-gray-100" />// Result: p-8 rounded-lg bg-gray-100 (p-4 and bg-white replaced)\`\`\`
## clsx
Use for conditional classes:
\`\`\`tsximport clsx from 'clsx';
function Button({ variant, loading, children }) { return ( <button className={clsx( 'px-4 py-2 rounded', { 'bg-blue-500': variant === 'primary', 'bg-gray-500': variant === 'secondary', 'opacity-50 cursor-not-allowed': loading, } )}> {children} </button> );}\`\`\`
## tailwind-variants
Use for type-safe variant systems:
\`\`\`tsximport { tv } from 'tailwind-variants';
const button = tv({ base: 'font-semibold rounded transition-colors', variants: { color: { primary: 'bg-pink-600 hover:bg-pink-700 text-white', secondary: 'bg-gray-600 hover:bg-gray-700 text-white', }, size: { sm: 'text-sm px-3 py-1.5', md: 'text-base px-4 py-2', lg: 'text-lg px-6 py-3', }, },});
function Button({ color, size, children }) { return <button className={button({ color, size })}>{children}</button>;}\`\`\`
## @headlessui/react
Use for accessible UI patterns:
\`\`\`tsximport { Menu } from '@headlessui/react';
function Dropdown() { return ( <Menu> <Menu.Button className="px-4 py-2 bg-blue-500 text-white rounded"> Options </Menu.Button> <Menu.Items className="mt-2 bg-white shadow-lg rounded"> <Menu.Item> {({ active }) => ( <a className={active ? 'bg-blue-100' : ''}>Account</a> )} </Menu.Item> </Menu.Items> </Menu> );}\`\`\`Step 7: Write Component Stories (~30 min)
Section titled “Step 7: Write Component Stories (~30 min)”A. Generic Hero Stories:
packages/ui-web/src/components/Hero/Hero.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';import { Hero } from './Hero';
const meta = { title: 'Generic/Hero', component: Hero, parameters: { layout: 'fullscreen', }, tags: ['autodocs'],} satisfies Meta<typeof Hero>;
export default meta;type Story = StoryObj<typeof meta>;
export const Default: Story = { args: { title: 'Welcome to Our Platform', description: 'Your journey starts here. Connect with amazing people around the world.', ctaText: 'Get Started', onCtaPress: () => console.log('CTA clicked'), backgroundImage: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4', showNavigation: true, },};
export const WithoutNavigation: Story = { args: { ...Default.args, showNavigation: false, },};
export const DarkMode: Story = { args: { ...Default.args, darkMode: true, },};
export const WithSecondaryButton: Story = { args: { ...Default.args, secondaryCtaText: 'Learn More', onSecondaryCtaPress: () => console.log('Secondary clicked'), },};
export const CustomBackground: Story = { args: { ...Default.args, backgroundImage: 'https://images.unsplash.com/photo-1469474968028-56623f02e42e', title: 'Explore Nature', description: 'Discover breathtaking destinations and create unforgettable memories.', },};B. Generic Button Stories:
packages/ui-web/src/components/Button/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';import { Button } from './Button';
const meta = { title: 'Generic/Button', component: Button, tags: ['autodocs'], argTypes: { color: { control: 'select', options: ['primary', 'secondary', 'outline', 'ghost'], }, size: { control: 'select', options: ['sm', 'md', 'lg'], }, },} satisfies Meta<typeof Button>;
export default meta;type Story = StoryObj<typeof meta>;
export const Primary: Story = { args: { color: 'primary', children: 'Primary Button', },};
export const Secondary: Story = { args: { color: 'secondary', children: 'Secondary Button', },};
export const Outline: Story = { args: { color: 'outline', children: 'Outline Button', },};
export const Ghost: Story = { args: { color: 'ghost', children: 'Ghost Button', },};
export const Small: Story = { args: { size: 'sm', children: 'Small Button', },};
export const Large: Story = { args: { size: 'lg', children: 'Large Button', },};
export const Loading: Story = { args: { loading: true, children: 'Loading...', },};
export const Disabled: Story = { args: { disabled: true, children: 'Disabled Button', },};
export const FullWidth: Story = { args: { fullWidth: true, children: 'Full Width Button', },};
export const AllVariants: Story = { render: () => ( <div className="space-y-4 p-6"> <h3 className="text-lg font-semibold mb-4">All Button Variants</h3>
<div> <h4 className="text-sm font-medium mb-2">Colors</h4> <div className="flex gap-2"> <Button color="primary">Primary</Button> <Button color="secondary">Secondary</Button> <Button color="outline">Outline</Button> <Button color="ghost">Ghost</Button> </div> </div>
<div> <h4 className="text-sm font-medium mb-2">Sizes</h4> <div className="flex items-center gap-2"> <Button size="sm">Small</Button> <Button size="md">Medium</Button> <Button size="lg">Large</Button> </div> </div>
<div> <h4 className="text-sm font-medium mb-2">States</h4> <div className="flex gap-2"> <Button loading>Loading</Button> <Button disabled>Disabled</Button> </div> </div> </div> ),};C. Stay Match Hero Stories:
packages/ui-stay-match/src/components/Hero/Hero.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';import { Hero } from './Hero';
const meta = { title: 'Stay Match/Hero', component: Hero, parameters: { layout: 'fullscreen', }, tags: ['autodocs'],} satisfies Meta<typeof Hero>;
export default meta;type Story = StoryObj<typeof meta>;
export const DefaultPinkGuest: Story = { args: { title: 'Welcome to PinkGuest', description: 'Your inclusive community for LGBTQ+ friendly stays.', ctaText: 'Get Started', onCtaPress: () => console.log('CTA clicked'), showNavigation: true, // Uses default gay couple beach image },};
export const OrangeGuestBrand: Story = { args: { title: 'Welcome to OrangeGuest', description: 'Discover unique stays in vibrant destinations.', ctaText: 'Explore Now', onCtaPress: () => console.log('CTA clicked'), showNavigation: true, },};
export const WithCustomImage: Story = { args: { title: 'Custom Hero', description: 'Override the default image with your own.', ctaText: 'Learn More', onCtaPress: () => console.log('CTA clicked'), backgroundImage: 'https://images.unsplash.com/photo-1527856263669-12c3a0af2aa6', },};Step 8: Test and Verify (~15 min)
Section titled “Step 8: Test and Verify (~15 min)”# Start Storybook dev serveryarn nx storybook storybook# Opens at http://localhost:6006
# Build static Storybookyarn nx build-storybook storybook# Outputs to dist/storybook
# Test stories render correctly# - Check all Hero variants# - Check all Button variants# - Toggle dark mode# - Verify Tailwind styles apply# - Check no console errorsStep 9: Add to Verification Pipeline (~5 min)
Section titled “Step 9: Add to Verification Pipeline (~5 min)”Update scripts/verify-repo.sh:
# Add after existing stagesecho "📚 Stage 12: Storybook Build"if yarn nx build-storybook storybook > /dev/null 2>&1; then echo " ✅ Storybook builds successfully"else echo " ❌ Storybook build failed" exit 1fiTroubleshooting Common Issues
Section titled “Troubleshooting Common Issues”Issue: Tailwind styles not applying
Fix: Verify postcss.config.js path in viteFinal(), restart Storybook
Issue: React Native imports causing errors
Fix: Check Vite resolve aliases match web app pattern
Issue: Stories not discovered
Fix: Verify stories glob pattern in main.ts includes correct paths
Issue: Dark mode toggle not working
Fix: Check decorator in preview.ts applies dark class to wrapper div
Future Storybook Enhancements
Section titled “Future Storybook Enhancements”- Chromatic Integration - Visual regression testing
- Accessibility Testing - @storybook/addon-a11y
- Figma Integration - storybook-addon-designs
- Design Tokens Documentation - Stories for color, spacing, typography
- Interaction Testing - @storybook/test with play functions
- Mobile Preview - Viewport addon for responsive testing
Chapter 3: Architecture Decisions & Rationale
Section titled “Chapter 3: Architecture Decisions & Rationale”Package Structure Philosophy
Section titled “Package Structure Philosophy”Generic vs Division-Specific Pattern
Section titled “Generic vs Division-Specific Pattern”Decision: Create separate ui-web (generic) and ui-\{division\} (specific) packages
Rationale:
- Prototyping Speed - Generic components allow quick app scaffolding
- Brand Identity - Division-specific packages maintain unique brand styling
- Code Reuse - Generic components shared across all divisions
- Flexibility - Apps choose appropriate package for their needs
- Clear Ownership - Division teams own their ui-{division} packages
When to Use Each:
- ui-web: Cross-division prototypes, shared tooling, Bonjour Services apps
- ui-stay-match: All Stay Match division apps (PinkGuest, OrangeGuest, etc.)
- ui-{division}: Future division-specific packages as needed
Package Dependencies
Section titled “Package Dependencies”Apps (pinkguest-web, bonjour-locker-web) ↓Division Packages (ui-stay-match) OR Generic (ui-web) ↓Shared Packages (config, theme) ↓Root Dependencies (tailwind, react, etc.)Tailwind CSS v4 Architecture
Section titled “Tailwind CSS v4 Architecture”Why Tailwind v4 with PostCSS Plugin?
Section titled “Why Tailwind v4 with PostCSS Plugin?”Decision: Use Tailwind v4 with @tailwindcss/postcss plugin, not Tailwind CLI
Rationale:
- Vite Integration - Seamless integration with Vite’s PostCSS pipeline
- HMR Support - Instant style updates during development
- Build Optimization - Automatic purging and minification
- Future-Proof - Tailwind v4 is the modern approach
- Monorepo Friendly - Shared preset works across all apps
Configuration Pattern:
// postcss.config.js (per app)module.exports = { plugins: { '@tailwindcss/postcss': {}, autoprefixer: {}, },};
// tailwind.config.js (per app)module.exports = { presets: [require('../../packages/config/tailwind-preset.js')], content: [ './src/**/*.{js,ts,jsx,tsx}', '../../packages/ui-web/src/**/*.{js,ts,jsx,tsx}', ],};Why Shared Tailwind Preset?
Section titled “Why Shared Tailwind Preset?”Decision: Create packages/config/tailwind-preset.js extended by all apps
Rationale:
- Design Consistency - All apps use same design tokens
- Single Source of Truth - Update colors/spacing in one place
- Brand Variants - Preset includes all division brand colors
- Easy Updates - Change preset, all apps update
- Plugin Management - Shared plugins (forms, typography, etc.)
Tailwind Ecosystem Libraries
Section titled “Tailwind Ecosystem Libraries”Why These Four Libraries?
Section titled “Why These Four Libraries?”Decision: Install @headlessui/react, tailwind-merge, clsx, tailwind-variants
Rationale:
-
@headlessui/react (Tier 1 Essential)
- Unstyled, accessible components (Dropdown, Modal, Tabs)
- WAI-ARIA compliant out of the box
- Full keyboard navigation
- Tailwind-first design
- React Native parallel: React Navigation, custom modals
-
tailwind-merge (Tier 1 Essential)
- Intelligent className merging
- Prevents conflicts (e.g.,
p-4+p-6=p-6, not both) - Critical for component libraries accepting className prop
- React Native parallel: StyleSheet.compose (but smarter)
-
clsx (Tier 1 Essential)
- Conditional className construction
- Clean syntax:
clsx({ 'text-red': error, 'text-green': success }) - Tiny bundle size (228 bytes)
- React Native parallel: Inline style conditionals
-
tailwind-variants (Tier 1 Essential)
- Type-safe variant system
- Compile-time safety for color/size props
- IntelliSense support
- Compound variants (size + color combinations)
- React Native parallel: Custom variant hooks, but with types
Not Installed (Yet):
- @tailwindcss/forms - Wait until forms package needed
- @tailwindcss/typography - Wait until content/blog features
- tailwindcss-animate - Wait until complex animations needed
Vite Configuration Strategy
Section titled “Vite Configuration Strategy”React Native Web Safeguards
Section titled “React Native Web Safeguards”Problem: React Native code bundling in web apps causes:
- White page / blank screen
- “window is not defined” errors
- 10+ MB bundle sizes
- Failed builds
Solution: Vite resolve aliases + optimizeDeps configuration
// vite.config.ts (every web app)export default defineConfig({ resolve: { alias: { 'react-native$': 'react-native-web', 'react-native-svg': 'react-native-svg-web', '@cloudalt-frontend/ui': '@cloudalt-frontend/ui/index.web', }, }, optimizeDeps: { exclude: ['react-native'], },});Why This Works:
- Alias Interception - Rewrites React Native imports to web equivalents
- Pre-bundling Prevention - exclude: [‘react-native’] stops Vite from pre-processing
- Selective Mapping - Only maps imports that could leak into web builds
- Zero Runtime Cost - Pure build-time transformation
Lessons Learned:
- Must use
$inreact-native$to match exact import (not react-native-web itself) - Must exclude react-native from optimizeDeps (pre-bundling breaks it)
- Must clean
.nx/cacheafter adding aliases
Centralized Dependency Policy
Section titled “Centralized Dependency Policy”Why No App-Level Dependencies?
Section titled “Why No App-Level Dependencies?”Decision: All dependencies at monorepo root, no app package.json deps/devDeps
Rationale:
- Version Consistency - One version of React across all apps
- Dependency Deduplication - Smaller total node_modules
- Update Simplicity - Update once, affects all apps
- Build Performance - Shared deps cached by Nx
- Policy Enforcement - Automated via check-app-package-policies.mjs
Enforcement:
# Runs in CI and verify-repo.shnode scripts/check-app-package-policies.mjsExceptions (None Currently):
- App-specific dependencies currently not allowed
- If needed, document in policy script
Component API Design
Section titled “Component API Design”Hero Component Props
Section titled “Hero Component Props”Design Decision: Flexible image prop supporting multiple formats
backgroundImage?: string | number | { uri: string };Rationale:
string- Web URLs, imported assetsnumber- React Native require() format{ uri: string }- Remote images, dynamic URLs- Cross-platform compatibility
Division-Specific Difference:
- ui-web:
backgroundImagerequired (no default) - ui-stay-match:
backgroundImageoptional (has default beach image)
Button Component Variants
Section titled “Button Component Variants”Design Decision: Use tailwind-variants with type-safe props
const button = tv({ base: 'font-semibold rounded transition-colors', variants: { color: { primary: 'bg-pink-600 hover:bg-pink-700', secondary: 'bg-gray-600 hover:bg-gray-700', outline: 'border-2 border-pink-600 hover:bg-pink-50', ghost: 'hover:bg-gray-100', }, size: { sm: 'text-sm px-3 py-1.5', md: 'text-base px-4 py-2', lg: 'text-lg px-6 py-3', }, },});Rationale:
- Type safety prevents invalid props
- IntelliSense shows available variants
- Easy to add new variants
- Compound variants support future needs
- Clear separation of concerns
Chapter 4: Unresolved Items & Next Actions
Section titled “Chapter 4: Unresolved Items & Next Actions”Immediate Actions (Before Storybook)
Section titled “Immediate Actions (Before Storybook)”1. Commit Hero Fix
Section titled “1. Commit Hero Fix”Status: Code working via HMR, not committed
Files Changed: packages/ui-stay-match/src/components/Hero/Hero.tsx
Action Required:
git add packages/ui-stay-match/src/components/Hero/Hero.tsxgit commit -m "fix(ui-stay-match): add default hero image fallback
- Import hero-13-gay-couple-visiting-beach.jpeg as default- Use default image when backgroundImage prop not provided- Maintains division-specific branding for Stay Match apps"2. Production Build Verification
Section titled “2. Production Build Verification”Status: Not tested
Risk: High - builds could fail despite dev working
Action Required:
# Test both web appsyarn nx build pinkguest-webyarn nx build bonjour_locker-web
# Check outputls -lh dist/apps/stay_match/pinkguest/web/ls -lh dist/apps/bonjour_services/bonjour_locker/web/
# Verify no React Native in bundlesgrep -r "react-native" dist/apps/stay_match/pinkguest/web/assets/
# Expected: No matches or only react-native-web referencesSuccess Criteria:
- Both builds complete without errors
- Bundle sizes reasonable (< 1MB main chunk)
- No React Native code in output
- TypeScript compilation succeeds
- All chunks have proper hashes
3. Push to Remote
Section titled “3. Push to Remote”Status: Ready after build verification
Action Required:
git push origin feat/tailwind-integration
# Expected: 8 commits pushed# 7 original + 1 hero fix4. Create Pull Request
Section titled “4. Create Pull Request”Status: Blocked by push
Content Required:
- Title:
feat: Integrate Tailwind v4 with ecosystem libraries and component packages - Link to TAILWIND_INTEGRATION_HANDOFF.md
- Highlight breaking changes (new package structure)
- Request reviews from division leads
- Note Storybook is next milestone
Medium Priority (After Storybook)
Section titled “Medium Priority (After Storybook)”5. Replicate to Other Stay Match Brands
Section titled “5. Replicate to Other Stay Match Brands”Apps to Update:
apps/stay_match/orangeguest/web/apps/stay_match/purpleguest/web/apps/stay_match/rainbowhost/web/apps/stay_match/staymatch_app/web/
Files to Create/Update Per App:
{app}/web/├── postcss.config.js (copy from pinkguest)├── tailwind.config.js (update content paths)├── vite.config.ts (add aliases)├── src/styles.css (add @tailwind directives)├── src/app/app.tsx (import Hero from ui-stay-match)└── project.json (verify serve/build targets)
{app}/shared/assets/images/hero/└── hero-image.jpeg (brand-specific image)Automation Opportunity: Create script to scaffold Tailwind setup for new apps
6. Division-Specific Packages
Section titled “6. Division-Specific Packages”Future Packages to Create:
packages/ui-bonjour-services/- For Bonjour division appspackages/ui-guestroom/- For Guestroom division appspackages/ui-homestay/- For Homestay division apps- etc.
When to Create: When division has 2+ web apps needing shared components
Pattern to Follow: Copy ui-stay-match structure, update branding
Low Priority (Future Enhancements)
Section titled “Low Priority (Future Enhancements)”7. Tailwind v4 Advanced Features
Section titled “7. Tailwind v4 Advanced Features”Not Yet Implemented:
- Custom @theme directives
- Component-level @utility
- @layer organization
- Advanced @variants
When Needed: When design system matures and requires custom utilities
8. Performance Optimization
Section titled “8. Performance Optimization”Potential Improvements:
- Analyze bundle sizes with @nx/rollup or vite-bundle-visualizer
- Tree-shake unused Tailwind utilities (already automatic)
- Split chunks for better caching
- Lazy load Hero images
Measure First: Use Lighthouse, WebPageTest before optimizing
9. Testing Strategy
Section titled “9. Testing Strategy”Current Status: No automated tests for components
Future Tests Needed:
- Component unit tests (Vitest + Testing Library)
- Visual regression tests (Chromatic + Storybook)
- Accessibility tests (jest-axe)
- E2E tests (Playwright for web apps)
Start With: Storybook interaction tests using @storybook/test
Known Issues (None Currently)
Section titled “Known Issues (None Currently)”✅ White page issue - RESOLVED (Vite aliases)
✅ Hero not displaying - RESOLVED (default image import)
✅ Type errors - RESOLVED (proper declarations)
✅ Build failures - RESOLVED (optimizeDeps configuration)
Questions for Next Session
Section titled “Questions for Next Session”- Storybook Architecture: ✅ Confirmed single monorepo Storybook (see Chapter 2 Step-by-Step guide)
- Component Priorities: Which components to add next after Hero/Button? (Form inputs, Cards, Modals?)
- Design Tokens: Should we formalize tokens in Storybook docs before or after Figma integration?
- Mobile Web: Any responsive design concerns for mobile web?
- Dark Mode: Implement dark mode system-wide now or wait for Storybook dark mode testing?
- Accessibility: Priority level for WCAG compliance? (Storybook a11y addon ready)
- Visual Regression: Use Chromatic (Storybook’s official tool) or alternatives?
- Component Scope: Should Storybook include React Native components or only web?
Development Roadmap Context
Section titled “Development Roadmap Context”Note: The original development roadmap from earlier in the conversation is now beyond working memory. Key priorities have been extracted and documented here, but the full roadmap may need to be referenced separately.
Known Priorities from Roadmap:
- ✅ Tailwind v4 integration (COMPLETE)
- 🔄 Storybook integration (NEXT)
- ⏳ Component library expansion
- ⏳ Design system documentation
- ⏳ Testing infrastructure
- ⏳ CI/CD pipeline enhancements
Useful Commands Reference
Section titled “Useful Commands Reference”# Developmentyarn nx serve pinkguest-web # Port 4200yarn nx serve bonjour_locker-web # Port 4202
# Buildingyarn nx build pinkguest-webyarn nx build bonjour_locker-web
# Testingyarn nx test pinkguest-webyarn nx lint pinkguest-web
# Verification./scripts/verify-repo.shnode scripts/check-app-package-policies.mjs
# 🆕 Storybook (after setup)yarn nx storybook storybook # Port 6006yarn nx build-storybook storybook # Static build to dist/storybookyarn nx test-storybook storybook # Run interaction tests (future)
# Gitgit status --shortgit log --oneline -10git push origin feat/tailwind-integrationDependencies Installed This Session
Section titled “Dependencies Installed This Session”{ "devDependencies": { "@headlessui/react": "latest", "tailwind-merge": "latest", "clsx": "latest", "tailwind-variants": "3.1.1", "tailwindcss": "4.1.14", "@tailwindcss/postcss": "4.1.14", "autoprefixer": "10.4.21" }}TypeScript Path Mappings Added
Section titled “TypeScript Path Mappings Added”{ "compilerOptions": { "paths": { "@cloudalt-frontend/ui-web": ["packages/ui-web/src/index.ts"], "@cloudalt-frontend/ui-stay-match": ["packages/ui-stay-match/src/index.ts"] } }}Handoff Checklist
Section titled “Handoff Checklist”For Next Session
Section titled “For Next Session”- Read Chapter 1 for current status
- Read Chapter 2 for Storybook plan
- Review Chapter 3 for architectural context
- Check Chapter 4 for immediate actions
- Run
git statusto see uncommitted changes - Verify both dev servers still running (ports 4200, 4202)
- Check if production builds pass
- Read commit history:
git log --oneline -10
Success Indicators
Section titled “Success Indicators”- All 7 (soon 8) commits on
feat/tailwind-integrationbranch - PinkGuest showing Hero with beach image at localhost:4200
- Bonjour Locker showing Hero with rotating taglines at localhost:4202
- No console errors in browser
- HMR working for both apps
- Policy enforcement active (no app-level dependencies)
Critical Files to Review
Section titled “Critical Files to Review”packages/ui-web/src/components/Hero/Hero.tsx- Generic Heropackages/ui-stay-match/src/components/Hero/Hero.tsx- Division Hero (has uncommitted fix)packages/config/tailwind-preset.js- Shared presetapps/stay_match/pinkguest/web/vite.config.ts- Vite aliases patterndocs/tailwind-ecosystem-guide.md- Library usage guide
End of Handoff Document
Section titled “End of Handoff Document”Total Work Completed: 7 commits, 2 working web apps, 2 component packages, comprehensive documentation
Next Milestone: Storybook integration
Estimated Next Session Duration: 2-3 hours for full Storybook setup
Branch Status: Ready to push after production build verification
Questions? Reference specific chapters above or check git commit messages for detailed changes.