Bio portfolio (static)
Single-page portfolio aligned with PRD/PRDs/Bio Site PRD.md: Vue 3, Vite, Tailwind, GSAP scroll reveals, static dist/ output for free static hosts.
Prerequisites
- Node.js 20+ (22 recommended for CI)
Local development
npm install
npm run dev
Open the URL Vite prints (usually http://localhost:5173).
Production build
npm run build
npm run preview
Artifacts are written to dist/. Deploy that folder to Cloudflare Pages, Netlify, Vercel, or GitHub Pages.
Docker (static site in a container)
The app is still a static build; the container only runs nginx to serve dist/. No Node at runtime.
Build and run
docker build -t bio-site .
docker run --rm -p 8080:80 bio-site
Open http://localhost:8080.
Compose (handy defaults)
docker compose up -d --build
By default this maps host 8080 → container 80. Override the host port:
HOST_PORT=3000 docker compose up -d --build
VITE_BASE inside Docker
- For **http://your-server/
** (site at root): use the default **/** (do not setVITE_BASE, or setVITE_BASE=/`). - For GitHub Pages–style subpaths behind a path-based reverse proxy only: build with e.g.
docker build --build-arg VITE_BASE=/bio/ -t bio-site .or setVITE_BASEindocker-compose.yml/.envnext to compose.
Mismatching VITE_BASE and where nginx actually serves the app is the most common reason assets return 404.
Hosting on your LAN (Linux server)
- Install Docker on the server (Docker Engine + Compose plugin, or Docker Desktop on a workstation acting as a host).
- Copy the project (or clone the repo) onto the machine and run
docker compose up -d --buildfrom the repo root. - Find the server’s LAN IP, e.g.
ip -br aorhostname -I. - From another device on the same network, open
http://<LAN-IP>:8080(or whateverHOST_PORTyou chose). - Firewall: allow the host port, e.g. with UFW:
sudo ufw allow 8080/tcp comment 'bio static'thensudo ufw reload(adjust port to matchHOST_PORT). - Optional: put Caddy, Traefik, or nginx on the host in front of the container for HTTPS, auth, or a single entrypoint on 443 while the container stays on an internal port.
The container listens on all interfaces inside the host’s Docker publish rules; binding to LAN-only is usually done with host firewall rules or by placing a reverse proxy on the edge.
Customize content
| What | Where |
|---|---|
| Name, links, email, tagline | src/content/site.ts |
| Skills | src/content/skills.ts |
| Projects | src/content/projects.ts |
| Roles | src/content/experience.ts |
| Headshot | Replace public/avatar.svg with your image (WebP/AVIF or optimized JPG/PNG under public/ — update HeroSection.vue if you change the filename). |
| Resume PDF | Add public/resume.pdf (the Experience section links to it). |
| SEO / social | Update <title>, <meta name="description">, Open Graph/Twitter tags, and og:url in index.html to match your real domain. |
GitHub Pages (project site)
If the site is served from https://username.github.io/repo-name/, Vite needs a base path:
- In the repo, add a variable
VITE_BASE(Settings → Secrets and variables → Actions → Variables) with value/repo-name/(leading slash, trailing slash optional). - Enable GitHub Pages from GitHub Actions using the included workflow (Settings → Pages → Build and deployment → Source: GitHub Actions).
- The workflow
.github/workflows/pages.ymlrunsnpm install,npm run buildwithVITE_BASE, and publishesdist.
For a user site (username.github.io with the site at the root of that repo), leave VITE_BASE unset so the site uses /.
Environment variable (local)
Create .env.production (or .env) if you want to test a non-root base locally:
VITE_BASE=/repo-name/
Then npm run build and npm run preview.
Lighthouse / performance notes
- Hero image uses
fetchpriority="high"and explicitwidth/height. - GSAP + ScrollTrigger load on demand per section via dynamic
import()to keep the initial JS smaller. - Google Fonts load from
index.html; self-hosting fonts is an optional future optimization.
License
Private / personal use — adjust as you like.