This document covers everything needed to understand, maintain, or hand over the website. It assumes basic comfort with a terminal and Git.


Stack overview

LayerTechnology
Site generatorHugo v0.158.0 extended
ThemeCustom (themes/btc/)
CMSSveltia CMS (browser-based, GitHub backend)
HostingCloudflare Pages
CMS OAuth proxyCloudflare Worker
Member gatingMembershipWorks member lock (mfl.js)
Source controlGitHub (Brighton-Tri-Club/brightontri-website)

The site is fully static — Hugo builds Markdown content into HTML, which Cloudflare Pages serves. Member-only pages are hidden client-side by MembershipWorks’ member-lock script (see below).


Repository

  • GitHub org: Brighton-Tri-Club
  • Repo: brightontri-website (private)
  • Default branch: main — Cloudflare Pages deploys automatically on every push to main
  • Clone URL: git@github.com:Brighton-Tri-Club/brightontri-website.git (SSH) or https://github.com/Brighton-Tri-Club/brightontri-website.git (HTTPS)

The Sveltia CMS commits directly to main, so always run git pull before starting local work.


Local development

Prerequisites: Hugo extended v0.158.0 (installed via Homebrew).

cd /path/to/your/clone
hugo server --buildDrafts --disableFastRender

Then open http://localhost:1313 .

--disableFastRender is required — one of the event pages is large enough to hit Hugo’s fast render buffer limit without it.

The local CMS is not configured (Sveltia runs against the live GitHub repo). For CMS testing, add local_backend: true to static/admin/config.yml and run npx decap-server in a second terminal, but this is rarely needed.


Key files

FilePurpose
hugo.tomlHugo configuration (base URL, title, menu, params)
static/admin/config.ymlSveltia CMS configuration — collections, fields, media
themes/btc/layouts/partials/member-gate.htmlMembershipWorks member-lock snippet injected on gated pages
themes/btc/static/css/main.cssAll site styles
themes/btc/layouts/Hugo layout templates
content/All page content as Markdown
content/training-sessions/Individual training session pages (CMS folder collection)
content/resources-for-committee/This section — protected, committee only
content/resources-for-coaches.mdCoaches resources page — protected, coaches and committee

Cloudflare Pages

Project name: brightontri-website
Production URL: brightontri-website.pages.dev (also brightontri.org once the domain is cut over)
Build command: hugo --minify
Build output directory: public

Deployments trigger automatically on every push to main. Build logs are visible in the Cloudflare dashboard under Pages → brightontri-website → Deployments.

Environment variables

The site build itself needs no secrets. Any WA_* or JWT_SECRET variables still present in the Cloudflare Pages dashboard are leftovers from the archived WildApricot login system and can be deleted.


CMS OAuth proxy

The Sveltia CMS authenticates editors via GitHub OAuth. Because a static site can’t handle the OAuth server-side callback itself, a small Cloudflare Worker acts as the proxy.

Worker name: brightontri-cms-auth
Worker URL: brightontri-cms-auth.rob-be7.workers.dev

This is configured in static/admin/config.yml under backend.base_url. The Worker holds the GitHub OAuth app credentials — these are in the Cloudflare Workers dashboard under the worker’s environment variables.

If editors can’t log in to /admin/, check the Worker is deployed and its GitHub OAuth app credentials are valid.


Member-only pages

Member-only pages are gated with MembershipWorks’ member lock (mfl.js) — the same mechanism used on the old SquareSpace site. The page hides itself on load, then the MembershipWorks script reveals it if the visitor is logged in to MembershipWorks as a member of one of the allowed levels; otherwise a login prompt is shown.

This is a courtesy gate, not security. The content is still present in the page source, so anyone technical can read it. Do not put genuinely sensitive material (passwords, personal data, finances) on the website — use Google Drive for that. Gated pages carry a noindex tag so search engines don’t surface them.

To gate a page, add member_gate: true to its front matter (in the CMS or the Markdown file). Section index pages (_index.md) use cascade to apply the gate to every page in the section. The allowed membership level IDs are configured in hugo.toml (mwMemberLevels).

Gated areas

PathGate
/discounts/Members (all levels)
/resources-for-coaches/Members (all levels)
/resources-for-committee/ (and sub-pages)Members (all levels)

The member lock can only distinguish MembershipWorks membership levels, not roles — so coach and committee pages are currently visible to any logged-in member. If MembershipWorks levels (or lockable labels) exist for coaches/committee, their IDs can be substituted in the gate for those sections.


Coaches list refresh

The coaches page (/the-coaches/) is generated from a private Google Sheet (the coach planner). An Apps Script endpoint exposes the current active coaches as JSON.

To refresh the page after coaching staff changes, run from the project directory:

/refresh-coaches

This is a Claude Code slash command (defined in .claude/commands/refresh-coaches.md). It fetches the endpoint, groups coaches by qualification level, updates content/the-coaches.md, commits, and pushes.


Domain

The live domain is brightontri.org, currently still on Squarespace. When ready to cut over:

  1. In Cloudflare, add brightontri.org as a custom domain on the Pages project
  2. Update the nameservers at the domain registrar to point to Cloudflare
  3. Update baseURL in hugo.toml if not already set to https://brightontri.org
  4. Repoint the Casual Kit link on /kit/ — it targets the SquareSpace shop at brightontri.org/shop, which stops existing at cutover

Backup and recovery

The entire site source is in GitHub — this is the canonical backup. The public/ directory (build output) is not committed and is always regeneratable from source.

If the Cloudflare Pages project were lost, recovery is:

  1. Create a new Pages project connected to the GitHub repo
  2. Set build command to hugo --minify, output directory to public
  3. Re-add all environment variables (from the list above — retrieve secrets from whoever holds them)
  4. Re-add the custom domain

There is no database. All content is Markdown files in the repository.