# 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 ``, `<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/` |