Building aidenknecht.dev
This site is 14 static pages with 17 KB of CSS and one 560-byte script, served from Cloudflare's edge and deployed by a push to main.
- Date
- Stack
- Astro, Tailwind CSS, TypeScript, Cloudflare Workers, Wrangler
The finding
The whole site ships as static HTML. Across 14 pages there is one stylesheet at 17 KB and one
client-side script, the theme toggle, at about 560 bytes. Everything else is markup, fonts,
and a handful of compressed images. It deploys itself when main changes.
Context
I wanted somewhere to put investigative work that reads like a lab notebook rather than a list of tools. That set the constraints: static output, no JavaScript framework on the client, one accent color, monospace only for data, and a content model where adding a project means adding one markdown file and nothing else.
What was built
- Astro with content collections. Projects, writeups and experience are three collections
in
src/content/, each validated against a schema at build time. A page like this one is a markdown file with frontmatter; the layouts, lists, feed and sitemap pick it up on their own. - Tailwind with design tokens. Colors, fonts, the type scale and radii are declared once as theme variables, and Tailwind’s defaults are reset so a hardcoded value cannot slip in. Dark mode follows the system until the visitor chooses, and the choice is remembered.
- Shiki at build time for KQL, PowerShell, Python and bash, with a light and a dark theme tied to the site palette. Code is highlighted HTML, not a runtime library.
- Cloudflare in front, Cloudflare underneath. I registered
aidenknecht.devand put it behind Cloudflare for DNS and TLS. The site itself runs on Cloudflare Workers as a static assets bundle: a GitHub push tomaintriggers a build andwrangler deploy. - The quiet parts. A sitemap, an RSS feed for writeups, a 404 page, canonical and Open
Graph tags on every page, a skip link, and a
TODO: reviewconvention in the content so nothing drafted for me ships in a voice that isn’t mine.
Evidence
The first deploy failed. Cloudflare rejected it with an authentication error, code 10000,
because the CI token had been scoped to the newer unified deploy permission, which covers
wrangler deploy and not the older Pages upload. Rather than widen the token, I moved the
project to Workers static assets, which is what the token was built for. The whole
configuration is one file:
{
"name": "portfolio",
"compatibility_date": "2026-09-16",
"assets": {
"directory": "./dist",
"html_handling": "drop-trailing-slash",
"not_found_handling": "404-page"
}
}
drop-trailing-slash matches the site’s canonical URLs, so /about serves the page instead
of redirecting to /about/. 404-page serves the real 404 page with a real 404 status.
Build output, for the record:
14 page(s) built in 886ms
sitemap-index.xml created
0 errors, 0 warnings, 0 hints (astro check)
What it means
- The least-privilege fix is usually to move the workload, not to widen the credential. The deploy token stayed narrow and the platform changed instead.
- A schema at build time is a cheap guardrail. A missing finding or a malformed date fails the build, not the reader.
- Constraints made the design decisions for me. No client framework, one accent, no cards: most of what a portfolio site usually argues about was settled before the first commit.