152 lines
4.7 KiB
Markdown
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
|
|
```
|