# Birthday Roast Gallery - Design Document **Date:** 2026-03-11 **Project:** Birthday meme website for Linh **Domain:** cmsn-b-linh.duylai.duckdns.org ## Overview A static birthday roast gallery website featuring embarrassing photos with funny Vietnamese captions. Built with Vite + React + Tailwind, containerized with Nginx, and deployed to Kubernetes. ## Architecture ``` ┌─────────────────────────────────────────────────────────┐ │ Build Stage │ │ Node.js image → npm install → Vite build → dist/ │ └─────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ Runtime Stage │ │ Nginx:alpine → copy dist/ → serve on port 80 │ └─────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ Kubernetes │ │ Deployment → Service → Ingress (Traefik) │ │ cmsn-b-linh.duylai.duckdns.org │ └─────────────────────────────────────────────────────────┘ ``` ## Tech Stack | Layer | Technology | |-------|------------| | Frontend | Vite + React 18 | | Styling | Tailwind CSS | | Animation | canvas-confetti | | Container | Multi-stage Dockerfile → nginx:alpine | | Orchestration | Kubernetes with Traefik ingress | ## Frontend Components ``` App.jsx ├── BirthdayBanner.jsx # Hero with name + roast message + confetti └── RoastGallery.jsx # Grid of photos with captions └── RoastCard.jsx # Individual photo + caption ``` ### BirthdayBanner - Large "Happy Birthday [Name]!" heading - Roast subtitle message - Confetti animation on page load - Click-to-trigger more confetti ### RoastGallery - Responsive CSS grid (2-3 cols mobile, 4-5 cols desktop) - Cards with hover effects ### RoastCard - Image with aspect ratio - Caption below or overlay - Hover animation (scale/shadow) ## Static Assets Images stored in `public/images/` with captions configured in `public/roasts.json`: ```json [ { "image": "roast-1.jpg", "caption": "Khoảnh khắc huyền thoại 😂" }, { "image": "roast-2.jpg", "caption": "Gương mặt khi quên đóng bài tập" } ] ``` - UTF-8 encoding for Vietnamese captions - Add photos: drop file → add JSON entry → rebuild ## Container Setup ### Dockerfile (multi-stage) ```dockerfile # Build stage FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Runtime stage FROM nginx:alpine COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 ``` ### nginx.conf - Static file serving - Gzip compression - SPA fallback ## Kubernetes Deployment ``` k8s/ ├── deployment.yaml # Nginx pod, 1-2 replicas ├── service.yaml # ClusterIP on port 80 └── ingress.yaml # Traefik ingress for cmsn-b-linh.duylai.duckdns.org ``` ### Deployment - Image: registry/cmsn-b-linh:latest - Replicas: 1 - Resources: minimal (50m CPU, 64Mi RAM) ### Ingress (Traefik) - Host: cmsn-b-linh.duylai.duckdns.org - Traefik-compatible annotations - Optional: TLS via cert-manager ## File Structure ``` cmsn/ ├── src/ │ ├── App.jsx │ ├── main.jsx │ ├── components/ │ │ ├── BirthdayBanner.jsx │ │ ├── RoastGallery.jsx │ │ └── RoastCard.jsx │ └── index.css ├── public/ │ ├── images/ # Add photos here │ └── roasts.json # Caption config ├── index.html ├── package.json ├── vite.config.js ├── tailwind.config.js ├── postcss.config.js ├── Dockerfile ├── nginx.conf └── k8s/ ├── deployment.yaml ├── service.yaml └── ingress.yaml ```