nimahejazi.me Personal Website
A short case study on how this site came together, covering the architecture decisions, the styling approach, and the math rendering that ties the writing together.
Approach
The goal was a content-first personal site that ships almost no JavaScript by default. Astro 5 handles that with islands architecture: pages are rendered to static HTML at build time, and client-side JavaScript is added only where it is actually needed (the dark-mode toggle and one Preact visualizer).
Content lives as MDX files validated by Zod schemas through Astro’s content collections, so every post and project is type-checked at build time. Adding new writing is a matter of dropping a file into the right folder.
Styling
Styling uses Tailwind CSS v4 in its CSS-first mode, with no tailwind.config.js.
Theme tokens and the dark-mode variant are declared directly in the global
stylesheet:
@import "tailwindcss";
@theme {
--color-cream-100: #faf8f3;
--color-brand-600: #5b5bd6;
}
@custom-variant dark (&:where(.dark, .dark *));
Dark mode is driven by a .dark class on <html>, toggled and persisted on the
client with no flash of incorrect theme.
Math and code
Statistics-heavy posts need real math, so the Markdown pipeline runs
remark-math + rehype-katex for KaTeX rendering, with Shiki providing
paired light/dark code themes and line wrapping. The same renderer serves both
blog and project pages.
A block formula renders cleanly, for example the utilization bound from the scheduling work:
Deployment
The code lives on GitHub, and Vercel’s Git integration turns every push to
main into an automatic build and production deploy, with npm run build
producing dist/ and no manual step. vercel.json scopes production deploys to
main (other branches get preview URLs), @astrojs/sitemap derives the sitemap
from the configured site URL, and analytics stay opt-in behind an environment
variable so the default build is tracker-free.