CloudAlt Component Layering - Complete Guide
Comprehensive Documentation for the Three-Tier Component Architecture
Last Updated: October 18, 2025
Status: ✅ Active
Table of Contents
Section titled “Table of Contents”- Overview
- Architecture Model
- Quick Start Guide
- Nx Layer Tagging System
- Component Promotion Guide
- Storybook Integration
- Design Tokens & Theming
- Testing & Validation
- Best Practices
- Troubleshooting
- Phase 1 Implementation Summary
Overview
Section titled “Overview”CloudAlt uses a three-tier component architecture designed to maximize component reuse across our multi-brand monorepo while enabling app-specific customization when needed.
Architecture Diagram
Section titled “Architecture Diagram”┌─────────────────────────────────────┐│ App Layer (@app/components) │ ← App-specific overrides│ Last-mile customizations │└──────────────┬──────────────────────┘ │ imports from ↓┌──────────────▼──────────────────────┐│ Division Layer (ui-<division>) │ ← Shared within family│ Brand-family patterns │└──────────────┬──────────────────────┘ │ imports from ↓┌──────────────▼──────────────────────┐│ Core Layer (ui, ui-web) │ ← Generic primitives│ Cross-app components │└─────────────────────────────────────┘Key Principles
Section titled “Key Principles”- Bottom-Up Dependencies: Apps depend on Division, Division depends on Core
- Promotion Over Duplication: Successful patterns move up the stack
- Token-First Theming: Visual customization via design tokens
- Enforcement: Nx module boundaries ensure architectural integrity
Architecture Model
Section titled “Architecture Model”1. Core Layer (Monorepo-Level, Generic)
Section titled “1. Core Layer (Monorepo-Level, Generic)”Scope: Cross-app primitives that work for all brands/divisions.
Location:
packages/ui— Platform-agnostic core componentspackages/ui-web— Web-specific core components (shadcn-based)- Supporting packages:
packages/common,packages/icons,packages/theme,packages/brands,packages/config
Import pattern:
import { Button, Input, Typography } from '@cloudalt-frontend/ui-web'import { Icon } from '@cloudalt-frontend/icons'Dependencies: Only core packages and external libraries. No division or app code.
Nx tags: ["layer:core-ui", "scope:shared"]
2. Division Layer (App-Family Specific)
Section titled “2. Division Layer (App-Family Specific)”Scope: Patterns shared within a brand family (e.g., Stay Match apps share hero sections, CTA blocks, navigation menus, auth flows).
Location: packages/ui-<division>
Existing divisions:
ui-stay-match— pinkguest, orangeguest, purpleguest, rainbowhost, staymatch_appui-altfinder— altFinder_app, orangeFinder, pinkFinderui-roommate— roommate_guru, roommate_help, roommate_worksui-guestroom— guestroom_city, guestroom_host, guestroom_travel, etc.ui-bonjour-services— bonjour_locker, bonjour_it_com, lingua_it_comui-greenhost,ui-homestay,ui-pride-city,ui-room-lgbt,ui-hostguest
Import pattern:
import { Hero, StayMatchNavigation } from '@cloudalt-frontend/ui-stay-match'Dependencies: Can depend on Core layer only. Cannot depend on other divisions or apps.
Nx tags: ["layer:division-ui", "division:<name>"]
3. App Layer (App-Specific Customizations)
Section titled “3. App Layer (App-Specific Customizations)”Scope: Last-mile “this app only” components — branded wrappers, page-level composites, one-off variations.
Location: apps/<division>/<app>/<platform>/src/components/
Import pattern:
import { Hero } from '@app/components' // App-specific overrideHow it works:
- Each app defines a local
@app/componentspath alias in itstsconfig.app.json - Override wraps/extends the division or core component
- Keeps same props API for easy promotion later
Dependencies: Can depend on Division and Core layers.
Nx tags: ["layer:app", "division:<name>", "platform:web|mobile"]
Quick Start Guide
Section titled “Quick Start Guide”Import Priority
Section titled “Import Priority”When working in an app, follow this import order:
-
✅ Division components (if available):
import { Hero } from '@cloudalt-frontend/ui-stay-match' -
✅ Core components (fallback):
import { Button } from '@cloudalt-frontend/ui-web'import { Icon } from '@cloudalt-frontend/icons' -
⚠️ App overrides (use sparingly):
import { Hero } from '@app/components'
When to Create an App Override
Section titled “When to Create an App Override”Good reasons:
- ✅ App-specific branding or styling
- ✅ One-off layout adjustments
- ✅ Integration with app-specific state/context
- ✅ Testing a new pattern before promoting
Bad reasons:
- ❌ Fixing a bug (fix it in the division/core instead)
- ❌ Adding a feature useful to other apps (promote it)
- ❌ Working around poor component design (refactor the source)
Creating an App Override
Section titled “Creating an App Override”1. Add the component file
Section titled “1. Add the component file”# In your app directorytouch apps/<division>/<app>/<platform>/src/components/Hero.tsx2. Implement the override
Section titled “2. Implement the override”Option A: Wrap the division component
import { Hero as DivisionHero, HeroProps } from '@cloudalt-frontend/ui-stay-match'
export const Hero = (props: HeroProps) => { return ( <DivisionHero {...props} className="pinkguest-custom-hero" theme="gradient" /> )}Option B: Extend with additional props
import { Hero as DivisionHero, HeroProps } from '@cloudalt-frontend/ui-stay-match'
interface PinkguestHeroProps extends HeroProps { showPromotion?: boolean}
export const Hero = ({ showPromotion, ...props }: PinkguestHeroProps) => { return ( <div> <DivisionHero {...props} /> {showPromotion && <div className="promotion-banner">Special Offer!</div>} </div> )}Option C: Fully custom (same API)
import { HeroProps } from '@cloudalt-frontend/ui-stay-match'
export const Hero = ({ title, subtitle, image }: HeroProps) => { return ( <section className="pinkguest-hero"> <h1>{title}</h1> <p>{subtitle}</p> <img src={image} alt="" /> </section> )}3. Export from index
Section titled “3. Export from index”export { Hero } from './Hero'4. Use in your app
Section titled “4. Use in your app”import { Hero } from '@app/components'
export const HomePage = () => { return <Hero title="Welcome to Pink Guest" />}Nx Layer Tagging System
Section titled “Nx Layer Tagging System”Tag Hierarchy
Section titled “Tag Hierarchy”All projects in the monorepo are tagged with layer identifiers that enforce architectural boundaries through ESLint’s @nx/enforce-module-boundaries rule.
┌─────────────────────────────────────┐│ layer:core-ui │ ← Generic, reusable components│ packages/ui, packages/ui-web │ (Button, Card, Modal, etc.)└─────────────────────────────────────┘ ↓ can depend on┌─────────────────────────────────────┐│ layer:division-ui │ ← Division-specific components│ packages/ui-stay-match │ (ListingCard, SearchFilters)│ packages/ui-altfinder ││ packages/ui-roommate (etc.) │└─────────────────────────────────────┘ ↓ can depend on┌─────────────────────────────────────┐│ layer:app │ ← App-specific customizations│ apps/stay_match/pinkguest/web │ (rare, only when needed)│ apps/roommate/roommate_guru/web │└─────────────────────────────────────┘Tag Definitions
Section titled “Tag Definitions”layer:core-ui
Section titled “layer:core-ui”Applied to: packages/ui, packages/ui-web
Can depend on:
- Other
layer:core-uipackages scope:sharedutilities (types, hooks, services)type:libortype:packagefoundational packages
Cannot depend on:
layer:division-uipackageslayer:appprojects
Additional tags:
scope:shared- Indicates shared/common usageplatform:web(for ui-web only)
layer:division-ui
Section titled “layer:division-ui”Applied to: packages/ui-{division} (e.g., ui-stay-match, ui-altfinder)
Can depend on:
layer:core-uipackages (core components)- Other
layer:division-uipackages (cross-division reuse, use sparingly) scope:sharedutilitiestype:libortype:packagefoundational packages
Cannot depend on:
layer:appprojects
Additional tags:
division:{name}- Links to division (e.g.,division:stay-match)
layer:app
Section titled “layer:app”Applied to: apps/**/* (all web and mobile apps)
Can depend on:
- Everything (no restrictions)
- Recommended: Prefer your division’s UI package
Additional tags:
division:{name}- Division membershipplatform:weborplatform:mobile- Platform identifier
Tag Assignment
Section titled “Tag Assignment”Tags are automatically assigned to project.json files using the tagging script:
node scripts/add-layer-tags.mjsModule Boundary Rules
Section titled “Module Boundary Rules”Enforcement is configured in eslint.config.mjs:
'@nx/enforce-module-boundaries': [ 'error', { depConstraints: [ // Core UI: Only depend on core/shared packages { sourceTag: 'layer:core-ui', onlyDependOnLibsWithTags: ['layer:core-ui', 'scope:shared', 'type:lib', 'type:package'], }, // Division UI: Can use core UI, but not apps { sourceTag: 'layer:division-ui', onlyDependOnLibsWithTags: ['layer:core-ui', 'layer:division-ui', 'scope:shared', 'type:lib', 'type:package'], notDependOnLibsWithTags: ['layer:app'], }, // Apps: Can depend on anything { sourceTag: 'layer:app', onlyDependOnLibsWithTags: ['*'], }, ], },],Checking Violations
Section titled “Checking Violations”# Lint all projectsnx run-many --target=lint --all
# Lint specific projectnx lint pinkguest-webCommon Boundary Violations
Section titled “Common Boundary Violations”❌ Core UI importing from Division UI
import { ListingCard } from '@cloudalt-frontend/ui-stay-match'; // ERROR!Error:
A project tagged with "layer:core-ui" can only depend on libs tagged with "layer:core-ui", "scope:shared", "type:lib", "type:package"❌ Division UI importing from App
import { CustomFilter } from 'apps/stay_match/pinkguest/web/src/components'; // ERROR!Error:
A project tagged with "layer:division-ui" cannot depend on libs tagged with "layer:app"Component Promotion Guide
Section titled “Component Promotion Guide”When to Promote
Section titled “When to Promote”App → Division
Section titled “App → Division”Promote when:
- ✅ The component is used (or will be used) by multiple apps in the same division
- ✅ The component has division-specific business logic or styling
- ✅ You want to establish a division-wide pattern
Example: ListingCard starts in pinkguest-web, but rainbowhost and purpleguest need it → promote to ui-stay-match
Division → Core
Section titled “Division → Core”Promote when:
- ✅ The component is used (or will be used) by multiple divisions
- ✅ The component is generic with no division-specific concerns
- ✅ You want to establish a monorepo-wide pattern
Example: RatingStars used in stay-match, altfinder, and roommate → promote to ui-web
When NOT to Promote
Section titled “When NOT to Promote”❌ Don’t promote if:
- Component is only used in one place (YAGNI principle)
- Component contains app-specific hacks or temporary code
- Unsure if pattern will be reused (wait for second use case - Rule of Three)
Promotion Method: Nx Generator (Recommended)
Section titled “Promotion Method: Nx Generator (Recommended)”The automated Nx generator handles file moving, export updates, and creates TODO comments for import updates.
Basic Usage
Section titled “Basic Usage”# Interactive promptsyarn nx g @cloudalt-frontend/generators:promote-component
# With argumentsyarn nx g @cloudalt-frontend/generators:promote-component \ ListingCard \ --from=app \ --to=division \ --sourceProject=pinkguest-web \ --targetProject=ui-stay-match
# Dry run (preview only)yarn nx g @cloudalt-frontend/generators:promote-component \ Button \ --from=division \ --to=core \ --sourceProject=ui-stay-match \ --targetProject=ui-web \ --dryRunGenerator Options
Section titled “Generator Options”| Option | Description | Required | Values |
|---|---|---|---|
component | Component name to promote | Yes | e.g., ListingCard, Button |
--from | Source layer | Yes | app, division, core |
--to | Target layer | Yes | division, core |
--sourceProject | Source Nx project name | Conditional | e.g., pinkguest-web, ui-stay-match |
--targetProject | Target Nx project name | Conditional | e.g., ui-stay-match, ui-web |
--dryRun | Preview without executing | No | true, false (default) |
What the Generator Does
Section titled “What the Generator Does”- ✅ Validates layer transition (only upward moves allowed)
- ✅ Finds all component files (directory or single file)
- ✅ Moves files to target location
- ✅ Removes export from source
index.ts - ✅ Adds export to target
index.ts - ✅ Adds TODO comments for manual import updates
- ✅ Formats files with Prettier
After Running Generator
Section titled “After Running Generator”The generator cannot automatically update import statements (too risky). You must:
-
Search for imports:
Terminal window # Find all imports of the componentgrep -r "import.*ListingCard" apps/ packages/ -
Update import paths:
// Before (app layer)import { ListingCard } from '@app/components';// After (division layer)import { ListingCard } from '@cloudalt-frontend/ui-stay-match'; -
Validate:
Terminal window # Run lint to check module boundariesyarn nx lint ui-stay-match# Run testsyarn nx test ui-stay-match# Check for remaining TODOsgrep -r "TODO.*ListingCard" .
Manual Promotion (Alternative)
Section titled “Manual Promotion (Alternative)”If the generator doesn’t fit your needs:
Step 1: Copy Component Files
Section titled “Step 1: Copy Component Files”# Example: promoting ListingCard from app to divisioncp -r apps/stay_match/pinkguest/web/src/components/ListingCard \ packages/ui-stay-match/src/ListingCardStep 2: Update Target Exports
Section titled “Step 2: Update Target Exports”Add export to packages/ui-stay-match/src/index.ts:
export * from './ListingCard';Step 3: Update Source Exports
Section titled “Step 3: Update Source Exports”Remove export from apps/stay_match/pinkguest/web/src/components/index.ts:
// Remove this line:// export * from './ListingCard';Step 4: Update Import Paths
Section titled “Step 4: Update Import Paths”Find and update all imports:
# Find importsgrep -r "from '@app/components'" apps/stay_match/pinkguest/web/src/
# Update imports in consuming files# Before:import { ListingCard } from '@app/components';
# After:import { ListingCard } from '@cloudalt-frontend/ui-stay-match';Step 5: Delete Source Files
Section titled “Step 5: Delete Source Files”rm -rf apps/stay_match/pinkguest/web/src/components/ListingCardStep 6: Validate
Section titled “Step 6: Validate”# Type checkyarn nx typecheck pinkguest-webyarn nx typecheck ui-stay-match
# Lint (module boundaries)yarn nx lint ui-stay-match
# Testyarn nx test ui-stay-match
# Buildyarn nx build pinkguest-webComponent Adaptation Guidelines
Section titled “Component Adaptation Guidelines”When promoting components, you often need to make them more flexible:
Make Props Configurable
Section titled “Make Props Configurable”Before (App-specific):
export function ListingCard({ listing }: { listing: Listing }) { return ( <Card> <Image src={listing.image} /> <Title>{listing.title}</Title> <Price>${listing.pricePerNight}/night</Price> </Card> );}After (Division-flexible):
export interface ListingCardProps { listing: Listing; priceLabel?: string; // Configurable showHost?: boolean; // Optional features onFavorite?: () => void;}
export function ListingCard({ listing, priceLabel = '/night', showHost = false, onFavorite,}: ListingCardProps) { return ( <Card> <Image src={listing.image} /> <Title>{listing.title}</Title> <Price>${listing.pricePerNight}{priceLabel}</Price> {showHost && <Host>{listing.host.name}</Host>} {onFavorite && <FavoriteButton onClick={onFavorite} />} </Card> );}Extract Division-Specific Logic
Section titled “Extract Division-Specific Logic”Before:
// All logic in one componentexport function SearchFilters() { const { filters, setFilters } = useStayMatchFilters(); // Division-specific // ... rendering}After (Core):
// packages/ui-web/src/FilterPanel.tsx - Generic structureexport function FilterPanel({ filters, onChange, children }) { return <Panel>{children}</Panel>;}
// packages/ui-stay-match/src/SearchFilters.tsx - Division logicexport function SearchFilters() { const { filters, setFilters } = useStayMatchFilters(); return ( <FilterPanel filters={filters} onChange={setFilters}> <PriceRangeFilter /> <DateRangeFilter /> <AmenityFilter /> </FilterPanel> );}Remove Hard-coded Styles
Section titled “Remove Hard-coded Styles”Before:
<Button className="bg-pink-600 hover:bg-pink-700"> Book Now</Button>After:
<Button variant="primary"> Book Now</Button>
// Variants handle brand colors via CSS variablesStorybook Integration
Section titled “Storybook Integration”Global Toolbar Controls
Section titled “Global Toolbar Controls”Storybook provides three toolbar selectors for brand-aware component development:
Division Selector:
- Options: altFinder, Stay Match, Roommate, Bonjour Services, Guestroom, Greenhost, Homestay, Pride City, Room LGBT, Hostguest
- Default: Stay Match
App Selector:
- Dynamically populated based on division
- Example (Stay Match): pinkguest, orangeguest, purpleguest, rainbowhost, staymatch_app
- Default: first app in division
Theme Selector:
- Options: light, dark
- Existing functionality, preserved
Preview Decorator
Section titled “Preview Decorator”What it does:
- Reads
globals.divisionandglobals.app - Loads corresponding brand theme from
@cloudalt-frontend/theme/brands - Applies CSS variables for shadcn components (e.g.,
--primary,--background) - Applies Tailwind classes for theme/brand
- Provides
context.globalsobject to stories with:brandIdbrandConfig(display name, description, features)theme(color scales, typography)
Implementation location: packages/storybook/.storybook/preview.ts
Story Authoring Pattern
Section titled “Story Authoring Pattern”Core component stories:
import type { Meta, StoryObj } from '@storybook/react'import { Button } from '@cloudalt-frontend/ui-web'
const meta: Meta<typeof Button> = { title: 'Core/Button', component: Button,}
export default metatype Story = StoryObj<typeof Button>
export const Primary: Story = { args: { children: 'Click me', color: 'primary', },}Division component stories:
import type { Meta, StoryObj } from '@storybook/react'import { Hero } from '@cloudalt-frontend/ui-stay-match'
const meta: Meta<typeof Hero> = { title: 'Stay Match/Hero', component: Hero,}
export default metatype Story = StoryObj<typeof Hero>
export const Default: Story = { args: { title: 'Find Your Perfect Match', subtitle: 'Connect with hosts worldwide', },}Viewing Components in Different Brands
Section titled “Viewing Components in Different Brands”- Open Storybook:
yarn storybook - Use toolbar controls:
- Division: Select your brand family
- App: Select specific app
- Theme: Toggle light/dark
Components will render with the selected brand’s tokens and styling.
Design Tokens & Theming
Section titled “Design Tokens & Theming”Token-First Approach
Section titled “Token-First Approach”Principle: Division/app look comes from tokens first. Only use props/styles when tokens aren’t enough.
Token sources:
packages/theme/src/tokens.ts— Base design system (colors, typography, spacing, shadows, etc.)packages/theme/src/brands.ts— Brand-specific overrides (e.g.,pinkguestTheme,orangeguestTheme)packages/config/tailwind-preset.js— Tailwind integration viacreateBrandPreset(brandName)
Using Tokens
Section titled “Using Tokens”Tailwind classes (preferred):
<button className="bg-primary-500 text-white"> Click me</button>CSS variables (shadcn components):
<button className="bg-primary text-primary-foreground"> Click me</button>JavaScript tokens:
import { colors, spacing } from '@cloudalt-frontend/theme'
const styles = { background: colors.primary[500], padding: spacing[4],}Brand-Specific Tokens
Section titled “Brand-Specific Tokens”In Tailwind config:
const { createBrandPreset } = require('../../../../packages/config/tailwind-preset')
module.exports = { presets: [createBrandPreset('pinkguest')], content: ['./src/**/*.{js,jsx,ts,tsx}']}At runtime (React):
import { getBrandTheme } from '@cloudalt-frontend/theme/brands'
const theme = getBrandTheme('pinkguest')console.log(theme.colors.primary[500]) // #ec4899Runtime Theming in Storybook
Section titled “Runtime Theming in Storybook”CSS Variables (shadcn components):
- Components using
bg-primary,text-foregroundget themed via CSS vars - Decorator injects vars based on selected brand
Token Scale Classes (other components):
- Components using
bg-primary-500require Tailwind config changes - Partial runtime theming until migrated to variable-based utilities
Testing & Validation
Section titled “Testing & Validation”Unit Tests
Section titled “Unit Tests”import { render, screen } from '@testing-library/react'import { Hero } from './Hero'
test('renders hero with title', () => { render(<Hero title="Test Title" />) expect(screen.getByText('Test Title')).toBeInTheDocument()})Testing After Promotion
Section titled “Testing After Promotion”Update test files along with component:
Section titled “Update test files along with component:”# Move test filemv apps/.../components/ListingCard.test.tsx \ packages/ui-stay-match/src/ListingCard/ListingCard.test.tsx
# Update test imports- import { ListingCard } from './ListingCard';+ import { ListingCard } from '../ListingCard';Run tests for affected projects:
Section titled “Run tests for affected projects:”# Run tests for affected projectsyarn nx affected -t test
# Run specific app testsyarn nx test pinkguest-webVisual Tests (Storybook)
Section titled “Visual Tests (Storybook)”# Run Storybookyarn storybook
# Build static Storybookyarn build-storybook
# Future: Visual regression testingyarn test:visualValidation Commands
Section titled “Validation Commands”# Type checkyarn nx typecheck <project>
# Lint (module boundaries)yarn nx lint <project>
# Testyarn nx test <project>
# Buildyarn nx build <project>
# Check all affected projectsyarn nx affected -t lint test buildBest Practices
Section titled “Best Practices”Component Design
Section titled “Component Design”- Keep core components generic — No brand-specific logic
- Use tokens for styling — Avoid hardcoded colors/spacing
- Export TypeScript types — Enable type-safe overrides
- Document props — Use JSDoc comments
- Write stories — Every component needs a Storybook story
File Organization
Section titled “File Organization”packages/ui-stay-match/├── src/│ ├── components/│ │ ├── Hero/│ │ │ ├── Hero.tsx│ │ │ ├── index.ts│ │ │ └── README.md│ │ └── Button/│ │ ├── Button.tsx│ │ └── index.ts│ ├── types/│ │ └── index.ts│ └── index.ts ← Export everything here├── package.json├── project.json├── tsconfig.json└── README.mdImport Organization
Section titled “Import Organization”// 1. External dependenciesimport React from 'react'import { clsx } from 'clsx'
// 2. Core packagesimport { Button } from '@cloudalt-frontend/ui-web'import { Icon } from '@cloudalt-frontend/icons'
// 3. Division packagesimport { Hero } from '@cloudalt-frontend/ui-stay-match'
// 4. App overrides (last resort)import { CustomHeader } from '@app/components'
// 5. Relative importsimport { helper } from './utils'Dependency Rules
Section titled “Dependency Rules”✅ Apps → Division + Core✅ Division → Core only✅ Core → External libs only
❌ Apps ↔ Apps❌ Division ↔ Division❌ Core → Division❌ Core → AppsPromotion Best Practices
Section titled “Promotion Best Practices”✅ Do:
- Promote when you have 2+ use cases (Rule of Three)
- Make promoted components flexible with props
- Write comprehensive prop documentation
- Add Storybook stories showing variants
- Test in multiple consuming contexts
❌ Don’t:
- Promote prematurely (“we might need it later”)
- Keep app-specific logic in promoted components
- Forget to update imports everywhere
- Skip validation (lint, test, build)
- Promote components with incomplete functionality
Troubleshooting
Section titled “Troubleshooting”TypeScript: Cannot find module ‘@app/components’
Section titled “TypeScript: Cannot find module ‘@app/components’”Cause: tsconfig.app.json missing path mapping.
Fix:
{ "compilerOptions": { "paths": { "@app/components": ["./src/components/index.ts"], "@app/components/*": ["./src/components/*"] } }}Import not working in app
Section titled “Import not working in app”Cause: Component not exported from src/components/index.ts.
Fix:
export { Hero } from './Hero'Storybook not showing brand theme
Section titled “Storybook not showing brand theme”Cause: Check toolbar selection or CSS variable mapping.
Fix: Verify division/app selector is set correctly in toolbar.
Nx lint: Module boundary violation
Section titled “Nx lint: Module boundary violation”Cause: Invalid import (e.g., division importing from another division).
Fix: Refactor to use core components or move shared code to core.
”Cannot find module” errors after promotion
Section titled “”Cannot find module” errors after promotion”Cause: Import paths not updated
Fix:
# Find all importsgrep -r "YourComponent" .
# Update to new path- import { YourComponent } from '@app/components';+ import { YourComponent } from '@cloudalt-frontend/ui-stay-match';Module boundary violations
Section titled “Module boundary violations”Cause: Division UI trying to import from apps
Fix: Check ESLint output and remove invalid imports
yarn nx lint ui-stay-match --verboseType errors after promotion
Section titled “Type errors after promotion”Cause: Type definitions not exported properly
Fix: Check index.ts exports
// Ensure both component and types are exportedexport * from './ListingCard';export type { ListingCardProps } from './ListingCard';Phase 1 Implementation Summary
Section titled “Phase 1 Implementation Summary”Changes Included
Section titled “Changes Included”1. Documentation
Section titled “1. Documentation”Created:
- ✅ Component layering comprehensive documentation
- ✅ Developer quick-start guide with examples
2. Storybook Enhancements
Section titled “2. Storybook Enhancements”File: packages/storybook/.storybook/preview.ts
Added:
- ✅ Division selector toolbar (10 divisions)
- ✅ App selector toolbar (dynamically filtered by division)
- ✅ Brand theming decorator that applies CSS variables based on selection
- ✅ Support for 5 brand themes (pinkguest, orangeguest, purpleguest, rainbowhost, roomlgbt)
How it works:
- User selects Division (e.g., “Stay Match”)
- App selector shows only apps in that division (e.g., “Pink Guest”, “Orange Guest”)
- Decorator applies brand-specific CSS variables (
--primary,--secondary, etc.) - shadcn components automatically reflect the brand theme
- Stories render with the selected brand’s visual style
3. App Override Infrastructure
Section titled “3. App Override Infrastructure”Updated:
- ✅
apps/stay_match/pinkguest/web/tsconfig.app.json— Added@app/componentspath mapping - ✅
apps/roommate/roommate_guru/web/tsconfig.app.json— Added@app/componentspath mapping
Created:
- ✅ Component override directories with index files and READMEs for sample apps
Developer experience:
// Create app-specific overrideimport { Hero as DivisionHero, HeroProps } from '@cloudalt-frontend/ui-stay-match'
export const Hero = (props: HeroProps) => { return <DivisionHero {...props} className="custom-pink" />}
// Use in appimport { Hero } from '@app/components' // ✅ Works!Testing Instructions
Section titled “Testing Instructions”1. Verify Storybook Theming
Section titled “1. Verify Storybook Theming”# Start Storybookyarn storybook
# Test toolbar:# - Select "Division" → "Stay Match"# - Select "App" → "Pink Guest"# - Verify button/card colors are pink# - Switch to "Orange Guest"# - Verify colors change to orange# - Toggle "Theme" → "Dark"# - Verify dark mode works alongside brand theme2. Verify App Alias
Section titled “2. Verify App Alias”# Test pinkguest-webcd apps/stay_match/pinkguest/web
# Verify TypeScript resolvesnpx tsc --noEmit# Should pass with no errors3. Run Lint
Section titled “3. Run Lint”# Check for TypeScript/ESLint errorsnx lint storybooknx lint pinkguest-webnx lint roommate_guru-webSuccess Metrics
Section titled “Success Metrics”- ✅ Storybook shows 10 divisions with correct apps
- ✅ Brand themes apply via CSS variables
- ✅ Two sample apps have working
@app/componentsalias - ✅ Documentation is comprehensive and actionable
- ✅ Zero breaking changes
- ✅ All tests/lint pass
Package Dependencies
Section titled “Package Dependencies”Core Packages
Section titled “Core Packages”packages/ui— Platform-agnostic corepackages/ui-web— Web-specific core (shadcn)packages/common— Shared utilitiespackages/icons— Icon componentspackages/theme— Design tokenspackages/brands— Brand configurationspackages/config— Shared configs
Division Packages
Section titled “Division Packages”packages/ui-stay-matchpackages/ui-altfinderpackages/ui-roommatepackages/ui-guestroompackages/ui-bonjour-servicespackages/ui-greenhostpackages/ui-homestaypackages/ui-pride-citypackages/ui-room-lgbtpackages/ui-hostguest
Further Reading
Section titled “Further Reading”- COMPONENT_LAYERING_WORKORDER.md — Full work order and implementation phases
- STORYBOOK_WORKFLOW.md — Storybook usage guide
- TAILWIND_INTEGRATION_HANDOFF.md — Design tokens and Tailwind
- Nx Documentation — Monorepo tooling
Getting Help
Section titled “Getting Help”- Check existing components in
packages/ui-webandpackages/ui-<division> - Review stories in
packages/storybook/stories - Ask in #frontend-architecture Slack channel
- Refer to this guide:
/docs/COMPONENT_LAYERING_COMPLETE_GUIDE.md
Remember: When in doubt, start with an app override, validate it works, then promote if useful! 🚀
Document History:
- Merged from: COMPONENT_LAYERING_GUIDE.md, COMPONENT_LAYERING_IMPLEMENTATION.md, NX_LAYER_TAGGING.md, COMPONENT_PROMOTION_GUIDE.md, PR_COMPONENT_LAYERING_PHASE1.md
- Last Updated: October 18, 2025