Source for blog.foyzul.com — a static, zero-JavaScript blog built with Astro and served from Cloudflare Workers static assets.
Posts are MDX files under src/content/posts/. Adding a folder there publishes a post; there is no
CMS, no database, and no server rendering.
| Piece | Choice |
|---|---|
| Framework | Astro 7, static output (no adapter, no SSR) |
| Content | MDX via @astrojs/mdx, typed frontmatter via a content collection schema |
| Syntax highlighting | Shiki, dual light/dark themes emitted as CSS variables |
| Feeds | @astrojs/rss (/rss.xml) and @astrojs/sitemap |
| Fonts | Self-hosted Source Serif 4, IBM Plex Mono, and Noto Sans Bengali |
| Styling | One hand-written stylesheet, src/styles/blog.css — oklch tokens, light and dark |
| Hosting | Cloudflare Workers assets, custom domain blog.foyzul.com |
| Client JS | None, apart from the theme toggle |
Requires Node 26 (see .nvmrc).
npm install
npm run dev # → http://localhost:4321Astro 7 runs the dev server as a background daemon, so npm run dev returns immediately:
npx astro dev status # is it running, and on which pid
npx astro dev logs # tail the output
npx astro dev stop # shut it downTwo things behave differently in dev, by design: draft: true posts are visible (and excluded from
the production build), and the Cloudflare runtime is not involved — so public/_headers, the custom
404 page, and the /about → /about/ redirect are not exercised. To test those, build and run the
real Workers runtime:
npm run build
npx wrangler dev # → http://localhost:8788| Command | What it does |
|---|---|
npm run dev |
Dev server with hot reload for prose, components, and CSS |
npm run build |
Static build to dist/ |
npm run preview |
Serve the built output |
npm run check |
astro check — type and content-schema diagnostics |
npm run verify |
Build, then drive a real browser at three widths in both colour schemes and fail on layout or accessibility regressions |
npm run deploy |
Build and wrangler deploy |
Run npm run verify before pushing.
astro.config.mjs # site URL, MDX, sitemap, Shiki dual themes
wrangler.jsonc # Workers assets config and custom domain
public/
_headers # cache-control for fonts and hashed assets
fonts/*.woff2 # self-hosted faces
scripts/
verify.mjs # browser check suite behind `npm run verify`
shots.mjs # screenshot helper
src/
content.config.ts # post collection schema
content/posts/ # one folder per post — the whole publishing surface
_TEMPLATE/index.mdx # leading _ = never published
<slug>/index.mdx # folder name is the URL; images sit beside it
pages/ # index, [...slug], about, topics, rss.xml, 404
layouts/ # Base.astro, Post.astro
components/ # Figure, Callout, Eyebrow, header/footer, chips
styles/blog.css # the single stylesheet
lib/ # rehype plugin for focusable code blocks
docs/
BLOG_SPEC.md # design and content spec — the visual source of truth
ARCHITECTURE.md # what gets built, and why each decision was made
AUTHORING.md # how to write and ship a post
cp -r src/content/posts/_TEMPLATE src/content/posts/my-new-postThe folder name becomes the URL (my-new-post/ → https://blog.foyzul.com/my-new-post/). Fill in
the frontmatter, write the body, and the homepage picks it up — index.astro is never edited to
publish.
---
title: My new post
description: One sentence, used on the index, in RSS, and as the meta description.
pubDate: 2026-08-05
tags: [astro, cloudflare]
lang: en # `bn` switches to Noto Sans Bengali and adds a বাংলা chip
draft: false # true = visible in dev, excluded from build, index, RSS, sitemap
---Invalid frontmatter fails the build with the offending field name; the schema lives in
src/content.config.ts. docs/AUTHORING.md covers the rest — reserved slugs, the components
available inside posts, and the two MDX rules that will bite you when authoring inline SVG diagrams.
The site deploys from the Cloudflare dashboard (Workers & Pages → the Worker → Settings → Builds)
with build command npm run build and deploy command npx wrangler deploy. The Worker name in the
dashboard must match name in wrangler.jsonc (blog-foyzul-com) or the build fails.
For a manual deploy:
npm run deployblog.foyzul.com is configured as a Custom Domain, so Cloudflare provisions the DNS record and the
certificate on deploy. workers_dev and preview_urls are both off deliberately — a second live
copy would be duplicate content for crawlers.
Code is MIT licensed — see LICENSE. Post content in src/content/posts/ is © Foyzul
Karim; please ask before republishing it.