Skip to content

Component Architecture - Three-Tier System

Date: October 11, 2025
Status: βœ… Implemented
Branch: feat/tailwind-integration

This monorepo uses a three-tier component architecture to organize UI code by reusability and scope:

1. MONOREPO-WIDE (packages/ui/) β†’ Everyone uses
2. DIVISION-SPECIFIC (packages/ui-{division}/) β†’ Division apps use
3. APP-SPECIFIC (apps/{division}/{brand}/src/components/) β†’ Single app uses

monorepo/
β”œβ”€β”€ packages/ # REUSABLE LIBRARIES
β”‚ β”‚
β”‚ β”œβ”€β”€ ui/ # Tier 1: Monorepo-wide
β”‚ β”‚ └── src/
β”‚ β”‚ └── components/
β”‚ β”‚ β”œβ”€β”€ Button/ # Generic button
β”‚ β”‚ β”œβ”€β”€ Input/ # Generic input
β”‚ β”‚ β”œβ”€β”€ Modal/ # Generic modal
β”‚ β”‚ β”œβ”€β”€ Slider/ # Generic slider
β”‚ β”‚ └── navigation/ # Base navigation primitives
β”‚ β”‚
β”‚ β”œβ”€β”€ ui-stay-match/ # Tier 2: Stay Match division
β”‚ β”‚ └── src/
β”‚ β”‚ └── components/
β”‚ β”‚ β”œβ”€β”€ Hero/ # Stay Match hero pattern
β”‚ β”‚ └── Navigation/ # Stay Match navigation
β”‚ β”‚
β”‚ β”œβ”€β”€ ui-guestroom/ # Tier 2: Guestroom division (future)
β”‚ β”‚ └── src/
β”‚ β”‚ └── components/
β”‚ β”‚
β”‚ └── ui-homestay/ # Tier 2: Homestay division (future)
β”‚ └── src/
β”‚ └── components/
β”‚
└── apps/ # APPLICATIONS
└── stay_match/
└── pinkguest/
β”œβ”€β”€ web/
β”‚ └── src/
β”‚ └── components/ # Tier 3: Pinkguest web only
β”‚
β”œβ”€β”€ mobile/
β”‚ └── src/
β”‚ └── components/ # Tier 3: Pinkguest mobile only
β”‚
└── shared/ # Brand assets (not components!)
β”œβ”€β”€ assets/images/ # Images
└── constants/ # Content/copy

Use when:

  • Component is used across ALL divisions (Stay Match, Guestroom, Homestay, etc.)
  • Zero business logic
  • Pure presentation
  • Generic and universal

Examples:

  • Button - Every app needs buttons
  • Input - Every app has inputs
  • Modal - Every app shows modals
  • Tooltip - Every app has tooltips
  • Slider - Generic slider component
  • Base navigation primitives (BottomNavigation, HorizontalTabs)

Package name: @cloudalt-frontend/ui

Import:

import { Button, Input, Modal } from '@cloudalt-frontend/ui';

Use when:

  • Component is used across multiple apps in one division
  • Implements division-specific UI patterns
  • Shared within division, not across divisions

Examples for Stay Match:

  • Hero - Landing page hero (shared pattern across pinkguest, orangeguest, roomlgbt)
  • StayMatchNavigation - Navigation specific to Stay Match apps
  • SearchBar - Stay Match-specific search patterns
  • ListingCard - How Stay Match shows listings

Examples for Guestroom (future):

  • GuestroomHero - Different hero pattern than Stay Match
  • GuestroomSearch - Different search UX

Package names:

  • @cloudalt-frontend/ui-stay-match
  • @cloudalt-frontend/ui-guestroom
  • @cloudalt-frontend/ui-homestay

Import:

import { Hero, StayMatchNavigation } from '@cloudalt-frontend/ui-stay-match';

Tier 3: App-Specific (apps/{division}/{brand}/{platform}/src/components/)

Section titled β€œTier 3: App-Specific (apps/{division}/{brand}/{platform}/src/components/)”

Use when:

  • Component is used ONLY in this specific app
  • Has business logic specific to this app/platform
  • Too unique to share

Examples for pinkguest web:

  • PinkguestCheckoutFlow - Pinkguest-specific checkout
  • PinkguestProfileSettings - Unique settings page
  • PinkguestDashboard - Brand-specific dashboard

Examples for pinkguest mobile:

  • MobileOnboarding - Mobile-specific onboarding flow
  • CameraIntegration - Mobile camera features

Location:

  • apps/stay_match/pinkguest/web/src/components/
  • apps/stay_match/pinkguest/mobile/src/components/

Import:

// Direct relative import within the app
import { CheckoutFlow } from '../components/CheckoutFlow';

Question: Where should this component live?
Is it used in multiple apps?
β”‚
β”œβ”€ NO ──→ apps/{division}/{brand}/{platform}/src/components/
β”‚ (App-specific, keep it there)
β”‚
└─ YES
β”‚
└─ Is it used across divisions?
β”‚
β”œβ”€ YES ──→ packages/ui/
β”‚ (Everyone needs it, make it generic)
β”‚
└─ NO ──→ Multiple apps in same division?
β”‚
└─ YES ──→ packages/ui-{division}/
(Division-specific pattern)

ComponentTierPackageUsed By
Button1packages/ui/All apps everywhere
Input1packages/ui/All apps everywhere
Modal1packages/ui/All apps everywhere
Hero2packages/ui-stay-match/All Stay Match apps
StayMatchNavigation2packages/ui-stay-match/All Stay Match apps
GuestroomHero2packages/ui-guestroom/All Guestroom apps
CheckoutFlow3apps/stay_match/pinkguest/web/Pinkguest web only
ProfileSettings3apps/stay_match/pinkguest/mobile/Pinkguest mobile only

❌ DON’T:

Build Hero in packages/ui/ "because we might need it later"

βœ… DO:

1. Build Hero in apps/stay_match/pinkguest/web/src/components/
2. Orangeguest needs it? β†’ Extract to packages/ui-stay-match/
3. Guestroom needs similar? β†’ They build their own in packages/ui-guestroom/
4. Everyone needs the pattern? β†’ Extract common parts to packages/ui/

Only move components to packages when you have real duplication, not anticipated future use.

Stay Match and Guestroom can have different implementations of similar components. Don’t force them to share unless truly generic.

Brand-specific images and content go in apps/{division}/{brand}/shared/, not in packages.


When extracting a component from app to package:

  1. Copy component to new package location
  2. Update imports in the package
  3. Update consuming apps to import from package
  4. Test that everything still works
  5. Delete original app-specific component
  6. Update documentation

  • βœ… packages/ui/ - Monorepo-wide components
  • βœ… packages/ui-stay-match/ - Stay Match division package
  • βœ… StayMatchNavigation migrated to ui-stay-match
  • βœ… Hero component placeholder in ui-stay-match
  • βœ… Brand assets structure: apps/stay_match/pinkguest/shared/
  • ⏳ packages/ui-guestroom/ - When Guestroom apps need shared components
  • ⏳ packages/ui-homestay/ - When Homestay apps need shared components
  • ⏳ Extract more components to ui-stay-match as duplication emerges

Icons and images that differ per brand belong in:


Q: Should generic components go in ui or ui-stay-match?
A: If ALL divisions will use it β†’ packages/ui/. If only Stay Match β†’ packages/ui-stay-match/.

Q: Can I import from ui-guestroom in a Stay Match app?
A: Technically yes, but don’t. Each division should be independent.

Q: What if pinkguest and orangeguest have slight variations of a component?
A: Put the shared pattern in ui-stay-match, pass brand-specific props or use design tokens for styling differences.

Q: Should I create ui-{division} packages for all divisions now?
A: No! Only create them when you have real components to share. Start with app-specific components first.



Last Updated: October 11, 2025
Maintained By: Engineering Team