Web
Client site platform
One component library, several client sites, and the release discipline that keeps them apart
- astro
- typescript
- css
- npm
Every small business site I build wants the same eight or nine things: a header that survives a long nav, a hero, a few content bands, a contact form that actually posts somewhere. Building those again per client is how you end up with nine slightly different headers and a bug fixed in one of them.
So the components live in one place, @iceniweb/ui, a published npm package, and each
client site is a thin layer of configuration and content on top. Forty-five components,
eight client folders, one person maintaining all of it.
None of the hard parts turned out to be component design. The hard part is that the thing I develop against and the thing that deploys are structurally different objects, and almost every serious bug this project has produced comes from that one fact. Locally there is a monorepo and every client is linked to the package source. On the build server there is a single client repository, no workspace, and whatever npm decides to install. The two disagree, and they disagree in the direction that hides the problem, because local development is the permissive one.
The articles in the sidebar go through that in detail: what is in the library and how a site is put together, the decisions the deploy topology forced, and what has gone wrong as a result.
Where it is at right now
@iceniweb/ui@0.9.0 is published and public on npm, and seven client sites plus one
landing-page project build from it.
Working now
- Forty-five components, published as one package, with a token-driven theme layer
- Four headers with configurable nav breakpoints and a page scroll lock while the mobile menu is open
- Contact forms with a real
res.okcheck, Formspark delivery and Cloudflare Turnstile - Responsive
srcsetoutput from the image components, so a phone stops downloading the desktop asset - Eight client folders, each its own repository, each deployable on its own
In development
- Moving the three sites on
^0.7.0and the two on older ranges onto 0.9.0, which means checking each one, because a shared component library is only a saving until an upgrade breaks three sites at once - A
turnstile.reset()call in the contact form’s success path, deferred to 0.9.1. A visitor who submits twice without reloading currently gets rejected the second time, because Turnstile tokens are single use andform.reset()clears the field without minting a new one
Planned
digital_ads_audithas a forked copy of the contact form that will never receive a package fix. It either reverts to the shared component or gets fixed by hand.- Some way of making an unknown prop fail rather than be silently dropped. Astro discards props a component has never heard of, with no warning, and that failure mode has now cost three separate sessions.
- Deciding whether
select-conceptsmoves inside the workspace glob, or whether the publish-first rule stands and gets written down properly.