output_dir in site/config.toml, so zola build in site/ and zola --root site build from the root both write ./public. The ignore is /public/, not site/public/. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
11 KiB
W1: Website
Status: phase 1 is built and checked in a browser (branch site, pull request to follow); the Install page works against the real server once Caddy allows the origin (below). Issue #12.
Goal: a public home for the project at roro9stack.net, separate from the blog (stories) and from Gitea (developers): what it is, how to install it, how to use each App, and the docs.
The home page was designed on a canvas in a Claude chat (a dark and a light theme, built on the device's own 256-colour palette, pixel-notched corners, DM Mono and Hanken Grotesk). It is the starting point, not the final copy: it has to say only what the firmware does today.
What was found while planning (2026-10-06)
roro9stack.netalready points at the server that hosts Gitea. Plain HTTP redirects to HTTPS; HTTPS has no certificate yet, which is the server's side to set up.- Release downloads from Gitea carry no CORS header, so a browser can't fetch the factory image from another origin as things are. Gitea is behind Caddy, which can add the header (below).
- Zola can read JSON from a URL at build time (
load_data), so the home page's "latest version" can come from the Gitea API. - There is no Gitea wiki: the design's "Wiki" links would 404.
- The blog is published by pulling its repository on the web server and running
zola build. The site does the same.
Decisions (design round 2026-10-06)
| # | Decision |
|---|---|
| Q175 | The site lives in this repository, in site/, so the documentation is built from docs/, CONTEXT.md and the README instead of being copied. |
| Q176 | Zola, like the blog. The design becomes a template, its tokens CSS custom properties. Dark and light follow the visitor's setting, with a visible switch. No JavaScript except the flasher's. |
| Q177 | Domain: roro9stack.net. The blog stays at experiments.twis.la. |
| Q178 | Publishing is the blog's way: the web server pulls main and runs zola build; that part is the maintainer's. Changes reach main through pull requests as everywhere. CI is split: a dedicated site job builds the site (zola build) when site/, docs/, README.md or CONTEXT.md change, and the firmware tests and builds skip a change that touches nothing else. A change that touches both runs both. |
| Q179 | Phases, each its own pull request: 1. the CI split, the home page, an Install page with the browser flasher, downloads and the changelog. 2. a user guide page per App. 3. how-tos and the FAQ. 4. developer docs generated from the repository. |
| Q180 | A browser flasher (ESP Web Tools), without copying the firmware. Caddy, in front of Gitea, adds Access-Control-Allow-Origin: https://roro9stack.net (and Vary: Origin) to GET and HEAD on /twisla/roro9stack/releases/download/* and /api/v1/repos/twisla/roro9stack/releases*: both are public already. The Install page asks the API for the latest release in the browser, finds the asset ending -factory.bin, and gives ESP Web Tools a manifest built on the spot, so it offers a new release as soon as it exists, with no rebuild. The library is vendored into site/static/ (Apache-2.0), not loaded from a CDN. The file's SHA-256 is shown on the page. Chrome or Edge on a desktop only; other browsers, and visitors without JavaScript, get the esptool steps on the same page. |
| Q181 | Docs for the latest version only. The changelog is the Gitea releases, read at build time. |
| Q182 | English only. |
| Q183 | The FAQ starts from real questions: the README, and issues labelled kind/docs. |
| Q184 | Fonts are self-hosted (no request to a third party). The hero keeps the design's illustrations, labelled as illustrations, and a section of real device screenshots is added. |
| Q185 | The site says only what the firmware does today. Planned features are marked as planned, with their milestone. The mesh messenger is planned: the LoRa Scanner listens, nothing is sent. |
| Q186 | Left out, each with its issue: a Gemini capsule mirror (#57), French (#58), docs per version (#59), search (#60). |
| Q187 | The site has no version of its own. Contact is contact@roro9stack.net, and the issue tracker. |
The design, reviewed
Kept as designed: the layout, the tokens, the nine App cards (their facts check out against the code: Probation 3 minutes, Safe Mode after 3 crashes, 60 seconds of typing before an update restarts the device).
Changed before it ships:
- Install, not Download, is the first action. Downloads are for developers; a visitor wants to try it.
- A "what you need" strip: Cardputer ADV, the Cap LoRa-1262 (only the radio needs it), a microSD card, Wi-Fi. And a plain status line: the version, and what isn't there yet.
- The mesh card and the hero no longer promise sending and reading mesh messages.
- The latest version is read from the API, not typed.
- "Wiki" is replaced by Docs. The updates section gains what v0.11.0 added: the device installs releases from the project's server itself.
- An independence line: not affiliated with or endorsed by M5Stack or Meshtastic.
- No cookies, no analytics, no third-party requests, said on the page (fonts self-hosted).
- The keyboard focus ring is invisible on the notched buttons:
clip-pathclips an outline. Another way to show focus is needed. - The wordmark SVGs carry an embedded C2PA content-credentials block: stripped from the site's copies.
Done when (phase 1)
- Pushing a change under
site/runs thesitejob and not the firmware tests; a firmware change runs the firmware jobs and not the site's. - The home page renders in both themes, at phone width, with the keyboard, and says nothing the firmware doesn't do.
- The latest version on the page is the latest release.
- The browser flasher installs the latest release on a Cardputer ADV from Chrome (tried by hand), the page shows the file's SHA-256, and the
esptoolsteps are on the same page. - A release published after the site was built is the one the Install page offers.
- The home and Downloads pages make no request to another origin, and the Install page only asks git.twis.la.
Work breakdown
- CI split: a
siteworkflow, path filters on the firmware workflow. - The skeleton:
site/with the tokens, fonts, base template and the theme switch. - The home page, from the design, with the changes above.
- Install and downloads: the flasher with its manifest built in the page, the
esptoolsteps, the changelog. Needs the Caddy headers on the Gitea host (the maintainer's side); the page is tested against them once they're in. - Checks, recorded here.
As built (phase 1)
- CI is split.
ci.yml(the firmware) haspaths-ignore: site/**, docs/**, README.md, CONTEXT.mdon pushes tomainand on pull requests.site.ymlrunszola checkandzola buildwith a Zola pinned by its checksum, thensite/tools/check_site.py, when those files change. Gitea's own source (v1.24, read, not run against the 1.27 server) shows that path filters count as matched for tag pushes, so a tag still releases. A change that touches both runs both. - The build output goes to
public/at the root of the repository, not intosite/:output_dir = "../public"insite/config.toml, sozola buildinsite/andzola --root site buildfrom the root agree, and git ignores/public/. - The site is in
site/:config.toml, templates (base, home, install, downloads, 404),data/for the App cards and the screenshots' captions,static/(stylesheet, theme switch, fonts, wordmark, icons, real screenshots, the vendored flasher).site/README.mdsays how to build it and what the server needs. - The home page follows the design. Changed from it: the hero and the mesh card promise nothing that isn't built (the mesh messenger is a "planned" card), an Install button first, a status box, a "what you need" row, a section of real screenshots, the updates section says the device installs releases itself, an independence line and a statement about cookies and third-party requests in the footer, the latest version read from the Gitea API at build time, and the nav's Wiki replaced. The two hero drawings are generated by
site/tools/make_illustrations.py(a port of the design's scripted shapes) as inline SVG. - The focus ring.
clip-pathclips outlines, so a focused notched control drops its notches and shows square corners and its ring. - The Install page asks the API for the latest release in the browser, builds the ESP Web Tools manifest as a blob, shows the version, size and SHA-256, and only ever hands the flasher a download from the project's own server for this repository. The flasher library (ESP Web Tools 10.4.0, Apache-2.0) is vendored, trimmed to the ESP32-S3. Fonts (DM Mono, Hanken Grotesk, SIL OFL) are self-hosted.
- The Downloads page lists the last 30 releases with their files, read at build time.
- The wordmark SVGs from the design carried an embedded C2PA content-credentials block; it is removed from the site's copies.
Checks (2026-10-06)
| Check | Result |
|---|---|
| The Site workflow's own commands, in a clean container with the pinned Zola | zola check clean, build and page checks pass |
tools/check_site.py |
4 pages, 0 problems: titles, descriptions, a language, every image with alt text, every local file referenced exists, nothing loaded from another origin |
| Browser tests (Chromium, 16 checks) | All pass: no request to another origin from the home, Downloads and 404 pages; no horizontal scroll at 1280 and 390 px; a visible focus ring on a notched button; the Install page reads the latest release, shows its version, size and SHA-256, builds a blob manifest naming an ESP32-S3 factory image at offset 0, and loads only git.twis.la; a download on another host is refused; the real server (no CORS header today) makes the page fall back to the esptool steps |
| The pages looked at | Home in dark and light, at desktop and phone width; Install; Downloads |
esptool against the real v0.11.0 factory image |
An ESP32-S3 image, bootloader at 0x0, partition table at 0x8000: flashing at offset 0 is right. The command's syntax was checked, not a flash |
Not checked:
- Flashing a real Cardputer from Chrome. It needs the device on a machine with a browser; the page's flasher logic is tested, the flashing itself isn't.
- Caddy's headers on the real server (not applied yet), and HTTPS on roro9stack.net (the name resolves, the certificate isn't there).
- The CI split on a change that touches only
site/ordocs/. This pull request touches both the workflows and the site, so it runs both; the first docs-only pull request will show it. - Firefox and Safari rendering, screen readers, and a printed page.
- The unverified wording in the Install text is kept to what's known: nothing about how long flashing takes, or what the screen shows in download mode.