Personal website and blog for Christopher Klint (christopherklint.com). Built with Astro v5, Tailwind CSS v4, shadcn/ui, and React. Deployed on Cloudflare Pages.
Repository: https://github.com/christopherklint97/root
root/
├── src/
│ ├── components/
│ │ ├── ui/ # shadcn/ui components (button, badge, card, dropdown-menu, separator)
│ │ ├── Header.astro # Sticky header with nav links and theme toggle
│ │ ├── Footer.astro # Site footer with social links
│ │ ├── ThemeToggle.tsx # React: light/dark/system theme switcher
│ │ └── MobileNav.tsx # React: hamburger menu for small screens
│ ├── content/
│ │ └── thoughts/ # Markdown blog posts (created at runtime, glob-loaded)
│ ├── data/
│ │ └── projects.ts # GitHub repo fetching, pinned repos config, language colors
│ ├── layouts/
│ │ ├── BaseLayout.astro # Root HTML layout (head, header, footer, fonts, theme script)
│ │ └── BlogPost.astro # Blog post layout with title, date, tags, prose styling
│ ├── lib/
│ │ └── utils.ts # cn() utility (clsx + tailwind-merge)
│ ├── pages/
│ │ ├── index.astro # Homepage: hero, projects preview, thoughts list, speaking
│ │ ├── about.astro # CV/resume: experience timeline, education, languages
│ │ ├── projects.astro # All GitHub repos (fetched at build time from GitHub API)
│ │ ├── contact.astro # Contact info, cal.com booking link
│ │ ├── thoughts/
│ │ │ ├── index.astro # Blog listing page
│ │ │ └── [...slug].astro # Dynamic blog post pages
│ │ └── rss.xml.ts # RSS feed generation
│ ├── styles/
│ │ └── globals.css # Tailwind config, theme colors (light/dark), animations
│ └── content.config.ts # Astro content collection schema for "thoughts"
├── public/
│ └── favicon.svg
├── astro.config.mjs # Astro config: site URL, Tailwind/React/MDX/Sitemap integrations
├── components.json # shadcn/ui config (new-york style, slate base color)
├── wrangler.toml # Cloudflare Pages config (output: ./dist)
├── tsconfig.json # Strict mode, path alias @/* -> ./src/*
└── package.json
- Astro v5 with static site generation (SSG). Pages are
.astrofiles. - React is used only for interactive islands:
ThemeToggleandMobileNav(loaded viaclient:load). - All other components are Astro components (server-rendered, zero JS).
- Tailwind CSS v4 configured via Vite plugin (not PostCSS).
- shadcn/ui with "new-york" style, slate base color, CSS variables enabled.
- Custom theme in
globals.cssusing CSS custom properties: warm editorial palette with a terracotta primary color (hsl(14 65% 48%)). - Dark mode via
.darkclass on<html>, toggled byThemeTogglecomponent with localStorage persistence. - Fonts: Instrument Serif (headings), Plus Jakarta Sans (body), JetBrains Mono (code) -- loaded from Google Fonts.
- Staggered entrance animations (
animate-slide-up,stagger-1throughstagger-8) withprefers-reduced-motionsupport.
- Blog posts ("thoughts") use Astro Content Collections with a glob loader reading
src/content/thoughts/*.md. - Frontmatter schema:
title(string),description(string),date(date),updated(date, optional),tags(string[], default []),draft(boolean, default false). - Drafts (
draft: true) are filtered out in all listings and the RSS feed. - Posts are sorted by date descending. The homepage shows the latest 5.
- The projects page fetches repos from the GitHub API at build time (
https://api.github.com/users/christopherklint97/repos). - Pinned repos are configured in
src/data/projects.tswith optional custom descriptions. - Hidden repos, fork repos, and archived repos are filtered out.
- The homepage shows the top 4 repos.
@/*maps to./src/*(configured intsconfig.json).
npm install # Install dependencies
npm run dev # Dev server at localhost:4321
npm run build # Production build to ./dist
npm run preview # Preview production build locally- Hosted on Cloudflare Pages.
wrangler.tomlpoints to./distas the build output directory.- The site is built with
npm run build(standard Astro static build). - GitHub repos are fetched at build time, so a rebuild is needed to reflect new/updated repos.
- Create
src/content/thoughts/my-post.mdwith the required frontmatter:--- title: "Post Title" description: "Short description for RSS and meta tags." date: 2026-01-15 tags: ["engineering", "leadership"] --- Post content here.
- Set
draft: truein frontmatter to hide from listings while writing.
Use the shadcn CLI or manually add components to src/components/ui/. The config is in components.json.
Add an entry to the pinnedRepos array in src/data/projects.ts with the repo name and optional custom description.
Add the repo name to the hiddenRepos array in src/data/projects.ts.