README.md

How this website is built

The stack, the structure and the principles behind this website.

Jump to sections of this article

Principles

The whole thing is built around three ideas:

  • Simplicity. Every dependency has to justify why it is there.
  • Clarity. The site shows how it is made while you read it, which is why the sidebar lists the real Markdown files.
  • Personal. A personal website should look and sound like the person behind it.

If you want the long version:

Tech stack

  • Astro for static site generation and file-based routing.
  • MDX so articles are Markdown files that can import components.
  • Tailwind CSS through @tailwindcss/vite, with the customisation living in src/styles/global.css.
  • Lucide for icons, rendered at build time.
  • Shiki for syntax highlighting, with the night-owl-light and night-owl themes.
  • TypeScript in strict mode, with the @/* path alias pointing to src/*.
  • pnpm with a workspace catalog, so versions are pinned in one place.
  • Netlify for instant deploys on every push.

Fonts

Three self-hosted typefaces, served as woff2 from public/fonts so there is no request to a third party and no layout shift:

  • DM Sans for headings and the article titles. Variable, so one file covers every weight.
  • Inter for body text and the interface, also variable and subset to latin to keep it small.
  • IBM Plex Mono for code, file names and the status bar, in a single 400 weight.

The two fonts used above the fold are preloaded in the layout, and all three use font-display: swap.

How it works

Content collections. Posts and pages live in src/content/{en,es}/{blog,pages} and are validated with Zod schemas in src/content.config.ts. Each post carries a postId that pairs it with its translation, which is what makes the language switcher and the hreflang tags work.

Internationalisation. The site is routed by URL, so / is English and /es/ is Spanish. Interface strings live in src/i18n/translations.json and every component reads them through a single useTranslations helper.

Almost no JavaScript. The sidebar is a hidden checkbox and a few CSS selectors, the file folders are <details> and <summary>, and the tooltips are CSS only. The three scripts I wrote are vanilla and inlined: one stores the sidebar state in localStorage, one remembers the chosen theme there too, and one works around an iOS bug with tooltips.

The exception is Google Analytics, the only third party script here and by far the largest. It loads asynchronously.

Accessibility and performance. Everything is rendered at build time, images are served as WebP with responsive sizes, and the only state I keep in the browser is the sidebar and the chosen theme, on top of the cookies Analytics sets for itself.