Skip to content
HTML edition

HTML edition

Plain static HTML — no framework, no bundler for the buyer, no server. pnpm run build writes html-dist/; open html-dist/index.html straight off the filesystem, or serve the folder from any static host.

Install and build

In html
pnpm install --frozen-lockfile
pnpm run build

Verifying a clean build

In html — Full repository only
pnpm run smoke
pnpm run check

pnpm run smoke opens html-dist/ over file:// and checks that it works. pnpm run check fails the moment html/shared/ has been edited by hand instead of shared/ at the repository root — see Generated folders.

Which folder to edit

FolderWhat it isEdit?
html/pages/One file per screen, plus a front-matter lineYes
html/partials/The markup every page shares: nav, header, footerYes
html/layouts/The page frames: shell, bare, and landingYes
html/assets/js/The behaviourYes
html/assets/vendor/Third-party files, copied inNo — node vendor.mjs
html/shared/Mirror of shared/, including the component CSS in shared/styles/No — node ../tools/sync-shared.mjs
html-dist/What shipsNo — node build.mjs

Editing html-dist/ is the mistake this layout invites, so it is worth saying plainly: nothing in it survives the next build. The file you want is in html/pages/ or html/partials/.

If you would rather not build at all, that is supported — take html-dist/, delete html/, and edit the finished pages directly. You lose the partials, so a change to the sidebar becomes a change to every page, which is the trade.

Why everything is a classic script

A browser refuses to load an ES module over file://. Since opening the package off the filesystem is the whole promise of this edition, nothing here can be a module, and everything arrives as a global.

That is also why three things are injected rather than fetched:

  • the icon sprite, because an external <use href="sprite.svg#id"> is a request that renders nothing at all over file://, silently;
  • the message catalogues, because fetch() cannot read a JSON file there;
  • the demo dataset, for the same reason.

The mock API, the formatters and the chart theme are not rewritten for this edition. bundle.mjs bundles the shared ones — the same code the React, Next, Vue and Nuxt editions import — into assets/vendor/orion-shared.js. A hand-written copy would be a second implementation that agrees with the first until the day it does not.

Each page loads the dataset afresh, so the changes a visitor makes are kept in localStorage by shared/mock/journal.mjs — the requests that changed something, replayed over the fixtures when the next page opens. A setting saved on one page is there on the next, and after a reload. The demo bar's Reset demo button empties the journal; where storage is blocked the demo runs in memory, one page at a time.

The one deliberate exception is assets/js/preferences.js, which has to run in <head> before anything paints and therefore cannot wait for a 33 kB bundle to parse. It repeats the four option lists and the storage key; the smoke test compares them against shared/ui/preferences.mjs on every run, so the copy cannot drift quietly.