# Bay Area IT — Website

Marketing website for **Bay Area IT**, an IT service provider based in Corpus Christi, TX.
Built with React 19, Vite 6, TypeScript, and Tailwind CSS. Deployed as a static site served by Caddy inside Docker.

---

## Table of Contents

- [Tech Stack](#tech-stack)
- [Project Structure](#project-structure)
- [Local Development](#local-development)
- [Build & Preview](#build--preview)
- [Deployment (Docker + Caddy)](#deployment-docker--caddy)
- [Adding Content](#adding-content)
- [Scripts Reference](#scripts-reference)

---

## Tech Stack

| Layer | Technology |
|---|---|
| UI Framework | React 19 |
| Build Tool | Vite 6 |
| Language | TypeScript 5 |
| Styling | Tailwind CSS 3 |
| Routing | React Router 7 |
| Animations | Framer Motion 12, GSAP 3 |
| Smooth Scroll | Lenis |
| Web Server | Caddy 2 (inside Docker) |
| Containerization | Docker + Docker Compose |

---

## Project Structure

```
bayarea/
├── components/           # Shared UI components
│   ├── Navbar.tsx
│   ├── Footer.tsx
│   ├── Hero.tsx
│   ├── Services.tsx
│   ├── Blog.tsx
│   ├── CTA.tsx
│   ├── Testimonials.tsx
│   ├── Process.tsx
│   ├── AreasWeServe.tsx
│   ├── SEO.tsx           # Head/meta tag injection
│   ├── Breadcrumb.tsx
│   └── LoadingScreen.tsx
├── src/
│   ├── pages/            # Route-level page components
│   │   ├── HomePage.tsx
│   │   ├── AboutPage.tsx
│   │   ├── ServicesPage.tsx
│   │   ├── ServicePage.tsx       # Dynamic: /services/:slug
│   │   ├── BlogPage.tsx
│   │   ├── BlogPostPage.tsx      # Dynamic: /blog/:slug
│   │   ├── LocationPage.tsx      # Dynamic: /locations/:slug
│   │   ├── LocationsPage.tsx
│   │   ├── ContactPage.tsx
│   │   ├── PrivacyPolicyPage.tsx
│   │   ├── TermsOfServicePage.tsx
│   │   └── LegalPage.tsx
│   ├── data/
│   │   └── seoData.ts    # All location, service, and blog post data
│   ├── routes/           # Route definitions
│   └── index.css         # Global styles
├── scripts/
│   ├── prerender-routes.ts   # Generates per-route index.html files
│   ├── prune-dist-assets.mjs # Removes unused assets from dist/
│   ├── generate-sitemap.ts   # Generates public/sitemap.xml
│   ├── generate-robots.ts    # Generates public/robots.txt
│   └── optimize-images.mjs   # Converts images to WebP
├── public/
│   ├── assets/services/  # Service page images (.webp)
│   ├── images/blog/      # Blog post images (.webp)
│   ├── sitemap.xml
│   └── robots.txt
├── Dockerfile            # Multi-stage: Node build → Caddy serve
├── docker-compose.yml    # Runs the container on localhost:8080
├── Caddyfile             # Caddy config inside the container
├── caddy-host.snippet    # Paste this into your host Caddy config
├── tailwind.config.cjs
├── postcss.config.cjs
├── vite.config.ts
└── tsconfig.json
```

---

## Local Development

### Prerequisites

- Node.js 22+ (or use [nvm](https://github.com/nvm-sh/nvm))
- npm

### Setup

```bash
# Install dependencies
npm install

# Start the dev server
npm run dev
```

The app runs at **http://localhost:3012**

Hot Module Replacement (HMR) is enabled. If port 3012 is taken, Vite picks the next available port automatically.

---

## Build & Preview

```bash
# Production build (Vite + prerender + asset pruning)
npm run build

# Preview the production build locally
npm run preview
```

The build pipeline runs three steps in sequence:

1. **`vite build`** — bundles and outputs to `dist/`
2. **`prune-dist-assets.mjs`** — removes any unreferenced files from `dist/assets/`
3. **`prerender-routes.ts`** — writes a unique `index.html` per route into `dist/` with correct `<title>`, `<meta>`, canonical URLs, Open Graph tags, and JSON-LD schema

The result is a fully static site where every URL has its own HTML file — no server-side rendering required.

---

## Deployment (Docker + Caddy)

This project uses a **two-Caddy setup**:

```
Internet
  └── Host Caddy  (HTTPS, port 443)
        └── Docker container  (internal port 80 → host localhost:8080)
              └── Caddy inside container
                    └── /srv  (built static files)
```

### Step 1 — Build and start the container

On your server, copy the project files (or clone the repo), then run:

```bash
docker compose up -d --build
```

This builds the image (Node 22 compiles the app, Caddy serves it) and starts the container.
The app is now available at **http://localhost:8080** — only reachable from the server itself.

To verify:

```bash
curl -I http://localhost:8080
# HTTP/1.1 200 OK
```

### Step 2 — Configure the host Caddy

Add the following to your host Caddy config (usually `/etc/caddy/Caddyfile`).
A ready-to-paste version is in `caddy-host.snippet`:

```caddy
bayareait.services, www.bayareait.services {
    encode zstd gzip
    reverse_proxy localhost:8080

    @www host www.bayareait.services
    redir @www https://bayareait.services{uri} permanent
}
```

Replace `bayareait.services` with your actual domain.

### Step 3 — Reload host Caddy

```bash
sudo systemctl reload caddy
# or
caddy reload --config /etc/caddy/Caddyfile
```

Caddy automatically obtains and renews an SSL certificate from Let's Encrypt — no manual certificate management needed.

### Updating the site

```bash
# Pull latest changes, rebuild, restart
git pull
docker compose up -d --build
```

Zero-downtime: Docker Compose replaces the old container while the new one starts.

### Useful Docker commands

```bash
# View logs
docker compose logs -f

# Stop the container
docker compose down

# Rebuild without cache
docker compose build --no-cache
docker compose up -d
```

---

## Adding Content

All page content is driven by **`src/data/seoData.ts`**. No new page files needed for standard locations, services, or blog posts — just add a new entry to the right array.

### Add a location page

```ts
// src/data/seoData.ts → locationData array
{
  city: "Sinton",
  slug: "locations/it-support-sinton",
  title: "IT Support Sinton, TX | Bay Area IT",
  description: "...",
  h1: "IT Support for Businesses in Sinton, TX",
  keywords: ["IT support Sinton", "..."],
  content: `<p>...</p>`,
  faq: [
    { question: "...", answer: "..." }
  ]
}
```

### Add a service page

```ts
// src/data/seoData.ts → serviceData array
{
  id: "9",
  slug: "services/cloud-backup",
  title: "Cloud Backup Services | Bay Area IT",
  description: "...",
  h1: "Cloud Backup for Corpus Christi Businesses",
  keywords: ["cloud backup", "..."],
  content: `<p>...</p>`,
  faq: []
}
```

### Add a blog post

```ts
// src/data/seoData.ts → blogPostData array
{
  slug: "blog/your-post-slug",
  title: "Your Post Title | Bay Area IT",
  description: "...",
  h1: "Your Post Heading",
  keywords: ["..."],
  content: `<p>...</p>`,
  date: "2026-03-25",
  image: "/images/blog/your-image.webp"
}
```

After adding content, rebuild:

```bash
npm run build
# or on the server:
docker compose up -d --build
```

---

## Scripts Reference

| Script | Command | Description |
|---|---|---|
| Dev server | `npm run dev` | Starts Vite dev server on port 3012 |
| Production build | `npm run build` | Vite build + asset pruning + prerendering |
| Preview build | `npm run preview` | Serves `dist/` locally via Vite |
| Generate sitemap | `npm run generate:seo` | Writes `public/sitemap.xml` and `public/robots.txt` |
| Optimize images | `npm run optimize:images` | Converts images in `public/` to WebP |
| Prerender only | `npm run prerender:routes` | Re-runs route prerendering on existing `dist/` |
