A static personal site built with Hugo. Two complete visual modes share the same content and navigation:
- Meadow (dark): Pixel font, animated canvas meadow with interactive flowers, smoky rose/purple palette. Writing, projects, software, friends, about.
- Dacian (light): Google Sans + Google Sans Code, CSS Grid card layouts, professional blue palette. Writing, projects, software, friends, about.
Switching modes swaps the entire visual identity — fonts, layout, colors, interactions, easter eggs — while keeping content and structure identical. Both modes are bounded to the viewport: the shell never scrolls globally, and long pages scroll internally (slugs, lists, and card grids), with headers held static.
- Hugo extended 0.168 or newer
- bun for TypeScript compilation and dev commands
bun dev # build the bundle, watch TS, and preview Hugo
bun run build # compile TS (minified) and write site to ./public
bun check # type-check, format-check, build, and run smoke tests
bun run smoke # verify the built site for both modes
bun fmt # format supported source files with Bun
bun new my-post-title # add content/writing/my-post-title.md
bun clean # remove build output and compiled JSThe commands are bun scripts in package.json; run them by hand if you do not want to install bun.
content/
_index.md homepage front matter
writing/_index.md writing list
writing/*.md posts
404.md not found page
about.md
friends.md
meadow.md palette page (both modes)
projects.md card grid (dacian) / list (meadow), deck: projects
software.md card grid (dacian) / list (meadow), deck: software
data/cards.json tiles for projects and software
data/navigation.json shared destinations rendered per mode
package.json bun scripts (replaces justfile)
tsconfig.json TypeScript config
layouts/
_default/baseof.html shared shell (head, header, footer, scripts)
_default/list.html writing list and tag filter
_default/page.html generic page
_default/cards.html card grid page
_default/meadow.html palette page
writing/page.html post layout
index.html homepage
partials/ head, header, footer and mode shells
dacian-header.html Dacian thin band masthead (brand + page title)
dacian-directory.html Dacian board navigation for the home page
mode-picker.html shared Meadow/Dacian radio group
robots.txt robots file with sitemap link
static/
assets/ images, favicons, badges, posters and the pixel font
css/base.css shared styles
css/meadow.css meadow mode styles
css/dacian.css dacian mode styles
js/index.js single bundle (mode-specific code guarded internally)
_headers Cloudflare Pages headers
assets/js/ TypeScript sources (not in public/)
tools/smoke.ts post-build checks for both themes (used by bun run check)
The site name, description, greeting, email and GitHub link live in hugo.toml. Page copy is in content/, and the project and software tiles are in data/cards.json.
The wordmark is pixel art built in assets/js/meadow.ts from the G glyph map. Recompile with bun run ts after editing any .ts file.
- Push this repository to GitHub.
- In the Cloudflare dashboard, open Workers & Pages, choose Create, then Pages, then Connect to Git.
- Pick the repository and use these settings:
- Framework preset: Hugo
- Build command:
bun run build - Build output directory:
public - Environment variable:
HUGO_VERSION = 0.168.0
- Add your domain under Custom domains. HTTPS is automatic.
static/_headers is copied into the build, so Cloudflare applies the cache and security headers.
- Mode is stored in
localStorage(site-mode) and URL (?mode=meadow|dacian) - The page mode is the fallback; system preference is only used if no mode exists
- Meadow uses text navigation with a canvas-meadow wordmark; Dacian uses a card board on the home page and a card grid on Projects/Software; both share a footer picker (radio group)
- Both modes keep the viewport bounded:
.fadenever scrolls globally, and lists, card grids, and prose regions scroll internally with sticky, static headers - One bundle contains shared code and mode-specific modules with lifecycle guards
bunfig.toml contains Bun formatter preferences. Hugo templates and Markdown
with Go-template syntax are excluded through .prettierignore, which is the
ignore file Bun's formatter supports; formatter exclusions cannot currently be
declared inside [fmt] in bunfig.toml.
bun run check runs the type checker, the formatter check, a Hugo build with
warnings as errors, and bun run smoke. tools/smoke.ts asserts the built
output for both modes: canvases, navigation links, the mode picker, the home
board, the palette page, the writing list/slug, and the absence of the removed
Made page and of anything from the glocean.dev light variant.
- Meadow mode originated from glocean.dev (MIT)
- Dacian mode originated from dacian.co (MIT)
- Full credits in CREDITS.md