{% extends "base.html" %} {% from "_architecture.html" import boundary, render_path %} {% from "_parts.html" import code, defs, lesson, prose, stage %} {% from "ui/layout.html" import grid, page_header, stack %} {# What the kit looks like from outside, in five pictures. These five existed before this page did, as mermaid blocks in a Chinese-only Markdown file sitting in `docs/` — which is the build output, so the file was published as plain text, never rendered, and never linked from any navigation. Five diagrams nobody could see. They live here now, and that file is gone: one home, in both languages, inside the vocabulary. Each section is one decision from `CHARTER.md` §3. The charter says what was decided and why; this page is what the decision looks like. Neither repeats the other, and the section says which letter it is drawing. House style: one idea per sentence, the takeaway first. #} {% set sections = [ {"id": "boundary", "num": "01", "title": "The package boundary"}, {"id": "loader", "num": "02", "title": "Template resolution"}, {"id": "timing", "num": "03", "title": "When the build runs"}, {"id": "render", "num": "04", "title": "The render path"}, {"id": "gate", "num": "05", "title": "The gate loop"}, ] %} {% block content %} {% call stack(8) %} {% set intro %} Five pictures, one for each architecture decision that is closed. What was decided and why is CHARTER.md §3; this is what the decisions look like. {% endset %} {{ page_header("How fjkit is put together", intro) }} {# ============================================================== 01 #} {% set a01 %}Your app touches the kit in four places. Everything else is sealed in the package.{% endset %} {% call lesson("boundary", "01", "The package boundary", a01, tag="A2") %} {% set fig = { "alt": "Four boxes in your app — routers, a page template, a partial and brand.css — each with one arrow to the fjkit box it uses.", "app": "YOUR APP", "routers": "routes, Depends, template choice", "page": "calls macros, writes no classes", "partial": "the htmx fragment", "brand": "the --primary lines, nothing else", "override": "overrides tokens", "shell": "head, theme, asset links", "vocab": "the closed vocabulary", "css": "eight packs, built at release", } %} {{ boundary(fig) }} {% call prose() %}
All four arrows point one way. Your app depends on fjkit; nothing in the kit reaches back into your app, and nothing in it knows the shape of your data.
brand.css is the whole configuration surface, and it is plain CSS variables.
No step compiles it.
Shadowing is the feature, not an accident: ChoiceLoader puts your directory in
front of the package's.
The price is that an ejected file stops receiving upstream fixes, which is why the
documentation does not lead with it. fjkit eject copies the macros you name
and re-exports the rest, so the file you own stays small — 49 lines against a 289-line
source, for badge.
Measured on the pack this site loads: {{ css.raw_kb }} KB raw, {{ css.gzip_kb }} KB gzip, against a {{ css.budget_kb }} KB budget the build enforces. About 217 KB of the raw figure is Basecoat's component layer, which ships whole whether fjkit writes one macro over it or forty.
{% endcall %} {% endcall %} {# ============================================================== 04 #} {% set a04 %}A full page and an htmx swap take different routes into the same template.{% endset %} {% call lesson("render", "04", "The render path: one partial, two entry points", a04, tag="A6") %} {% set fig = { "alt": "A GET and a POST both arrive at tasks/_board.html, which leaves as a whole document on one path and as a fragment on the other.", "only": "the only definition", "only_2": "included, and returned", "document": "a whole document", "document_2": "page.html, through the shell", "fragment": "a fragment", "fragment_2": "swapped into #board", } %} {{ render_path(fig) }} {% call prose() %}
_board.html has two ways in and two ways out, and one definition in the middle.
What the first paint shows and what the swap replaces it with are therefore rendered from
the same source, so they cannot drift.
partial= is the guarantee. One route answers a navigation with the page and an
htmx request with the fragment, so there is no second route to keep in step.
This feedback edge is the kit's only expansion mechanism, and the demo is its test case. Measured when 0.1 was finished: the same screens written before fjkit produced 258 violations; rewritten against the kit, 0.
The check also blocks a utility that merely happens to exist today. An app that borrows
gap-4 because the shell emits it breaks silently on the day the shell stops.