Static bilingual site (EN / NL) built with Eleventy + Markdown. Deployed to Cloudflare Workers.
Content lives in two language files — always update both in the same change:
| File | Language |
|---|---|
src/en/index.md |
English |
src/nl/index.md |
Dutch |
The shared HTML template (src/_includes/base.njk) handles the <head>, header, and footer.
| Syntax | Purpose |
|---|---|
::: settings |
At-a-glance settings table |
::: step N "Title" "Summary" |
Numbered step card |
::: more "Label" |
Hover/tap popover inside a step |
::: cli "Label" |
Monospace command block with copy button |
All containers are closed with :::.
npm install # once
npm start # dev server at http://localhost:8080 with live reloadnpm run build # outputs to _site/This site uses Cloudflare Workers Assets — a Cloudflare product that combines
a static file host with a programmable edge worker. It is configured in wrangler.jsonc
and deployed with wrangler deploy.
Eleventy builds the site into _site/. Cloudflare serves those files globally from
its CDN, the same way it would for any static site.
On top of the static files, a thin Cloudflare Worker runs at the CDN edge for every
request to /. It reads the browser's Accept-Language header and issues a 302
redirect to /nl/ for Dutch speakers or /en/ for everyone else — all before a
single byte of HTML is sent to the browser.
GET /
Accept-Language: nl-NL, nl;q=0.9 → 302 /nl/
Accept-Language: en-US, en;q=0.9 → 302 /en/
Any request that is not / (the two language pages, assets, sitemap, etc.) is passed
straight through to the static asset handler via env.ASSETS.fetch(request).
The Eleventy dev server (npm start) does not run worker.js. Instead, src/index.njk
generates a _site/index.html with the same JS redirect logic so that navigating to
localhost:8080/ still works. In production this file is never reached because the
worker intercepts the request first.
npm run deploy # builds _site/ then runs wrangler deployAll absolute URLs in the build (canonical tag, hreflang, Open Graph, sitemap, robots.txt) are driven by a single environment variable. Set it once in the Cloudflare dashboard and nothing else needs touching when you change domains.
In the Cloudflare dashboard → Workers & Pages → your Worker → Settings → Variables and Secrets:
SITE_URL = https://yourdomain.com
How it works:
| Env variable | When it applies |
|---|---|
SITE_URL set in dashboard |
Production — always use this |
CF_PAGES_URL |
Injected by Cloudflare for preview deployments |
| Neither set | Falls back to https://settings.woodwar.com (local dev only) |
The src/assets/og-image.png share card is generated by a standalone script using only Node.js built-ins (no extra dependencies):
node scripts/gen-og-image.jsRe-run this if you need to update the image. The result is committed to the repository and served as a static asset.
src/
en/
index.md ← English content
en.11tydata.json ← EN language data (brand name, nav labels)
nl/
index.md ← Dutch content
nl.11tydata.json ← NL language data
_includes/
base.njk ← shared HTML template (head, header, footer)
_data/
site.js ← site-wide data (reads SITE_URL from env)
assets/
main.css ← stylesheet
main.js ← copy-to-clipboard + popover behaviour
og-image.png ← Open Graph share image (1200×630)
_redirects ← Cloudflare Workers redirect rules (future use)
sitemap.njk → builds /sitemap.xml
robots.njk → builds /robots.txt
scripts/
gen-og-image.js ← generates og-image.png (run manually)
worker.js ← Cloudflare Workers entry point: language redirect / → /nl/ or /en/
wrangler.jsonc ← Cloudflare Workers deployment config
.eleventy.js ← build config