Bun · Addressable · TypeScript · CSS · Htmx: a no-build, server-rendered
hypermedia substrate. This directory is BATCH itself: the composition engine, HTTP/asset helpers,
and the audit engine. (The component catalog moved up to GRAIN, grain/catalog.) The app that
proves it out, a personal site whose home route / is a live desk you can watch the AI act on
(server-rendered HTML, htmx for reads/nav, one /intent door for writes), lives in
tjakoen.github.io, the composition root that wires
the stack together.
GRAIN (the design system) and MILL (the content engine) build on top of BATCH; the product and the portfolio consume the whole stack. → the why: PHILOSOPHY.md · the full reasoning (SSOT): ARCHITECTURE · the build rules: CONVENTIONS · where BATCH sits in the wider stack: bread.
Landing: tjakoen.github.io/batch · Docs: tjakoen.github.io/batch/docs.
BATCH's explanatory docs are canonically homed in the portfolio repo (
tjakoen.github.io/docs/batch/) and rendered there through MILL. This repo keeps its code, PLAN, and this README; it no longer ships adocs/folder.
What BATCH gives you (the full, tiered list is the source in ARCHITECTURE §"What BATCH gives you"):
- Hero: no build step (Bun runs the TS; even client
.tsis transpiled on request, no bundler) · static export as a projection of the running server (never a second renderer). - Also: the composition engine (zero runtime deps) · a generic SSE push hub · sitemap/SEO from
one source · a framework-generic perf + SEO/AEO audit engine. (The
/catalogcomponent browser moved up to GRAIN,grain/catalog.)
BATCH is a library: it has no server of its own. To work on the package:
bun install
bun test # unit + integration
bun run check # tsc, erasable-onlyTo see it composed and running, use the composition root (tjakoen.github.io), which wires BATCH + GRAIN + MILL and serves the live site.
- One folder per component:
<components-root>/<level>/<name>/<name>.{html,css}(atoms → molecules → organisms), CSS co-located with its template. The roots are wired by the consumer (tjakoen.github.io/config.ts; in this monorepo the components live ingrain/components+tjakoen.github.io/components). - Flat-file pages:
<pages-root>/about.html→/about(here:tjakoen.github.io/pages); folders only group subpages. URL mirrors the tree. Minimal JS sits in a<script>after the UI. - Pages compose components: rendered through the engine (
renderPage), so they use atomic tags. Raw HTML belongs inside a component's own.html, never in a page. - Sitemap + SEO:
/catalogsidebar lists Pages (site map) + Components; the same page list feeds/sitemap.xmland/robots.txt. Add a page → it appears in all three. - Tokens live up in GRAIN (
grain/styles). BATCH ships the mechanism, not a theme: it bundles each component's co-located CSS into/components.cssat request time, no build step. - Animated navigation: native CSS cross-document View Transitions (
@view-transition { navigation: auto }); plain<a href>loads animate, no client router. Navigate/↔/aboutto see it (Chromium). Honoursprefers-reduced-motion. - Component catalog at
/catalog: Storybook-style, generated server-side from each component's co-located<name>.md. Live render + copyable source + side nav. No build, no deps. Vanilla CSS: one class per element, variants as attributes (.btn[data-variant="soft"]); pseudo-states forced viadata-force. Two-layer tokens (primitives → semantic): change a primitive, every panel restyles. - Polymorphic atoms: one
b-textrendersh2/h3; oneb-buttonfor all variants. - Client
.tswith no bundler: modules served to the browser are transpiled on request behind a client-safe guard (nonode:/secrets), so a static-style page ships typed JS with no build. - One write path: all mutation flows through GRAIN's single door (
POST /intent→ render ops over SSE), not a separate CRUD API. BATCH just provides the generic SSE hub; the vocabulary lives ingrain/ai/*.
BATCH ships no server, so the /intent + SSE door runs from the composition root. Clone
tjakoen.github.io and bun run dev there, then open
/: the home route is a live desk where a click and an AI decision are the same Intent through
the one door (POST /intent), and the reply streams back as render ops over SSE (the AI spotlight,
the console narration). Navigate / ↔ /about for the View Transition.
tsconfig.json needs allowImportingTsExtensions: true for tsc to accept the
.ts-extension imports the architecture mandates. The doc's recommended-flags
list omits it.
🤖 Built with Claude. I don't prompt and pray, I prompt and prove. Every commit here is co-authored with an AI, on purpose. How I actually work with AI, receipts and all →