5.5 KiB
Smash or Pass — Frontend
Vue 3 + TypeScript + Vite + Pinia + Tailwind CSS v4. Talks to the sop-back FastAPI server.
See ../CLAUDE.md for full architecture notes.
Stack
- Vue 3 with
<script setup lang="ts">(TypeScript everywhere) - Vite 8 (dev server + bundler)
- Vue Router (SPA:
/,/game/:id,/summary) - Pinia (game state: queue / smashed / passed)
- Tailwind CSS v4 via
@tailwindcss/vite. Custom palette + fonts insrc/assets/main.cssinside an@theme { ... }block.
Project layout
sop-front/
├── index.html
├── vite.config.ts # Tailwind v4 plugin + alias '@'
├── nginx.conf # SPA fallback for production image
├── Dockerfile # multi-stage: vite build → nginx
└── src/
├── main.ts # imports ./assets/main.css
├── App.vue # only <RouterView />
├── router/index.ts
├── api/client.ts # typed fetch wrapper, reads VITE_API_BASE_URL
├── stores/game.ts # Pinia store
├── assets/main.css # Tailwind + @theme tokens (palette, fonts)
├── components/
│ ├── SwipeCard.vue # pointer-event drag, exposes triggerSmash/Pass
│ └── AdminPanel.vue
└── views/
├── HomeView.vue
├── GameView.vue
└── SummaryView.vue
Configuration
One environment variable, build-time (Vite inlines it):
| Var | Default | Notes |
|---|---|---|
VITE_API_BASE_URL |
http://localhost:8000 |
Backend origin |
For dev, set in a .env.local:
VITE_API_BASE_URL=http://localhost:8000
For Docker builds, pass it as a build arg (already wired in the repo-root docker-compose.yml):
frontend:
build:
context: ./sop-front
args:
VITE_API_BASE_URL: "https://api.example.com"
Local development
cd sop-front
npm install
npm run dev
- App: http://localhost:5173
- Make sure the backend is running at
VITE_API_BASE_URL(defaulthttp://localhost:8000).
Other scripts
| Command | What |
|---|---|
npm run dev |
Vite dev server with HMR |
npm run build |
Type-check + production build into dist/ |
npm run type-check |
vue-tsc --build only |
npm run preview |
Serve the built dist/ locally |
npm run lint |
oxlint + eslint |
npm run format |
Prettier |
npm run test:unit |
Vitest |
Customizing colors / fonts
All design tokens live in src/assets/main.css under @theme. The palette is graded (Tailwind-style): each hue has a 50–900 scale, with 500 as the canonical brand value. Add new shades there; don't hardcode hex values in components.
Scales (50 lightest → 900/950 darkest)
| Family | 500 (brand) | Use |
|---|---|---|
--color-purple-{50…900} |
#8324DE |
Primary brand |
--color-red-{50…900} |
#FF453B |
Pass / errors / destructive |
--color-lime-{50…900} |
#B9D532 |
Smash / success |
--color-neutral-{50…950} |
#63586E |
Backgrounds, surfaces, text, borders |
Components reference shades like var(--color-purple-300), var(--color-neutral-800), etc. The neutral scale handles dark-mode surfaces (50 = off-white text, 950 = #16141A background).
Semantic aliases (prefer these in components)
These point at scale values — change them once in main.css to retheme:
| Alias | → Scale | Role |
|---|---|---|
--color-primary |
purple-500 |
CTAs / brand |
--color-primary-hover |
purple-400 |
CTA hover |
--color-smash |
lime-500 |
Smash action |
--color-pass |
red-500 |
Pass action |
--color-bg |
neutral-950 |
App background |
--color-surface |
neutral-900 |
Card surface |
--color-surface-2 |
neutral-800 |
Elevated surface |
--color-border |
neutral-700 |
Borders |
--color-text |
neutral-50 |
Primary text |
--color-text-muted |
neutral-300 |
Secondary text |
Fonts: Bebas Neue (display) + Roboto (body), loaded via Google Fonts in main.css.
Production deployment
Docker (recommended)
A multi-stage Dockerfile builds the SPA with Vite and serves the static bundle with nginx. From the repo root:
docker compose up -d --build frontend
The image listens on port 80 inside the container; the compose file maps it to host :8080.
For a real deployment:
- Set
VITE_API_BASE_URLto the public backend URL at build time (build arg, not runtime env). Rebuild the image whenever it changes. - Front it with HTTPS — Caddy / Traefik / Cloudflare in front of port 80.
- The bundled
nginx.confalready does SPA fallback (try_files $uri /index.html). - Make sure the backend CORS
ALLOWED_ORIGINSincludes the frontend's public origin.
Static hosting (no Docker)
npm install
VITE_API_BASE_URL=https://api.example.com npm run build
Upload the contents of dist/ to any static host (S3+CloudFront, Netlify, Vercel, GitHub Pages, etc.). Configure SPA fallback so any unknown path serves index.html.
Notes
VITE_API_BASE_URLis baked into the JS bundle at build time. Changing it requires a rebuild.- All API calls go through
src/api/client.ts— extend that wrapper rather than callingfetchdirectly from components. - The admin panel renders only when
GET /admin/statusreturnsadmin_enabled: true. That flag lives in the backend's.env.