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
htmlpnpm install --frozen-lockfile
pnpm run buildVerifying a clean build
html — Full repository onlypnpm run smoke
pnpm run checkpnpm 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
| Folder | What it is | Edit? |
|---|---|---|
html/pages/ | One file per screen, plus a front-matter line | Yes |
html/partials/ | The markup every page shares: nav, header, footer | Yes |
html/layouts/ | The page frames: shell, bare, and landing | Yes |
html/assets/js/ | The behaviour | Yes |
html/assets/vendor/ | Third-party files, copied in | No — node vendor.mjs |
html/shared/ | Mirror of shared/, including the component CSS in shared/styles/ | No — node ../tools/sync-shared.mjs |
html-dist/ | What ships | No — 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 overfile://, 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.