Source for badcluster.dev — static HTML/CSS, Python build script for the blog.
  • HTML 62.1%
  • Python 21.2%
  • CSS 16.7%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
arelas 3f12957333 Point Forgejo links at git.badcluster.dev
The instance's canonical hostname moved off git.zephyrftp.com.
FORGEJO_URL in build.py, README's deploy section, and both blog posts'
prose links all updated; rebuilt to regenerate every page.
2026-08-25 12:18:06 -04:00
assets Add ZephyrFTP to projects, move all links off GitHub onto Forgejo 2026-08-25 11:15:20 -04:00
blog Point Forgejo links at git.badcluster.dev 2026-08-25 12:18:06 -04:00
contact Point Forgejo links at git.badcluster.dev 2026-08-25 12:18:06 -04:00
posts Point Forgejo links at git.badcluster.dev 2026-08-25 12:18:06 -04:00
build.py Point Forgejo links at git.badcluster.dev 2026-08-25 12:18:06 -04:00
index.html Point Forgejo links at git.badcluster.dev 2026-08-25 12:18:06 -04:00
README.md Point Forgejo links at git.badcluster.dev 2026-08-25 12:18:06 -04:00
styles.css Remove the mouse-follow spotlight glow effect 2026-08-25 11:42:55 -04:00

badcluster.dev

Source for the Bad Cluster site. Static HTML/CSS, no JS framework, no build tooling beyond a small Python script that generates the blog pages from Markdown.

Structure

index.html          Generated - home page (don't hand-edit, see build.py)
styles.css           Shared stylesheet for every page
assets/              Images
blog/                Generated - blog index + one folder per post
contact/             Generated - contact page
posts/               Source - Markdown files, one per blog post
build.py             Regenerates index.html, blog/, and contact/ from
                     the content below and posts/*.md

Anything under blog/ or contact/, plus index.html itself, is build output. Hand-editing those will work until the next python3 build.py, which overwrites them. Edit posts/*.md for blog content, or build.py itself for the home page, header/footer, or page templates.

Adding a blog post

Create a new file in posts/, named however you like (the date in the filename is just for sorting convenience - the real date comes from frontmatter). Format:

---
title: Your Post Title
date: 2026-07-04
summary: One sentence, shown on the blog index page.
---

Body in Markdown from here on. Headings, links, lists, `inline code`,
and fenced code blocks all work.

Then rebuild:

python3 build.py

Requires the markdown package: pip install markdown.

Editing the home page or templates

The home page content, header, footer, and page shell are all defined as Python strings in build.py (see build_home(), render_header(), render_footer(), page_shell()). Edit those, then rerun the build.

Contact form

The contact form posts to Formspree. To make it actually deliver messages to you:

  1. Create a free account at formspree.io and add a new form.
  2. Copy the form endpoint URL it gives you (looks like https://formspree.io/f/abcd1234).
  3. In build.py, replace the FORM_ACTION placeholder with that URL.
  4. Rerun python3 build.py and redeploy.

Until that's done, the form is wired up correctly but has nowhere real to send to - build.py will print a reminder every time you build if the placeholder is still in place.

Local preview

Browser security blocks the page's absolute paths (/styles.css, etc.) when opened directly as a file:// URL. Serve it locally instead:

python3 -m http.server 8000

Then open http://localhost:8000/.

Deploying

Hosted on linveo1, git-based: /var/www/badcluster.dev on the server is a clone of this repo (origin = git.badcluster.dev/arelas/badcluster-site). To publish a change: push here, then on the server cd /var/www/badcluster.dev && git pull. No GitHub Pages, no CNAME file, no separate build/deploy step beyond that.