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 insrc/styles/global.css. - Lucide for icons, rendered at build time.
- Shiki for syntax highlighting, with the
night-owl-lightandnight-owlthemes. - TypeScript in strict mode, with the
@/*path alias pointing tosrc/*. - 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.