Shipping This Site with Astro and shadcn/ui
A short tour of the stack behind this site: Astro content collections, MDX, Tailwind v4, and shadcn components on a dark, mono, terminal-flavored theme.
This site is intentionally boring infrastructure. Two pages, one content collection, zero client-side JavaScript on most routes. Here’s what the pieces look like.
Content collections
Posts live as MDX files in src/content/blog, and Astro’s content layer turns the folder into a typed collection:
import { defineCollection, z } from "astro:content"
import { glob } from "astro/loaders"
const blog = defineCollection({
loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/blog" }),
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
}),
})
export const collections = { blog }
The Zod schema means typos in frontmatter fail the build instead of quietly rendering an empty page. That trade — a little friction now for no surprises later — is worth it even for a two-page site.
Tailwind v4, CSS-first
There’s no tailwind.config.js. The theme lives in CSS: design tokens are plain custom properties in global.css, exposed to Tailwind through an @theme inline block. The whole “brand” is JetBrains Mono, a near-black green-tinted background, and one terminal-green accent.
.dark {
--background: oklch(0.14 0.005 160);
--primary: oklch(0.77 0.17 155);
}
shadcn/ui, the Astro way
shadcn components are source code you own, not a dependency you fight. The React versions render fine inside .astro files — they become static HTML at build time and ship no JavaScript. The rest of the “design system” is a handful of hand-rolled utilities: a fading blueprint grid, a text glow, an animated nav underline. All CSS.
The result
The whole site builds to static files, so it deploys anywhere — this one is set up for Cloudflare’s static asset hosting via Wrangler. No servers, no databases, no runtime to patch.
The lesson I keep relearning: constraints make side projects finish. Two pages means two pages.
// fin