4.7 KiB
4.7 KiB
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:
[
{ "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)
# 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