Overview
I’ve rebuilt my personal site more times than I’d like to admit. This version is the one I plan to keep: built with Astro 5, Tailwind CSS, and TypeScript, with content managed through Astro’s Content Layer API.
The constraints I set going in: no client-side framework, no hydration overhead, and a design system I could understand in full without a documentation tab open.
Stack
Astro 5 handles routing, static generation, and content collections. Pages are .astro files — essentially HTML templates with a TypeScript frontmatter section. No virtual DOM, no state management, no bundle to reason about at runtime.
Tailwind CSS with the typography plugin covers all styling. The zinc palette provides a neutral, readable tone in both light and dark mode. darkMode: 'class' lets a small inline script apply the saved theme before the first paint, preventing any flash.
MDX for content means I can drop into JSX components mid-prose when I eventually need to. For now every post is plain markdown inside an .mdx file.
Dark Mode Without Flash
The trick is a synchronous inline script in <head> that runs before any CSS or content renders:
(function () {
const saved = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (saved === 'dark' || (!saved && prefersDark)) {
document.documentElement.classList.add('dark');
}
})();
It reads localStorage and falls back to the OS preference. No flash, no layout shift.
What’s Next
- Table of contents for long posts
- RSS feed
- Open Graph images