Personal portfolio website with multi-theme support, internationalization, and a Go backend for blog content.
Live: tomandrieu.com
- 4 Switchable Themes - Default, Terminal (cyber-hacker), Blueprint (technical drawing), Retro 90s (GeoCities nostalgia)
- Bilingual - English and French with theme-specific translations
- Responsive Design - Mobile-first with touch-friendly interactions
- Scroll Animations - GSAP + ScrollTrigger effects
- Git Timeline - Interactive career/project history visualization
- Blog System - Go backend serving Markdown articles
- CRO Elements - Exit modals, floating CTAs, availability badges
cd frontend
# Pick one:
python -m http.server 8000
npx serve .
php -S localhost:8000docker-compose -f docker-compose.local.yml up- Frontend: http://localhost:8000
- API: http://localhost:3000
tomandrieu.com/
├── frontend/ # Static frontend (no build required)
│ ├── index.html # Main SPA entry
│ ├── blog.html # Blog page
│ ├── css/
│ │ ├── base.css # Reset, typography, shared styles
│ │ └── components/ # Buttons, cards, carousel styles
│ ├── js/
│ │ ├── core/ # Content loader, carousel, scroll effects
│ │ ├── components/ # Header, etc.
│ │ ├── theme-manager.js
│ │ ├── git-timeline.js
│ │ └── analytics.js
│ ├── themes/ # Theme-specific CSS + JS
│ │ ├── default/
│ │ ├── terminal/
│ │ ├── blueprint/
│ │ └── retro90s/
│ ├── data/ # Content JSON files (en/fr)
│ ├── i18n/ # Translation files
│ └── assets/ # Images, videos
├── backend/ # Go REST API
│ ├── main.go
│ ├── handlers/
│ ├── models/
│ └── services/
├── blog/ # Markdown articles
│ └── articles/
├── nginx/ # Web server config
├── docker-compose.yml # Production
└── docker-compose.local.yml # Local dev
Switch themes via URL parameter or the floating theme button:
| Theme | URL | Description |
|---|---|---|
| Default | ?theme=default |
Clean, minimal professional |
| Terminal | ?theme=terminal |
Green CRT, ASCII art, typewriter |
| Blueprint | ?theme=blueprint |
Technical drawings, SVG animations |
| Retro 90s | ?theme=retro90s |
GeoCities, marquees, hit counters |
- Create
frontend/themes/{theme-name}/with:{theme-name}.css- Theme styles{theme-name}.js- Theme initialization + card renderer
- Register in
frontend/js/theme-manager.js→ThemeManager.THEMES - Add i18n files:
frontend/i18n/themes/{theme-name}/en.jsonandfr.json - Theme-specific HTML in
index.htmlis hidden by default; show via CSS
All content is driven by JSON files in /frontend/data/{lang}/:
| File | Purpose |
|---|---|
content.json |
Hero text, about section, tech stack, contact info |
projects.json |
Portfolio projects (title, description, images, tags) |
git-history.json |
Timeline entries (education, freelance, projects) |
To update content, edit the JSON files - no code changes needed.
Supported Languages: English (en), French (fr)
Detection Priority:
- URL parameter (
?lang=en) - localStorage
- Browser language
- Default: French
Files:
- Base translations:
frontend/i18n/locales/{lang}.json - Theme-specific:
frontend/i18n/themes/{theme}/{lang}.json
Go 1.21 with Chi router.
| Method | Path | Description |
|---|---|---|
| GET | / |
Health check |
| GET | /articles |
List all blog articles |
| GET | /articles/{id} |
Get article by ID |
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
Server port |
ARTICLES_DIR |
./blog/articles |
Markdown articles path |
FRONTEND_URL |
http://localhost:8000 |
CORS allowed origin |
cd backend
go run main.godocker-compose up -dRoutes via Traefik:
tomandrieu.com→ Frontend (nginx:4200)api.tomandrieu.com→ Backend (go:3000)
SSL handled by Let's Encrypt via Traefik.
- Local:
frontend/config.js - Production: Generated from
frontend/config.template.jsvia envsubst
Always test in browser before committing:
- Start local server
- Check all themes if theme-related:
?theme=terminal,?theme=blueprint,?theme=retro90s - Test both languages:
?lang=en,?lang=fr - Check browser console for errors
- Test on mobile viewport
- Never push directly to
main(production branch) - Create feature branches:
feature/,fix/,refactor/ - Use pull requests for all changes
Follow conventional commits:
feat:New featuresfix:Bug fixesrefactor:Code improvementsdocs:Documentationstyle:Formatting, CSS
- Theme Manager:
frontend/js/theme-manager.js- Controls theme switching - Content Loader:
frontend/js/core/content-loader.js- Populates DOM from JSON - Card Renderer:
frontend/js/core/card-renderer.js- Project card rendering - Main HTML:
frontend/index.html- All sections, theme elements
Elements use data-content attributes for dynamic content:
<h1 data-content="hero.title"></h1>
<!-- Populated from content.json: { "hero": { "title": "..." } } -->theme-manager.jsdetects theme from URL/localStorage- Loads theme CSS dynamically
- Loads theme JS which calls:
PortfolioBase.initBase()- Initialize shared functionalityPortfolioBase.loadProjects()- Load and render projects
- Theme JS provides custom
renderCard()function
- New section: Add HTML in
index.html, data incontent.json, styles inbase.cssor theme CSS - New project: Add entry to
projects.jsonwith images inassets/ - New translation: Add keys to all locale files in
i18n/
When multiple agents work on this project:
- Check
/.claude/work/for in-progress work - Create your own work file:
{timestamp}-{description}.md - Update status as you progress
- Push regularly to avoid conflicts
Frontend:
- Vanilla JS (ES modules)
- GSAP + ScrollTrigger
- CSS custom properties
- No build required (Vite available for optimization)
Backend:
- Go 1.21
- Chi router
- Markdown processing
Infrastructure:
- Docker + Docker Compose
- Nginx (static serving)
- Traefik (reverse proxy, SSL)
Private repository - All rights reserved.
Questions? Open an issue or contact via the website.