Files
cmsn-b-linh/docs/plans/2026-03-11-birthday-roast-gallery-design.md

152 lines
4.7 KiB
Markdown

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