Booting · 00:00:00

Tom LEFRERE · Data Scientist

Raw data. A signal.

0%
EN FR

← Portfolio

· astro · claude · cloudflare · css · javascript · mcp · node · o2switch · php · tailwind · typescript · vite · vue · web · wordpress

tom-lefrere.fr, my personal site across four lives

History of the four versions of my site, from pure PHP in 2019 to Astro + Sveltia CMS + MCP today. A permanent experimentation ground.

tom-lefrere.fr, my personal site across four lives

The project

Since 2019 I’ve been maintaining my personal site to showcase projects and try out new web tech. It’s gone through four successive versions, which makes it a pretty good mirror of my evolution as a developer. Each rewrite was a chance to dig into a new stack, test what actually works, and throw away what doesn’t last.

Version 1, 2019

Very simple first version, pure PHP with a few JavaScript animations. Minimal features obviously, but it was my first real production experience, which counts a lot when you’re starting out.

Version 2, 2020

Full rewrite in Vue.js to learn the framework. Added libraries like GSAP and jQuery for more polished animations. Typically the version where I wanted to prove I could do it, especially on the visual side.

Version 3, 2021-2026

Migration to WordPress to simplify maintenance and focus on content rather than infrastructure. Much less time maintaining, much more writing, which was the right balance at the time. But over time, the WordPress + plugins + custom theme combo got heavy, slow to load, and the editorial experience no longer fit me.

Version 4, 2026 · current

Full rewrite in Astro 5 (SSG) with Sveltia CMS, an MCP server, and O2switch deployment via GitHub Actions. This version has a different ambition from the previous ones: become a reliable, fast, measurable, bilingual and traceable site, without sacrificing the slightly alive side I like.

The principles that guided the rewrite:

  • Fully static for zero server compute at runtime, near-instant TTFB, and zero infrastructure load on the host.
  • Deep Space theme: night blue background #0a0f1f, yellow accent #fdf854, Space Grotesk + JetBrains Mono typography. Consistent atmosphere end to end.
  • Natively bilingual (FR / EN) with auto-detection, / and /en/ URLs, self-referencing hreflang, and 27 projects + 2 legal pages translated line by line.
  • Git-versioned content in src/content/projects/*.mdx with Zod-validated schema, instead of an opaque database.
  • Continuously audited: every CI push re-runs eco and accessibility audits, results shown live in a dedicated site app (Impact 🌱).

What it actually looks like

  • Tom OS, a CV section that mimics a macOS desktop with a row of clickable apps (Experience, Education, Skills, Terminal, SHAP Playground, Iris UMAP, Pokémon-like Booster, Snake, Impact). A slightly playful metaphor that fits me better than a plain PDF download.
  • Command palette (⌘K or Ctrl+K) to jump to any project, open an app, switch language, toggle blueprint mode, mute audio. Fuzzy-search, multi-groups, deep-link via ?q=.
  • Portfolio in 3 views: a scrolling slider, an editorial list, and an SVG constellation where each project is a star positioned on a year × category plane. Full-screen modal opens without leaving the page, deep-link via #p-slug, close button that rotates on hover.
  • Multi-tag filters: search input + multi-select toggle pills + top-N with “see more”. 40 distinct tags automatically extracted from article content via a regex dictionary.
  • MCP server exposing the site content as tools for Claude Desktop or Claude Code. 12 tools (list/get/create/update/delete projects, pages, medias, git_status, commit_and_push), Zod validation, SITE_ROOT sandbox, integration tests via node --test.
  • Impact app with live eco scores (96/100 via Sustainable Web Design v4), a11y (99/100 via WCAG 2.2 heuristics), tests (15/15 passing), runtime perf. Each gauge has a collapsible methodology that explains the computation, so it’s auditable rather than just decorative.
  • Rich editorial bits: styled PDF buttons, responsive galleries with hover zoom, <VideoEmbed> component for demos, all from MDX.

My contributions

Everything, end to end. Design, architecture, integration, audit tooling, deployment, docs. The repo also includes:

  • Automated scripts: eco-audit.mjs (carbon impact per visit), a11y-audit.mjs (63 pages scanned), extract-tags.mjs (auto-tagging via regex), optimize-images.mjs (PNG → WebP via sharp), generate-audit-report.mjs (serializes scores to JSON consumed by the Impact app), import-wp.mjs (initial WordPress migration).
  • CI tests: 6 build structure tests, 9 MCP tests in stdio JSON-RPC, always green before merge.
  • GitHub Actions: check + build + test + audit + mcp-test jobs in parallel, O2switch FTPS deployment after every push to main.
  • Cloudflare Worker for GitHub OAuth (api.netlify.com stopped supporting non-Netlify sites in 2026). Free, 5-minute deploy, secrets-side secure.
  • SEO 93/100: i18n sitemap, robots with max-image-preview:large, self-ref hreflang, Person + WebSite + BlogPosting + BreadcrumbList schemas (eligible for Google rich snippets), 1200×630 SVG OG images generated on the fly per project.

What I took away from this V4

SSG is real comfort. Once I migrated to Astro, dev speed exploded. Instant hot-reload, no database to babysit, all content in git with clean diffs. For a personal portfolio, it’s the right level of abstraction, really.

LLMs are changing the editorial game. The idea of an MCP server on my own content was a bit experimental at first. Now I can ask Claude “list my projects by date, create a draft foo, commit and push”, and it’s done. Huge friction reduction on small updates, and it opens the door to more ambitious usage (auto-tagging, title suggestions, cross-linking, etc.).

Measuring is already fixing. The Impact app displays scores live on the site. Making the numbers visible forces you not to let them slip. When the eco score drops below 90, I see it on every new feature I push, so I trade off differently.

Accessibility has a zero cost. 99/100 on the a11y audit isn’t a goal in itself but the natural result of good practices (semantic landmarks, systematic alt, skip link, decent contrast). The only remaining “low” is the missing skip link on the CMS /admin/ page, which is deindexed anyway.

Bilingual is doubly work. 27 projects × 2 languages = 54 articles to maintain. Without solid multilang infrastructure (lang schema, getCollection filter, en/xxxxxx slug normalization), it quickly becomes a nightmare. No regrets, but you have to be aware of the load.

Context

Personal project, ongoing since 2019. V4 was built in an intensive week with Claude Code, code entirely written and audited pair-programming with it.

Tech stack

LayerV4 (2026)
FrameworkAstro 5 (SSG, content collections, i18n)
LanguageTypeScript
StylingCSS custom properties + occasional Tailwind
ContentMDX + gray-matter + Zod validation
CMSSveltia CMS (modern Decap fork)
CMS authCloudflare Worker OAuth (sveltia-cms-auth)
MCP@modelcontextprotocol/sdk on stdio JSON-RPC
Imagesharp (PNG → WebP, -97%)
HostO2switch (cPanel Apache)
DeployGitHub Actions FTPS
Testsnode —test (native, zero deps)

Previous lives for the record:

  • V1: PHP / JavaScript (2019)
  • V2: Vue.js / GSAP / jQuery (2020)
  • V3: WordPress (2021-2026)
  • V4: Astro + Sveltia CMS + MCP (April 2026)