Skip to content

Onboarding Guide

Welcome to the cloudalt-frontend! This guide will help you get started with the project.

  1. Clone the repository:

    Terminal window
    git clone <repository-url>
    cd cloudalt-frontend
  2. Install dependencies:

    Terminal window
    yarn install

    Important: All dependencies are centrally managed in the root package.json. Individual mobile and web apps no longer maintain their own dependencies or devDependencies. This ensures consistency across the monorepo and eliminates redundant installations.

This is an Nx workspace. Here’s a brief overview of the directory structure:

  • apps/: Contains all the applications.
    • Each application is further divided into mobile (React Native/Expo) and web (React/Vite) projects.
  • packages/: Contains shared libraries and packages used across applications.
  • docs/: Contains documentation.
  • scripts/: Contains utility scripts, including:
    • verify-repo.sh: Runs comprehensive workspace validation (lint, typecheck, build, cycle detection)
    • check-nested-node_modules.sh: Safeguard to prevent nested dependency installations

All dependencies are centralized in the root package.json. Individual app package.json files contain only:

  • name
  • version
  • private
  • scripts

This architecture ensures:

  • Single source of truth for all package versions
  • Faster installations (no redundant node_modules)
  • Consistent dependency resolution across all apps
  • Reduced repository size

⚠️ Never add dependencies or devDependencies to individual app package.json files. All packages must be added to the root.

To run an application, use the nx command:

Terminal window
# To run a web application
yarn nx serve <app-name>-web
# To run a mobile application
yarn nx start <app-name>-mobile

For example, to run the pinkguest-web application:

Terminal window
yarn nx serve pinkguest-web

And to run the pinkguest-mobile application:

Terminal window
yarn nx start pinkguest-mobile
### Web (Vite) specifics
To keep web apps fast and isolated from React Native internals, ensure the following in each web app (e.g. `apps/.../web/`):
- Tailwind CSS v3 PostCSS plugins in `postcss.config.js`:
```js
// Tailwind v3 standard
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
  • Vite aliases to prevent RN code from entering the web bundle and to force the web entry of shared UI:

    vite.config.ts
    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'],
    },
    • Ensure a serve target exists in project.json using @nx/vite:dev-server so you can run:
    Terminal window
    yarn nx serve <app-name>-web
## Building Applications
To build an application for production, use the `nx` command:
```bash
# To build a web application
yarn nx build <app-name>-web
# To build a mobile application
# (Refer to Expo documentation for building and deploying mobile apps)

To run tests for a specific project, use the nx command:

Terminal window
yarn nx test <project-name>

To get more help on the Nx CLI, check out the Nx documentation.


The monorepo includes automated verification scripts to ensure code quality, dependency integrity, and build consistency.

To run the full verification suite:

Terminal window
yarn verify
# Or directly:
./scripts/verify-repo.sh

This runs the following stages:

  1. InstallIntegrity: Verifies yarn install succeeded without errors
  2. RegistrySchemaValidation: Validates packages/brands/registry.json against schema
  3. BrandTypesCheck: Ensures brand types are up-to-date (not stale)
  4. VitePathValidation: Checks Vite path configurations
  5. ProjectTargets: Validates project.json targets
  6. Lint: Runs eslint across the workspace
  7. Typecheck: Runs TypeScript type checking
  8. Build: Builds all packages
  9. UITreeShake: Verifies web bundle doesn’t include React Native code
  10. SizeThresholds: Checks bundle sizes against configured thresholds
  11. BundleScan: (Placeholder for future bundle analysis)

Output: Verification results are saved to .verify/ directory:

  • .verify/full-output.log: Complete verification log
  • .verify/summary.json: Machine-readable summary with timings and status
  • .verify/size-results.json: Bundle size measurements

Tip: Use jq to query the summary JSON:

Terminal window
# Check which stages failed
jq '.stages[] | select(.status == "error")' .verify/summary.json
# Get total verification time
jq '.duration.seconds' .verify/summary.json
# Check specific stage timing
jq '.stages[] | select(.name == "Build") | .duration' .verify/summary.json

Run this whenever you:

  • add or modify Tailwind configs (app or Storybook)
  • add a new UI package that uses Tailwind
  • see styling differences between Storybook and an app
Terminal window
yarn tailwind:guard

It fails the run if Tailwind versions drift, v4-only artifacts are present, or configs don’t use the expected monorepo patterns.

Generates TypeScript types and constants from the brand registry:

Terminal window
yarn generate-brand-types
# Or directly:
node scripts/generate-brand-types.mjs

Output:

  • packages/brands/src/generated-types.ts: Brand ID types and constants
  • packages/brands/src/generated-constants.ts: ALL_BRAND_IDS array

When to run:

  • After adding/removing brands in packages/brands/registry.json
  • After modifying brand properties

Stale detection: The verify script automatically detects stale types. If types are out of date, verification fails with a clear error message.

Generates per-brand README files with auto-generated and custom sections:

Terminal window
# Generate/update all brand READMEs
node scripts/generate-brand-readme.mjs
# Check mode (dry-run)
node scripts/generate-brand-readme.mjs --check
# Force overwrite (for migration)
node scripts/generate-brand-readme.mjs --force-overwrite

Protected Regions:

  • <!-- AUTO-GENERATED --> to <!-- END AUTO-GENERATED SECTION -->: Regenerated on every run
  • Custom content below auto-generated section: Preserved across regenerations

Output: apps/{family}/{brand}/mobile/README.md for each brand

Fail-fast schema validation for the brand registry:

Terminal window
node scripts/validate-brand-registry.mjs

Exits with code 1 if registry.json doesn’t match the Zod schema. This runs automatically in the verify pipeline.

Verifies that the web build of @cloudalt/ui doesn’t include React Native code:

Terminal window
node scripts/check-ui-tree-shake.mjs

What it checks:

  • Web bundle (packages/ui/dist/index.web.js) for React Native imports
  • Native bundle size vs web bundle size
  • Reports size savings percentage

Expected: Web bundle should be ~50% smaller than native bundle

Monitors bundle sizes and enforces regression guards:

Terminal window
# Check sizes (warns on violations)
node scripts/check-size-thresholds.mjs
# Strict mode (fails on violations, for CI)
node scripts/check-size-thresholds.mjs --strict

Configuration: size-thresholds.json defines per-package limits:

{
"packages/brands": {
"softLimit": 40000,
"hardLimit": 50000
}
}

Output: Results saved to .verify/size-results.json

Compares build tool performance (tsc vs tsup):

Terminal window
node scripts/benchmark-build-tools.mjs

Findings from M5:

  • tsc --emitDeclarationOnly is 44-138% faster than tsup
  • Recommendation: Use tsc for declaration-only builds

Critical Naming Convention for Mobile Apps

Section titled “Critical Naming Convention for Mobile Apps”

Problem: The default Nx Expo generator (@nx/expo:app) creates mobile applications with a generic package name ("name": "mobile") in their package.json file. This creates fatal Duplicate workspace name errors when running yarn install in a properly configured monorepo.

Policy: To prevent this, the following naming convention must be followed for all new mobile applications:

  1. Package Name (package.json): The name must be unique across the entire workspace and follow the format app-name-platform.

    • Example: For the staymatch_app mobile app, the name must be staymatch-app-mobile.
    apps/stay_match/staymatch_app/mobile/package.json
    {
    "name": "staymatch-app-mobile",
    // ...
    }
  2. App Name and Slug (app.json): The name and slug fields in the app.json file must also be updated to match the unique package name.

    apps/stay_match/staymatch_app/mobile/app.json
    {
    "expo": {
    "name": "staymatch-app-mobile",
    "slug": "staymatch-app-mobile",
    // ...
    }
    }

This workspace has been configured to resolve several common dependency and peer dependency conflicts. Here are some key things to be aware of:

  • Problem: The version of @swc/core used by Nx and other tools can conflict with the version required by packages like @swc-node/register. This results in YN0060 peer dependency warnings during yarn install.
  • Resolution: The root package.json has been updated to use a specific version of @swc/core (^1.13.3) that satisfies all consumers. If you encounter this issue again, ensure the versions are aligned in the root package.json.
  • Problem: Mobile applications generated with @nx/expo may not include react and react-dom in their package.json, leading to peer dependency issues.
  • Resolution: All mobile applications must have react and react-dom listed as dependencies. The versions have been standardized to ~18.3.1 across the workspace.
  • Problem: The SafeAreaView component from react-native is deprecated and can cause layout issues.

  • Resolution: Always import SafeAreaView from react-native-safe-area-context. If the dependency is not present in a mobile app’s package.json, add it.

    // Correct usage
    import { SafeAreaView } from 'react-native-safe-area-context';

4. Workspace Integrity and Dependency Management

Section titled “4. Workspace Integrity and Dependency Management”
  • Problem: Incorrectly running yarn install or other package management commands from within an individual application’s directory (e.g., apps/some-app/mobile/) instead of the monorepo root can create nested node_modules folders. This leads to unpredictable builds, dependency conflicts, and errors like ENOENT: no such file or directory, uv_cwd.
  • Policy: All package management commands must be executed from the monorepo root (/Users/work-station/company/cloudalt/cloudalt-frontend). There should only be one node_modules directory at the top level of the workspace.
  • Verification and Cleanup Procedure:
    1. To ensure workspace integrity, periodically verify that no nested node_modules directories exist.
    2. Run the following command from the monorepo root to find any incorrect installations:
      Terminal window
      find . -name "node_modules" -type d -maxdepth 3
    3. If this command returns any path other than ./node_modules, those directories are incorrect and must be deleted immediately. For example:
      Terminal window
      # This indicates a problem
      ./apps/some-app/mobile/node_modules
      # To fix, run:
      rm -rf ./apps/some-app/mobile/node_modules
    4. After cleanup, run a single yarn install from the monorepo root to ensure the workspace is in a correct and stable state.

Xcode Environment Setup: Offloading Data Directories to External SSD

Section titled “Xcode Environment Setup: Offloading Data Directories to External SSD”

Note for Developers & GitHub Copilot:

We have relocated Xcode’s build artifacts, cache directories, and archives from the default system drive to an external SSD (T7). This change was implemented to optimize disk usage and improve overall build performance.

  • DerivedData is now stored at: /Volumes/T7/XcodeDerivedData
  • Archives are now stored at: /Volumes/T7/XcodeArchives
  • CompilationCache.noindex is now stored at: /Volumes/T7/XcodeCompilationCache.noindex

This setup uses symbolic links, so Xcode continues to reference the usual ~/Library/Developer/Xcode/ folders, while files are physically stored on the external drive. Future build, cache, and archive data will be preserved externally.

Important: Please ensure that the T7 drive is always mounted and available when working in Xcode, or resolving build and archive artifacts. If the drive is not present, Xcode will be unable to access these directories.

This setup benefits disk space and performance for larger projects and is safe for day-to-day development workflows.


All assets in the monorepo follow a two-tier organization system to optimize bundle sizes and maintain clear ownership boundaries.

Use for: Generic icons, illustrations, and company branding used across ALL apps.

Structure:

packages/assets/
├── icons/
│ ├── arrows/ # Navigation arrows
│ ├── actions/ # Action icons (plus, trash, edit)
│ ├── navigation/ # Nav icons (home, menu, back)
│ └── social/ # Social media icons
├── illustrations/ # Generic empty states, errors
└── brand/logo/ # CloudAlt company logo

Guidelines:

  • Keep this directory minimal - assets here are bundled with ALL apps
  • Only generic, truly universal assets belong here
  • SVG format preferred for icons (< 5KB each)
  • All assets must work for every brand

Brand-Specific Assets (apps/{division}/{brand}/shared/assets/)

Section titled “Brand-Specific Assets (apps/{division}/{brand}/shared/assets/)”

Use for: Images, fonts, and assets unique to a specific brand.

Structure:

apps/{division}/{brand}/shared/assets/
├── images/
│ ├── hero/ # Hero/banner images
│ ├── logos/ # Brand logos and wordmarks
│ ├── features/ # Feature showcase images
│ ├── onboarding/ # Tutorial and onboarding images
│ ├── avatars/ # Default avatars, placeholders
│ └── misc/ # Other brand-specific images
├── fonts/ # Custom brand fonts (if needed)
└── config/ # Brand-specific configuration

Image Guidelines by Folder:

  • hero/ - Landing page backgrounds

    • Target: < 300KB (optimized)
    • Format: WebP (web), PNG/JPG (mobile)
    • Naming: hero-background.png, hero-background@2x.png
  • logos/ - Brand logos

    • Target: < 25KB
    • Format: SVG (preferred), PNG with transparency
    • Naming: logo.png, logo-white.png, logo-icon.png
  • features/ - Feature screenshots

    • Target: < 150KB each
    • Format: WebP (web), PNG/JPG (mobile)
    • Naming: feature-search.png, feature-messaging.png
  • onboarding/ - Tutorial images

    • Target: < 100KB each
    • Naming: onboarding-step-1.png, welcome.png
    • Use sequential numbering for multi-step flows
  • avatars/ - Default avatars

    • Target: < 50KB each
    • Consistent dimensions (e.g., 200x200)
    • Naming: default-avatar.png, placeholder-user.png
  • misc/ - Other images

    • Images that don’t fit other categories
    • Use descriptive names

Use the automated script to create the standard asset structure:

Terminal window
# Set up asset folders for all brands
scripts/setup-brand-assets.sh
# Or manually create for a specific brand:
mkdir -p apps/{division}/{brand}/shared/assets/images/{hero,logos,features,onboarding,avatars,misc}

Web:

// Generic icon from packages/assets
import ArrowRight from '@cloudalt-frontend/assets/icons/arrows/arrow-right.svg';
// Brand-specific hero image
import heroImage from '../../shared/assets/images/hero/hero-background.png';

Mobile:

// Generic icon from packages/assets
import ArrowRight from '@cloudalt-frontend/assets/icons/arrows/arrow-right.svg';
// Brand-specific hero image
const heroImage = require('../../shared/assets/images/hero/hero-background.png');

Before committing images:

  • squoosh.app - Web-based image compressor
  • tinypng.com - PNG/JPG compression
  • imageoptim.com - macOS app for optimization
  • SVGO - SVG optimization