Part 1 — Customizing This Astro Theme: Separating Identity, Narrative, and Datasets from Templates
A practical guide to cloning, running, and customizing this Astro 7 + Tailwind CSS v4 site for your own domain—without ever editing a single .astro template.
When I first built this site on Astro and Tailwind CSS, it started the way most personal engineering sites do: my name, domain, bio paragraphs, social links, and navigation items were scattered directly across a dozen .astro page templates, layout shells, and utility scripts.
That works fine when a repository only ever serves one person. It becomes a headache the moment you want to release the codebase as a clean, open-source starter theme—or even when you simply want to update your bio or domain metadata without grepping through layout components, Open Graph generators, RSS feeds, and JSON-LDJSON-LDJavaScript Object Notation for Linked DataArchitectureA W3C standard for serializing linked data using JSON to provide search engines with structured Schema.org contextual meaning about web pages. blocks.
Over the past few days, we refactored the entire frontend architecture around a single rule: a user adopting this theme should be able to customize 100% of their identity, UI mechanics, personal narrative, articles, career timeline, and photography without ever opening an .astro file in src/pages/ or src/components/.
Here is how the configuration, content collections, and datasets are structured—and the exact checklist to make this site your own.
Architecture Overview: Configs, Content, and Datasets
Instead of mixing personal prose with presentation markup, the src/ directory is divided into four distinct responsibilities:
Why separate src/content/ from src/data/?
src/content/holds editorial prose and Markdown collections managed by Astro’s Content Layer (pages,blog,log,photography). These files go through the Unified Remark/Rehype pipeline (syntax highlighting, build-time Mermaid diagrams, GitHub-style alerts, and automatic acronym tooltips).src/data/holds pure, strongly-typed TypeScript datasets (experiences.ts,recommendations.ts,techTerms.ts) that are queried, cross-referenced, or indexed across multiple routes at build time. Helper logic lives insrc/utils/, keepingsrc/data/100% data-only.
Step 1: Local Prerequisites & Quickstart
To run and build the site locally, you only need Node.js >= 20 and Playwright’s Chromium binary (which rehype-mermaid uses at build time to compile Markdown mermaid code fences into responsive inline SVGsSVGScalable Vector GraphicsArchitectureAn XML-based two-dimensional vector graphics format that renders crisp graphics at any display resolution and can be styled dynamically with CSS. with zero client-side JavaScript).
# 1. Install dependencies from package-lock.json
npm ci
# 2. Install headless Chromium for build-time Mermaid SVG rendering
npx playwright install chromium
# 3. Start the local dev server at http://localhost:4321
npm run dev
When you are ready to test a full production build (including static HTML, responsive WebP/AVIF photography variants, and build-time Satori Open Graph PNG cards):
npm run build
npm run preview
Step 2: Configure Your Identity (src/site.config.ts)
Your first stop when customizing the site is src/site.config.ts. This file is the single source of truth for who owns the site:
export const siteConfig: SiteConfig = {
site: {
url: 'https://www.example.com',
domain: 'example.com',
title: 'Jane Doe',
description:
'Jane Doe — Systems & Cloud Architect. Notes on distributed systems, infrastructure automation, and photography.',
keywords: ['Jane Doe', 'Cloud Architecture', 'Infrastructure', 'Linux', 'Terraform'],
locale: 'en_US',
defaultTheme: 'dark', // 'dark' | 'light' | 'system'
},
author: {
name: 'Jane Doe',
givenName: 'Jane',
familyName: 'Doe',
role: 'Systems & Cloud Architect',
jobTitle: 'Principal Systems Architect',
location: 'Tokyo',
flagEmoji: '🇯🇵',
email: 'jane@example.com',
avatar: 'author.jpg', // Small circular chip on blog & log cards
aboutImage: 'author.jpg', // Large portrait on /about/ (can be a separate photo!)
pronouns: 'she/her',
},
socials: {
linkedin: { url: 'https://www.linkedin.com/in/jane-doe/', label: 'LinkedIn' },
github: { url: 'https://github.com/janedoe', label: 'GitHub' },
gitlab: { url: 'https://gitlab.com/janedoe', label: 'GitLab' },
instagram:{ url: '', label: 'Instagram' }, // Leave empty ('') to hide automatically
},
};
Updating src/site.config.ts automatically cascades across the entire build:
- Astro Site URL: Feeds
astro.config.mjsfor canonical URLs and@astrojs/sitemap. - Default Post Author:
src/content.config.tsdefaults every blog post’sauthorfrontmatter field tositeConfig.author.name. - Headshots: Drop your portrait into
src/images/author.jpg(or configure two separate files insrc/images/foravatarandaboutImage). Astro’s asset pipeline automatically generates optimized AVIF and WebP variants. - Dynamic Endpoints:
/rss.xml,/atom.xml,/robots.txt,/sitemap-style.xsl,/llms.txt,/llms-full.txt,/llms.json, and Schema.org JSON-LD blocks all pull your name, domain, role, and social links directly fromsiteConfig.
Step 3: Tune Theme & UI Mechanics (src/theme.config.ts)
Next, open src/theme.config.ts to customize how the interface behaves and looks:
- Header SVG Wordmark (
logo): Replace the inline SVG paths or adjustviewBox,width, andheightfor the top navbar logo. - Third-Party Integrations (
integrations):- Formspree (
formspreeEndpoint): Paste your Formspree form endpoint URL to activate the interactive contact forms on/contact/and/about/#contact(or set to""to disable). - Umami Analytics (
analytics): Toggleenabled: true/falseand set your script URL andwebsiteId.
- Formspree (
- Navigation & Pagination (
navigation,pagination): Configure top navbar links and item counts per page for the blog archive (olderBlogsCount), log stream (logCount), photo gallery (galleryCount), and homepage carousels (homepageCarouselCount). - Open Graph Card Branding (
openGraph): Customize the badge label and footer text rendered onto every 1200×630 PNG social card generated by Satori at/og/*.png. - Automatic Technical Tooltips (
tooltips):enabled: trueautoLink: 'first'('first' | 'all' | 'off') — When set to'first', the build-time Rehype plugin (rehype-tech-tooltips.mjs) scans your Markdown articles in top-to-bottom reading order and automatically wraps only the first occurrence of any technical acronym found insrc/data/techTerms.tsinto an accessible glassmorphic tooltip card (both in light and dark mode), leaving subsequent mentions as clean plain text.
- Footer Toggles (
footer): Customize the footer navigation columns, copyright notice,showPoweredBybadge, andshowThemeAttributiontoggle.
Step 4: Edit Page Titles, Descriptions & Personal Narrative (src/content/pages/)
Every top-level page (/ , /about/, /now/, /contact/, /blog/, /log/, /photography/, and /tags/) loads its pageTitle, meta description, headings, structured cards, and personal narrative prose from Markdown files in src/content/pages/:
| File | Route | What You Customize Here |
|---|---|---|
src/content/pages/home.md | / | Homepage pageTitle, description, Intro hero card (eyebrow, heading, two-column blurb, CTA link), and section headers for the Photo Roll, Blog, and Log carousels. |
src/content/pages/about.md | /about/ | About pageTitle, description, greeting line, Markdown body (your personal biography paragraphs), spoken languages badges (fluent, native, beginner), faq accordion groups & answers, and contact form prompt. |
src/content/pages/now.md | /now/ | Your /now snapshot (lastUpdated, introHtml, and nowSections cards for Location, Engineering Focus, Homelab, Offline life, and Reading). Supports inline <abbr> tags for technical tooltips. |
src/content/pages/contact.md | /contact/ | Contact pageTitle, description, heading, Markdown body (intro paragraph), timezone/response-time labels, and form header. |
src/content/pages/blog.md | /blog/ | Blog index pageTitle, description, and archive section heading/subtitle. |
src/content/pages/log.md | /log/ | Log stream pageTitle, description, header badge, heading, and subtitle. |
src/content/pages/photography.md | /photography/ | Photography gallery pageTitle and description. |
src/content/pages/tags.md | /tags/ | Topic & tag directory pageTitle, description, eyebrow, heading, and subtitle. |
Because these files are validated by Zod in src/content.config.ts, Astro will immediately catch any missing or mistyped frontmatter fields during npm run dev or npm run build.
Step 5: Populate Your Datasets (src/data/)
Three TypeScript files in src/data/ power the interactive career timeline, peer recommendations, and technical dictionary:
src/data/experiences.ts: Powers the Living CV timeline on/about/and automatically links career roles into matching topic archive pages under/tags/. Each entry includesrole,company,date,location,summary,highlights, andtags.src/data/recommendations.ts: Powers the symmetrical bento grid of peer recommendations on/about/#recommendations.src/data/techTerms.ts: Contains ~60 categorized technical definitions (Cloud,Networking,Storage,Security,Hardware,Virtualization,Architecture,DevOps,AI). Add your own domain acronyms here and they will automatically upgrade to interactive tooltips across your blog posts with zero client-side dictionary payload.
Step 6: Authoring Blog Posts, Logs & Photography
Once your identity and narrative pages are in place, day-to-day publishing happens entirely in Markdown:
1. Technical Articles (src/content/blog/*.md)
Create a new .md file in src/content/blog/:
---
title: "Designing Resilient Multi-Region Storage"
description: "Practical notes on replication topologies, RPO/RTO trade-offs, and automated failover testing."
pubDate: "Nov 03 2026"
tags: ["AWS", "Infrastructure", "Architecture"]
sectionNav: "h1 to h2"
---
- No
authorline needed: Defaults automatically tositeConfig.author.name. - Floating Section Navigation (
sectionNav): AddingsectionNav: "h1 to h2"(ortrue) automatically extracts your article’s headings at build time and populates the floating sub-navigation pill bar. - Rich Markdown Extensions:
- GitHub Alerts: Use
> [!NOTE],> [!TIP],> [!IMPORTANT],> [!WARNING], or> [!CAUTION]. - Mermaid Diagrams: Standard
```mermaidfences compile at build time into theme-adaptive inline SVGs. - YouTube Facades: Use a
```youtubecode fence with a video URL on line 1 and title on line 2 to render a zero-tracking poster facade that also promotes the post to a<VideoCardPoster />in blog listings.
- GitHub Alerts: Use
2. Operational Logs (src/content/log/*.md & CHANGELOG.md)
- Status-Updates: Drop short operational field notes into
src/content/log/YYYY-MM-DD-slug.md. - Changelogs: Maintain site release notes in the root
CHANGELOG.mdusing Keep a Changelog headings (## [YYYY-MM-DD] - Title). The/log/stream parsesCHANGELOG.mddynamically and merges releases with your status-updates and photo rolls.
3. Photography Roll (src/images/photography/ & src/content/photography/)
- Place high-resolution source
.jpgphotos insrc/images/photography/(e.g.,1.jpg) and create a matchingsrc/content/photography/1.mdfile withid,filename,title,pubDate,category,location,tags, and the backstory in the Markdown body. - The build pipeline automatically generates responsive WebP thumbnails for the justified gallery grid, full-resolution WebP lightbox views, individual
/photography/[id]/field-note pages, and 1200×630 Open Graph cards.
Next Up: Deploying Your Static Build (or Replicating the AWS Stack)
Once npm run build succeeds locally with your customized config and content, you have a complete static site in ./dist/.
From here, the ./dist/ folder is ready for your deployment platform of choice—or, if you also want to replicate the multi-region AWSAWSAmazon Web ServicesCloudAmazon global cloud computing platform offering on-demand compute, object storage, content delivery, and managed infrastructure services across dozens of geographic regions. infrastructure behind this site, give Part 2 — Provisioning & Deploying the Multi-Region AWS Stack with Terraform and GitLab CI/CD a whirl.

About the author
Ed Stuurman
Infrastructure and reliability engineer based in Japan. Passionate about resilient systems, bare-metal hardware, cloud automation with Terraform, and all things homelab.
Contact
Thoughts or questions on this article?
Found a bug, tested a similar setup, or want to discuss the details? Drop a message below.