← Back to projects

My personal website: SvelteKit, blog, and terminal

sveltekittypescriptblogfrontendself-hosting
· View project

Why this website?

I needed somewhere to explain my projects and tell people what I do, along with my experiences. Everything I don’t have room for on LinkedIn, or that wouldn’t necessarily belong there, goes here; this is a website for me above all: articles, projects, and a small interactive terminal, because otherwise it would feel a bit too simple, even though I like the minimalist style.

Before that, I’d already built simpler websites, first with HTML + vanilla JS and Flask, then with Svelte. I’m trying to build something clean, easy to evolve, and running on my cluster like everything else.

Repo : github.com/BaptTF/bapttf-front

Overview

The site is a SvelteKit frontend written in TypeScript, served as static files. The content lives in Markdown files. And on the home page, there’s a terminal that talks to a Go backend over WebSocket.

PieceChoiceRole
FrontendSvelteKit + TypeScriptApp, routing, pages
ContentMarkdown + mdsvexArticles and project pages
StyleTailwind CSS 4UI
Buildadapter-staticExport HTML/CSS/JS
Serverstatic-web-serverServe the production build
Terminal backendGo (API + WebSocket)Easter egg / Hall of Root
DeployDocker → GHCR → ArgoCDGitOps on my K3S

The frontend is deliberately simple. No CMS, no database for the blog. A src/posts/ folder, .md files, and that’s it.

Frontend stack

SvelteKit

I chose SvelteKit because I’d already used it and I like it. It’s lightweight and really intuitive. The model with +page.svelte / +page.ts, file-based routing, and the ability to generate static output is really nice.

The site runs on Svelte 5 ($props(), $state(), $effect()). At first you have to get used to it, but once it clicks, it’s pleasant to use.

Content in Markdown

The articles are Markdown files with frontmatter. mdsvex turns them into Svelte components. A “classic” article is just a title, a date, and tags. A project article also has a project block:

project:
  slug: bapttf-front
  link: "https://github.com/BaptTF/bapttf-front"
  description: "My personal website, portfolio, blog and interactive terminal."
  featured: true

That’s what lets me have both a blog and a projects page without duplicating the content. One project = one article. If I want to talk about a repo, I write the article, add the project frontmatter, and it appears automatically.

Design

The UI is deliberately restrained: navbar, footer, dark mode via prefers-color-scheme, and no overly heavy component framework. The idea was for the content to come before the design. The terminal is the only slightly heavy element on the home page.

The terminal and the Hall of Root

Work in progress: the interactive terminal is not on the home page yet (Go backend still to finish).

On the home page, there’s a fake terminal. It connects to my Go API over WebSocket; you can type commands and, if you find the root password… you get into the Hall of Root.

It’s an easter egg. You gain root privileges, enter a nickname, and appear on /root with the others who succeeded. Nothing useful, just a little cybersecurity challenge because I like CTFs.

On the frontend side, it’s a Svelte component that manages:

  • the WebSocket connection
  • the display of the lines
  • the $ / # prompt
  • the nickname popup once root is obtained

The API/WS URLs go through environment variables (PUBLIC_API_URL, PUBLIC_WS_URL) injected during the Docker build.

Build and deployment

The site is generated as a static site. No Node in prod on the frontend side: SvelteKit generates the pages, and static-web-server serves them in the container.

The Dockerfile is multi-stage:

  1. node:22-alpinenpm ci + npm run build
  2. lightweight final image with the build/ folder

The GitHub Actions CI:

  1. check + lint + build
  2. on main, build the Docker image
  3. push to GHCR (latest + sha tag)
  4. ArgoCD on the cluster side picks the new image

In other words: a push to main, and the site updates itself. Same logic as the rest of my infrastructure, described in the vps-infra article.

Technical choices

Why static?

A personal website doesn’t need a Node runtime. Pre-generated HTML is simpler to host, faster, and consumes almost nothing on the cluster. The only dynamic parts (the terminal and Hall of Root) go through the Go API alongside it.

Why Markdown rather than a CMS?

Because I want to write in my editor, version the content with the code, and open a PR if I want. No admin panel, no plugin that breaks, no database to back up for three articles.

Why a separate backend for the terminal?

The frontend must not know the root password or manage the list of rooters. That lives on the backend. The site stays static, and the secrets must remain dynamic. It also gives me a reason to have a small backend in the stack.

What I would like to improve

It’s a first draft that works, but there is clearly room for improvement:

  • More content — the site exists, now I need to write
  • The Go backend — better documentation for the terminal / Hall of Root API, maybe link to it more clearly from this article
  • SEO / metadata — Open Graph, clean descriptions per page
  • Some UX improvements — keyboard navigation in the terminal, better loading states on /root
  • Tests — today the CI runs check/lint/build, but there are no real tests yet

In summary

My choices

  • SvelteKit + adapter-static: simple, fast, perfect for a self-hosted personal website
  • Articles = source of truth for the blog and the projects
  • GitOps all the way: same pipeline as the rest of my infrastructure
  • The terminal: a small fun part of the site, and a reason to have both a frontend and a backend

Debatable choice

  • Two repos / two services (frontend + Go API) for a personal website: it may be overkill, but I like things to be clean.

What I would change

  • Clarify the link between this repo and the terminal backend in the documentation

In summary: it’s my little website on the internet. It’s not revolutionary, but it’s mine, it’s clean, it runs on my cluster, and it lets me write about whatever I want.

© 2026 Baptiste