@homepages/template-kit 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +39 -4
  3. package/dist/asset-modules.d.ts +81 -0
  4. package/dist/cli/check/config.js +5 -1
  5. package/dist/cli/check/css.js +27 -13
  6. package/dist/cli/check/loader.js +4 -0
  7. package/dist/cli/check/section-assets.js +58 -0
  8. package/dist/cli/check/stages/size.js +124 -8
  9. package/dist/cli/check/stages/tree.js +26 -27
  10. package/dist/cli/dev/build-css.js +91 -0
  11. package/dist/cli/dev/catalog.js +35 -0
  12. package/dist/cli/dev/compose-template.js +38 -0
  13. package/dist/cli/dev/content-override.js +58 -0
  14. package/dist/cli/dev/discover.js +35 -0
  15. package/dist/cli/dev/fill-state.js +75 -0
  16. package/dist/cli/dev/index.js +32 -0
  17. package/dist/cli/dev/inspect.js +38 -0
  18. package/dist/cli/dev/island-bootstrap.js +38 -0
  19. package/dist/cli/dev/island-map.js +35 -0
  20. package/dist/cli/dev/island-transform.js +31 -0
  21. package/dist/cli/dev/manifest-instances.js +31 -0
  22. package/dist/cli/dev/render-section.js +24 -0
  23. package/dist/cli/dev/screenshot-target.js +24 -0
  24. package/dist/cli/dev/section-page.js +54 -0
  25. package/dist/cli/dev/server.js +480 -0
  26. package/dist/cli/dev/slot-schema.js +12 -0
  27. package/dist/cli/dev/structure-summary.js +118 -0
  28. package/dist/cli/dev/tailwind.js +32 -0
  29. package/dist/cli/dev/vite-server.js +33 -0
  30. package/dist/cli/dev/workspace.js +63 -0
  31. package/dist/cli/link/index.js +74 -0
  32. package/dist/cli/link/overlay.js +59 -0
  33. package/dist/cli/link/watch.js +25 -0
  34. package/dist/cli/new/emit.js +48 -0
  35. package/dist/cli/new/index.js +69 -0
  36. package/dist/cli/new/scaffold/section/_Renderer.tsx +20 -0
  37. package/dist/cli/new/scaffold/section/_fill-spec.ts +18 -0
  38. package/dist/cli/new/scaffold/section/_fixtures.ts +13 -0
  39. package/dist/cli/new/scaffold/section/_schema.ts +24 -0
  40. package/dist/cli/new/scaffold/template/_manifest.json +9 -0
  41. package/dist/cli/new/scaffold/template/sections/hero/ExpandableText.tsx +45 -0
  42. package/dist/cli/new/scaffold/template/sections/hero/Renderer.tsx +40 -0
  43. package/dist/cli/new/scaffold/template/sections/hero/components/Feature.tsx +14 -0
  44. package/dist/cli/new/scaffold/template/sections/hero/fill-spec.ts +29 -0
  45. package/dist/cli/new/scaffold/template/sections/hero/fixtures.ts +41 -0
  46. package/dist/cli/new/scaffold/template/sections/hero/schema.ts +60 -0
  47. package/dist/cli/new/scaffold/template/theme.css +24 -0
  48. package/dist/cli/new/scaffold/template/theme.ts +27 -0
  49. package/dist/cli/new/scaffold-assets.js +20 -0
  50. package/dist/cli/pack/collect.js +44 -0
  51. package/dist/cli/pack/guards.js +57 -0
  52. package/dist/cli/pack/index.js +66 -0
  53. package/dist/cli/pack/manifest.js +15 -0
  54. package/dist/cli/pack/zip.js +86 -0
  55. package/dist/cli.js +80 -14
  56. package/dist/dev-client/assets/index-DfEfrp6P.css +1 -0
  57. package/dist/dev-client/assets/index-bpMP0b0W.js +50 -0
  58. package/dist/dev-client/index.html +13 -0
  59. package/dist/eslint/is-client-file.d.ts +4 -0
  60. package/dist/index.d.ts +1 -0
  61. package/dist/islands/detect.d.ts +16 -0
  62. package/dist/islands/detect.js +22 -0
  63. package/dist/islands/discover.d.ts +5 -0
  64. package/dist/islands/discover.js +19 -0
  65. package/dist/islands/esbuild-plugin.d.ts +13 -0
  66. package/dist/islands/esbuild-plugin.js +39 -0
  67. package/dist/islands/wrap.d.ts +11 -0
  68. package/dist/islands/wrap.js +48 -0
  69. package/dist/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +77 -0
  70. package/dist/node_modules/magic-string/dist/magic-string.es.js +939 -0
  71. package/dist/package.js +1 -1
  72. package/dist/ssr.d.ts +31 -0
  73. package/dist/ssr.js +46 -0
  74. package/docs/INDEX.md +9 -1
  75. package/docs/assets.md +128 -0
  76. package/docs/check.md +6 -3
  77. package/docs/dev.md +187 -0
  78. package/docs/islands.md +5 -2
  79. package/docs/llms.txt +41 -3
  80. package/docs/new.md +27 -0
  81. package/docs/overview.md +57 -0
  82. package/docs/pack.md +46 -0
  83. package/docs/primitives.md +5 -6
  84. package/docs/quickstart.md +102 -0
  85. package/docs/recipes/INDEX.md +27 -0
  86. package/docs/recipes/bind-property-fact.md +58 -0
  87. package/docs/recipes/collection-slot.md +100 -0
  88. package/docs/recipes/fill-spec-decision.md +66 -0
  89. package/docs/recipes/fixture-states.md +68 -0
  90. package/docs/recipes/image-slot-crop.md +98 -0
  91. package/docs/recipes/interactive-island.md +100 -0
  92. package/docs/recipes/organize-section-folder.md +72 -0
  93. package/docs/recipes/prepare-submission.md +55 -0
  94. package/docs/recipes/second-template.md +49 -0
  95. package/docs/recipes/select-slot.md +59 -0
  96. package/docs/recipes/static-asset.md +103 -0
  97. package/docs/recipes/third-party-package.md +90 -0
  98. package/docs/rules/INDEX.md +4 -2
  99. package/docs/rules/bundle-binary-asset.md +33 -52
  100. package/docs/rules/bundle-incomplete.md +2 -2
  101. package/docs/rules/css-reason.md +3 -3
  102. package/docs/rules/no-bare-css-import.md +8 -8
  103. package/docs/rules/no-css-import-from-render-path.md +7 -8
  104. package/docs/rules/size-assets.md +88 -0
  105. package/docs/rules/size-island-bundle.md +123 -0
  106. package/docs/rules/size-section-css.md +19 -14
  107. package/docs/schema-system.md +28 -28
  108. package/docs/theme-and-css.md +2 -2
  109. package/docs/vocabulary.md +98 -0
  110. package/package.json +11 -3
  111. package/tsconfig.json +1 -1
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.3.0";
2
+ var version = "0.4.0";
3
3
 
4
4
  //#endregion
5
5
  export { version };
package/dist/ssr.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ import { ResolvedFixture } from "./schema/fixture-schema.js";
2
+ import "./schema/index.js";
3
+ import { hasClientDirective } from "./eslint/is-client-file.js";
4
+ import { isClientComponentFile, islandKey } from "./islands/detect.js";
5
+ import { wrapClientModuleSource } from "./islands/wrap.js";
6
+ import { findSectionIslands } from "./islands/discover.js";
7
+ import { islandTransformEsbuildPlugin } from "./islands/esbuild-plugin.js";
8
+ import { ComponentType } from "react";
9
+ //#region src/ssr/index.d.ts
10
+ type LoadedSection = {
11
+ Renderer: ComponentType<Record<string, unknown>>;
12
+ snapshotFixtures: ResolvedFixture[];
13
+ invariantFixtures: ResolvedFixture[];
14
+ schemaSlots: Record<string, {
15
+ type: string;
16
+ editable_by_user?: boolean;
17
+ required?: boolean;
18
+ }>;
19
+ };
20
+ type SectionModuleLoader = (dir: string, file: string) => Promise<unknown>;
21
+ declare const nativeSectionLoader: SectionModuleLoader;
22
+ /**
23
+ * Load a bundle's Renderer plus its resolved snapshot and invariant fixture
24
+ * sets from the bundle directory `dir`. `load` resolves each of the four
25
+ * contract files; it defaults to native dynamic import.
26
+ */
27
+ declare function loadSectionBundle(dir: string, load?: SectionModuleLoader): Promise<LoadedSection>;
28
+ /** Render a resolved fixture's props to static markup — the byte-for-byte page render. */
29
+ declare function renderSectionHtml(renderer: ComponentType<Record<string, unknown>>, props: Record<string, unknown>): string;
30
+ //#endregion
31
+ export { LoadedSection, SectionModuleLoader, findSectionIslands, hasClientDirective, isClientComponentFile, islandKey, islandTransformEsbuildPlugin, loadSectionBundle, nativeSectionLoader, renderSectionHtml, wrapClientModuleSource };
package/dist/ssr.js ADDED
@@ -0,0 +1,46 @@
1
+ import { FixtureModuleShape, buildFixtureSet, buildInvariantFixtures } from "./schema/fixture-schema.js";
2
+ import { hasClientDirective } from "./eslint/is-client-file.js";
3
+ import { isClientComponentFile, islandKey } from "./islands/detect.js";
4
+ import { findSectionIslands } from "./islands/discover.js";
5
+ import { wrapClientModuleSource } from "./islands/wrap.js";
6
+ import { islandTransformEsbuildPlugin } from "./islands/esbuild-plugin.js";
7
+ import { createElement } from "react";
8
+ import { basename, join } from "node:path";
9
+ import { pathToFileURL } from "node:url";
10
+ import { renderToStaticMarkup } from "react-dom/server";
11
+
12
+ //#region src/ssr/index.ts
13
+ const nativeSectionLoader = (dir, file) => import(pathToFileURL(join(dir, file)).href);
14
+ /**
15
+ * Load a bundle's Renderer plus its resolved snapshot and invariant fixture
16
+ * sets from the bundle directory `dir`. `load` resolves each of the four
17
+ * contract files; it defaults to native dynamic import.
18
+ */
19
+ async function loadSectionBundle(dir, load = nativeSectionLoader) {
20
+ const rendererMod = await load(dir, "Renderer.tsx");
21
+ const fixturesMod = await load(dir, "fixtures.ts");
22
+ const schemaMod = await load(dir, "schema.ts");
23
+ const parsed = FixtureModuleShape.parse(fixturesMod.default);
24
+ const selfAnchor = schemaMod.schema.meta?.anchor ?? basename(dir);
25
+ return {
26
+ Renderer: rendererMod.default,
27
+ snapshotFixtures: buildFixtureSet({
28
+ schema: schemaMod.schema,
29
+ module: parsed,
30
+ selfAnchor
31
+ }),
32
+ invariantFixtures: buildInvariantFixtures({
33
+ schema: schemaMod.schema,
34
+ module: parsed,
35
+ selfAnchor
36
+ }),
37
+ schemaSlots: schemaMod.schema.slots
38
+ };
39
+ }
40
+ /** Render a resolved fixture's props to static markup — the byte-for-byte page render. */
41
+ function renderSectionHtml(renderer, props) {
42
+ return renderToStaticMarkup(createElement(renderer, props));
43
+ }
44
+
45
+ //#endregion
46
+ export { findSectionIslands, hasClientDirective, isClientComponentFile, islandKey, islandTransformEsbuildPlugin, loadSectionBundle, nativeSectionLoader, renderSectionHtml, wrapClientModuleSource };
package/docs/INDEX.md CHANGED
@@ -2,7 +2,7 @@
2
2
  purpose: Router for the docs corpus that ships inside the @homepages/template-kit npm package.
3
3
  status: living
4
4
  related: []
5
- updated: 2026-07-15
5
+ updated: 2026-07-16
6
6
  ---
7
7
  # template-kit docs — router
8
8
 
@@ -13,11 +13,19 @@ detail.
13
13
 
14
14
  | Doc | What it covers | Read when you're touching... |
15
15
  |---|---|---|
16
+ | `quickstart.md` | The cold-start tutorial: create a workspace, preview the starter's `hero` section, read its four files, make one edit, and check it — owns the author-loop anchor every recipe links to | your first section, end to end |
17
+ | `overview.md` | The one-page mental model: workspace → templates → sections, the four reserved files, and the server-vs-client mapping | orienting yourself before your first section |
16
18
  | `llms.txt` | Machine-readable index of this corpus for an agent working inside an installed kit | orienting an agent in a consuming workspace |
17
19
  | `schema-system.md` | The section authoring contract: `schema.ts` / `fill-spec.ts` / `fixtures.ts` / `Renderer.tsx`, `SectionProps` inference, the closed source/transform vocabulary, and the golden property fixtures | authoring or changing any section |
20
+ | `vocabulary.md` | The closed authoring vocabulary — the 9 slot types, the `direct()` source fields, and the `derived()` transforms, with selection semantics | choosing which slot type / source / transform to use |
18
21
  | `primitives.md` | The five contract primitives (`Section`, `Slot`, `SlotItem`, `SlotGroup`, `Image`) and the marker attributes they emit | writing a section's markup |
22
+ | `assets.md` | Static assets (svg/images/fonts): where they live (template level or a section folder, if referenced), referencing them from a section's CSS with `url()`/`@font-face` or importing them into a component, and the asset-vs-content-image distinction | shipping a logo, texture, icon, or brand font with a template |
19
23
  | `theme-and-css.md` | The two CSS entries, the one-Tailwind-import rule, `TokenTheme` (your own token names → your own Tailwind utilities), the `template-kit theme` command that generates `theme.css` from `theme.ts`, the breakpoint ladder, and the opt-in `content-visibility` recipe for long, image-heavy pages | a template's theme, its CSS entry, or a long page with a live map or full-screen overlay |
20
24
  | `islands.md` | Interactivity: `"use client"` components, the serializable-props rule, editor options, the loader | anything interactive in a section |
21
25
  | `eslint.md` | The `@homepages/template-kit/eslint` preset: the two-line config, the ESLint + parser install it expects, and its glob scope | wiring up lint in a template workspace |
22
26
  | `check.md` | The `template-kit check` CLI command: the toolchain it resolves from your workspace, its stages, `--all`/`--json`, output shape, and exit code | running `check` locally or wiring it into CI |
27
+ | `dev.md` | The `template-kit dev` CLI command: the local preview server, its home launcher and two tool cards (full template viewer, canvas mirror), the canvas mirror's selection overlay and per-slot edit panel, live reload, the content-override protocol, and the render/inspect/screenshot endpoints | previewing sections as you author them |
28
+ | `new.md` | The `template-kit new` / `new-section` CLI commands: scaffolding a new template with a working starter section, or adding a bare section to an existing template | adding a template or a section to a workspace |
29
+ | `pack.md` | The `template-kit pack` CLI command: builds a template's submission zip — template source, the workspace lockfile, and a stamped `pack-manifest.json`; refuses on a failing `check`, a missing lockfile, or a local-protocol dependency | packaging a template for submission |
23
30
  | `rules/` | One page per rule-id (`template-kit/*`), across both venues — the id printed by a lint or `check` failure resolves here. `rules/INDEX.md` routes them; `rules/server-vs-client.md` is the concept the server-rendering rules assume | any lint or `check` failure |
31
+ | `recipes/` | Task recipes: how do I add a slot, bind a fact, add an island, package a template… Each is a minimal delta from the starter's `hero` section, ending in the author loop. `recipes/INDEX.md` routes them by task | doing something specific and not sure which file to touch |
package/docs/assets.md ADDED
@@ -0,0 +1,128 @@
1
+ ---
2
+ purpose: Authoring static assets (svg, images, fonts) at the template level or in a section folder, and how they differ from content images.
3
+ status: living
4
+ related: [INDEX.md, primitives.md, theme-and-css.md, rules/size-assets.md, rules/bundle-binary-asset.md, rules/no-raw-element.md]
5
+ updated: 2026-07-16
6
+ ---
7
+ # Static assets
8
+
9
+ A **static asset** is a file that is part of the template itself — a logo, a
10
+ background texture, an icon, a brand webfont. It is authored once, is the
11
+ **same for every deliverable** rendered from the template, and is referenced
12
+ either from your CSS or from a component.
13
+
14
+ ## Where an asset lives
15
+
16
+ An asset lives in one of two places:
17
+
18
+ - **Template level** — e.g. `templates/<template>/assets/` — shared across every
19
+ section in the template. Reference it from a section's CSS with a `../../assets/…`
20
+ path (below).
21
+ - **Section folder** — e.g. `sections/<section>/assets/` — local to that one
22
+ section. A section-folder asset must be *referenced*: imported in `Renderer.tsx`
23
+ or `url()`-referenced from one of the section's CSS files. An unreferenced binary
24
+ in a section folder is flagged by [`bundle-binary-asset`](rules/bundle-binary-asset.md) —
25
+ it has no build meaning and can never be served.
26
+
27
+ Keep a logo, texture, or font that several sections share in the template-level
28
+ folder; keep one that only a single section uses alongside that section.
29
+
30
+ ## Supported files
31
+
32
+ ```
33
+ .svg .png .jpg .jpeg .gif .webp .avif .ico images
34
+ .woff .woff2 .ttf .otf .eot fonts
35
+ .mp4 .webm .mov .mp3 .wav audio / video
36
+ .pdf .zip documents
37
+ ```
38
+
39
+ ## Referencing an asset — from CSS with `url()`
40
+
41
+ Reference a template-level asset from a section's `styles.css` with `url()` —
42
+ a `@font-face` `src` or a `background-image` resolves to the asset's built URL,
43
+ identical in the `template-kit dev` preview, in the editor, and on the published
44
+ page. Hand-CSS opens with a `css-reason:` comment (see
45
+ [`css-reason`](rules/css-reason.md)):
46
+
47
+ ```css
48
+ /* css-reason: brand webfont + crest watermark, both template assets */
49
+ @font-face {
50
+ font-family: "Brand Serif";
51
+ src: url("../../assets/fonts/brand.woff2") format("woff2");
52
+ font-display: swap;
53
+ }
54
+
55
+ .hero-crest {
56
+ background-image: url("../../assets/crest.svg");
57
+ }
58
+ ```
59
+
60
+ The `../../assets/…` path climbs out of the section folder to the template-level
61
+ `assets/` directory. A font you declare here is available to the whole section.
62
+ If the family you want is one the template's theme already loads, reference that
63
+ instead — see [Theme and CSS](theme-and-css.md); a font the page already has
64
+ costs nothing to reuse.
65
+
66
+ A **section-folder** asset is referenced the same way, with a section-relative
67
+ path instead — no climbing out of the folder:
68
+
69
+ ```css
70
+ /* css-reason: hero watermark, local to this section */
71
+ .hero-crest {
72
+ background-image: url("./assets/crest.svg");
73
+ }
74
+ ```
75
+
76
+ ## Referencing an asset — from a component with `import`
77
+
78
+ An asset you render as an element — a logo, a crest — can be imported in the component and
79
+ handed to the `Image` primitive, whose `src` accepts a URL string:
80
+
81
+ ```tsx
82
+ import { Image } from "@homepages/template-kit";
83
+ import crest from "./assets/crest.svg";
84
+
85
+ export default function Renderer() {
86
+ return <Image src={crest} alt="" />;
87
+ }
88
+ ```
89
+
90
+ The import resolves to the asset's built `/section-assets/…` URL — the same URL in the
91
+ `dev` preview, the editor, and the published page. The kit ships an ambient TypeScript
92
+ declaration that types every supported asset import as that URL string, so the import
93
+ typechecks with no per-workspace setup. Use `alt=""` for a purely decorative
94
+ asset. (Never a raw `<img>` — `Image` carries the responsive/fallback contract; a raw
95
+ `<img>` is rejected by [`no-raw-element`](rules/no-raw-element.md).)
96
+
97
+ ## Which to use
98
+
99
+ - CSS decoration (backgrounds, `@font-face`, masks) → `url()`.
100
+ - An asset rendered as an element → `import` + `<Image>`.
101
+ - A tiny glyph or icon → inline `<svg>` markup — no file at all.
102
+
103
+ ## Assets vs. content images
104
+
105
+ These are two different things; keep them apart.
106
+
107
+ - A **static asset** is authored template decoration — a logo, a texture, a brand
108
+ font. It ships inside the template, is the same on every deliverable, and you
109
+ reference it from your CSS with `url()` or by importing it into a component (above).
110
+ - A **content image** is the property's own content — listing photos, floor
111
+ plans, a headshot. It differs on every deliverable and flows through the
112
+ section's slots via the [`Image`](primitives.md) descriptor, never as a file in
113
+ your template. Declare an image slot in your schema and render it with the
114
+ `Image` primitive; the platform supplies the actual photo per deliverable.
115
+
116
+ Rule of thumb: if the picture is the same for every home the template ever
117
+ renders, it's an asset — place it at the template level or in the section folder
118
+ and reference it from CSS or a component `import`. If it changes with the
119
+ property, it's content — give it a slot. Never hard-code a listing photo as an
120
+ asset.
121
+
122
+ ## Keep them lean
123
+
124
+ Every asset a section pulls in counts toward its asset budget — see
125
+ [`size-assets`](rules/size-assets.md). Fonts and images are already compressed, so
126
+ the budget is raw bytes: subset a webfont to the weights and characters you use,
127
+ export images at the size they render, and drop anything a section doesn't
128
+ actually reference.
package/docs/check.md CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  purpose: The `template-kit check` command — what it runs, its flags, its output shape, and its exit code.
3
3
  status: living
4
- related: [INDEX.md, rules/INDEX.md, eslint.md, theme-and-css.md]
4
+ related: [INDEX.md, rules/INDEX.md, eslint.md, theme-and-css.md, dev.md]
5
5
  updated: 2026-07-15
6
6
  ---
7
7
  # template-kit check
@@ -62,8 +62,9 @@ one class of fix at a time:
62
62
  stray `[object Object]`, a bare `null`/`undefined`/`NaN` leaf, a `src`-less `<img>` —
63
63
  plus, for the `typical` fixture, that the editor's sidebar order matches the page's
64
64
  real render order.
65
- 6. **size** — every section's renderer bundle and compiled stylesheet, gzipped, against a
66
- fixed byte budget.
65
+ 6. **size** — every section's renderer bundle, compiled stylesheet, and largest island
66
+ (browser) bundle, gzipped, against a fixed byte budget; and its static assets
67
+ (svg/images/fonts), budgeted by raw bytes since they are already compressed.
67
68
 
68
69
  **The dependency gates** run once per invocation, never once per template — they are
69
70
  properties of the workspace's one lockfile and installed tree, not of any one template:
@@ -119,3 +120,5 @@ submission.
119
120
  each, Rule / Reason / Fix.
120
121
  - [ESLint preset](eslint.md) — the one-file-at-a-time half of the same rule set, run
121
122
  directly by your editor, not only through `check`.
123
+ - [Dev](dev.md) — the other workspace command: preview sections in a browser as you
124
+ author, before running `check` as the acceptance gate.
package/docs/dev.md ADDED
@@ -0,0 +1,187 @@
1
+ ---
2
+ purpose: The `template-kit dev` command — the local preview server, what it serves, its URLs, and the agent-facing render endpoints.
3
+ status: living
4
+ related: [INDEX.md, theme-and-css.md, schema-system.md, check.md, islands.md]
5
+ updated: 2026-07-16
6
+ ---
7
+ # template-kit dev
8
+
9
+ `template-kit dev` is the local preview server for a workspace's templates. It
10
+ renders every section against its fixtures the same way the platform does —
11
+ server-side, with your template's theme and compiled Tailwind utilities — so you
12
+ see real output while you author, and reload as you edit.
13
+
14
+ ## Usage
15
+
16
+ ```bash
17
+ template-kit dev # serve every template in the workspace
18
+ template-kit dev <template> # preselect one template in the UI
19
+ template-kit dev --port 4000 # choose the port (default 5180)
20
+ ```
21
+
22
+ Run it from the workspace root or from inside a template's own folder — running
23
+ from inside a template preselects it on the home launcher. The server serves every
24
+ template it finds under `templates/`, regardless of where you launched it; the
25
+ command runs until you interrupt it (Ctrl-C).
26
+
27
+ ### What it needs installed
28
+
29
+ ```bash
30
+ npm i -D vite @vitejs/plugin-react tailwindcss @tailwindcss/cli
31
+ ```
32
+
33
+ Like [`check`](check.md), `dev` runs **your** toolchain, resolved from your
34
+ workspace — Vite renders and reloads your sections, and Tailwind compiles your
35
+ theme's utilities. None of these are dependencies of the kit; they resolve from
36
+ your install, so the versions you pinned are the ones that run. A missing one is
37
+ reported by name with the line to install it.
38
+
39
+ Screenshots (below) additionally need Playwright, which is optional:
40
+
41
+ ```bash
42
+ npm i -D @playwright/test && npx playwright install chromium
43
+ ```
44
+
45
+ ## What you get
46
+
47
+ Open the server's URL and you land on the **home launcher** (`/`) — a template
48
+ selector plus two tool cards, each a self-contained view you open in its own
49
+ page and return from by clicking the `template-kit dev` wordmark:
50
+
51
+ - **Full template viewer** — opens `/template/<template>`, the whole page as it
52
+ publishes.
53
+ - **Canvas mirror** — a section and fixture selector, opening
54
+ `/canvas/<template>/<section>/<fixture>`.
55
+
56
+ The home launcher is the only place navigation happens; both tools are
57
+ near-chromeless and URL-keyed, so a link to either reloads and shares exactly
58
+ the view you're looking at.
59
+
60
+ ### Live reload
61
+
62
+ The server watches your template files. Editing a section reloads any open
63
+ preview of it; editing a theme recompiles its Tailwind utilities and hot-swaps
64
+ the stylesheet without a full reload.
65
+
66
+ ### Interactivity (islands)
67
+
68
+ A section's `"use client"` components hydrate in the preview. The server renders
69
+ the section — including each client component's initial HTML — then the browser
70
+ loads React and hydrates only those components, so they become interactive while
71
+ the rest of the page stays static. A section with no `"use client"` component
72
+ loads no React at all. Editing a client component reloads the preview, re-rendered
73
+ and re-hydrated. See [Islands](islands.md) for the client-component contract.
74
+
75
+ ### Overriding content
76
+
77
+ A preview page accepts an `overrides` query parameter — base64-encoded JSON,
78
+ deep-merged onto the fixture's resolved props — so you can probe a section against
79
+ content the fixtures don't carry without editing `fixtures.ts`. The decoded JSON
80
+ shape mirrors the props themselves:
81
+
82
+ ```json
83
+ { "slots": {}, "options": {}, "nav": { "selfAnchor": "", "anchors": {} }, "variants": {} }
84
+ ```
85
+
86
+ All four top-level keys are optional. Merging follows one rule at every level: a
87
+ plain object merges key by key with the fixture's value; anything else — a scalar,
88
+ an array, or `null` — replaces the fixture's value outright. A key the override
89
+ omits keeps the fixture's value; a key set explicitly to `null` replaces it with
90
+ `null`, so you can probe an empty/missing state a fixture doesn't otherwise cover.
91
+ The encoded payload is capped at 16 KB.
92
+
93
+ ## Views: the full template, and the section render behind the canvas
94
+
95
+ The whole template renders at `/template/<template>` — every section in
96
+ `manifest.json` order, at each section's base fixture (with that instance's manifest
97
+ options), with the template theme and page shell applied and islands hydrating, as
98
+ the published site stacks it. Open it from the home launcher's **Full template
99
+ viewer** card, or point a browser tool at the URL directly. (Cross-section
100
+ `reconcile` rules are a publish-time behavior and are not applied here.)
101
+
102
+ Every section also has a standalone render at
103
+ `/sections/<template>/<section>/<fixture>` — the document the `/api/screenshot`
104
+ endpoint targets for a single section. It is not a navigation destination in its
105
+ own right.
106
+
107
+ ## Canvas mirror
108
+
109
+ `/canvas/<template>` — open it from the home launcher's **Canvas mirror** card,
110
+ or directly — mirrors the whole template in an iframe: every section instance
111
+ from `manifest.json`, stacked in one scroll container, exactly as the deliverable
112
+ renders. A paint-only overlay sits on top of it, scoped per section instance —
113
+ the same slot name in two different sections selects independently. Click a slot
114
+ to select it (a gold ring); hover to preview one (a blue ring). Only slots that
115
+ instance's schema marks `editable_by_user: true` select individually — clicking
116
+ anywhere else in a section selects that section instance as a whole. The overlay
117
+ never changes the rendered markup. A mode toggle switches between this canvas
118
+ view and a published view — the clean full-template render, the same page with
119
+ the overlay off. A `template-kit dev` wordmark in the toolbar links back to the
120
+ home launcher (`/`) — the canvas mirror is otherwise chromeless.
121
+
122
+ The current template, mode, and selection are encoded in the URL as
123
+ `/canvas/<template>?mode=&sel=`, where `sel` names a section instance
124
+ (`<instanceId>`), a slot within it (`<instanceId>~<slotId>`), or one item of
125
+ a repeating slot (`<instanceId>~<slotId>~<itemIndex>`) — so a canvas link
126
+ reloads and shares exactly the view you're looking at.
127
+
128
+ Selecting a slot (not the section instance as a whole) docks an **edit panel**
129
+ beside the iframe. One JSON editor handles every slot type — you edit the
130
+ slot's value in its own JSON shape rather than switching between per-type
131
+ widgets — alongside a **fill-state badge** (filled / empty / missing) for the
132
+ selected slot and a per-instance **fixture picker**. The picker lists that
133
+ section's whole fixture corpus, including its synthetic empty-content
134
+ fixtures, so you can drive a slot into an empty or missing state without
135
+ touching `fixtures.ts`. Applying a JSON edit, switching the fixture, or
136
+ resetting the instance re-renders that section instance alone, the same way
137
+ it renders in the stack, and swaps its HTML into the iframe in place — no
138
+ page reload, no other instance disturbed — then re-anchors the selection ring
139
+ to the refreshed markup. The kit dev server is the harness's only backend, so
140
+ this loop never touches fixture files, a database, or another service. Edit
141
+ state lives only in the tab: nothing is written to disk, and reloading the
142
+ page loses it. A re-rendered instance's `"use client"` islands stay static —
143
+ they do not re-hydrate — until you rebuild; the panel notes this so it isn't
144
+ mistaken for a bug.
145
+
146
+ ## Agent-facing endpoints
147
+
148
+ These render a section — or the whole template — without a browser, for an agent
149
+ verifying its work from a terminal.
150
+
151
+ - `/api/inspect` — the low-context semantic view of a section×fixture as JSON: each
152
+ declared slot with its `type`, `required`, `fillState` (`filled` / `empty` /
153
+ `missing`), whether it actually `rendered`, a compact `value` preview, and its
154
+ `schema.ts:<line>`; plus resolved `options` / `variants` and `nullCorpus` (the
155
+ synthetic empty-state fixtures, one per optional slot, you can walk to review every
156
+ missing-content branch). An `empty`/`missing` slot that still shows `rendered: true`
157
+ is a broken empty shell. Takes `template`, `section`, `fixture`, the same base64
158
+ `overrides` parameter as a preview page, and `format=text` to add the indented
159
+ structure outline.
160
+ - `/api/render` — the full structured render payload for one section×fixture
161
+ (`html`, `resolvedProps`, `slotTree`, `islandMap`, `slotSchema` — that
162
+ section's slot types and editability — and `slotFills`, the same per-slot
163
+ fill state `/api/inspect` reports); GET takes `overrides`, POST takes an
164
+ unencoded JSON body. POST also accepts a manifest `instanceId` in place of
165
+ `section`+`fixture`: the server resolves that instance's section, its
166
+ manifest options, and its default fixture the same way the whole-template
167
+ render does, so re-rendering one instance matches how it renders in the
168
+ stack. This is what the canvas mirror's edit panel calls to apply a slot
169
+ edit or switch a fixture.
170
+ - `/api/template-schema?template=<t>` — the editability map for the whole
171
+ template: every manifest section instance with its per-slot `type` and
172
+ whether it's user-editable. The canvas mirror's overlay gates selection on
173
+ this endpoint, not on `/api/render`'s per-section `slotSchema`.
174
+ - `/api/screenshot` — a full-page PNG. Give it `template` + `section` + `fixture` for a
175
+ section, or `template` alone for the whole template. `width=<px>` sets the viewport
176
+ (default 1280). Requires Playwright (`npm i -D @playwright/test && npx playwright
177
+ install chromium`); without it the endpoint answers with the install line.
178
+
179
+ `/api/catalog.json` returns the full **template × section × fixture** matrix.
180
+
181
+ ## See also
182
+
183
+ - [Theme and CSS](theme-and-css.md) — the theme the preview compiles, and the
184
+ `template-kit theme` command that generates it.
185
+ - [Schema system](schema-system.md) — the fixtures the preview renders and the
186
+ props the `overrides` parameter merges onto.
187
+ - [Check](check.md) — the acceptance gate to run once a section previews cleanly.
package/docs/islands.md CHANGED
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  purpose: How interactivity works in a template — client components ("islands"), their props contract, and their editor options.
3
3
  status: living
4
- related: [INDEX.md, eslint.md]
5
- updated: 2026-07-14
4
+ related: [INDEX.md, eslint.md, dev.md]
5
+ updated: 2026-07-15
6
6
  ---
7
7
 
8
8
  # Islands: interactivity in a template
@@ -17,6 +17,9 @@ If a page contains no islands, it loads no React and no island code — every pa
17
17
  still ships the small island loader (a few KB, gzipped), but that loader only ever
18
18
  reaches React through a dynamic import it takes when an island is actually on the page.
19
19
 
20
+ [`template-kit dev`](dev.md) hydrates a section's islands in its preview, so you can
21
+ exercise one as you author it.
22
+
20
23
  ## Writing one
21
24
 
22
25
  ```tsx
package/docs/llms.txt CHANGED
@@ -6,16 +6,30 @@
6
6
 
7
7
  Status: 0.0.x. The schema system, the contract primitives, and the theme system are
8
8
  complete — a section is authorable end to end against the kit. The `template-kit`
9
- CLI's `check` and `theme` commands are implemented — see [Check](check.md) and
10
- [Theme and CSS](theme-and-css.md#generating-themecss); its other commands arrive with
11
- the surfaces they drive.
9
+ CLI's `check`, `theme`, `dev`, `pack`, `new`, and `new-section` commands are
10
+ implemented — see [Check](check.md), [Theme and CSS](theme-and-css.md#generating-themecss),
11
+ [Dev](dev.md), [Pack](pack.md), and [Adding templates](new.md); its remaining
12
+ commands arrive with the surfaces they drive.
12
13
 
13
14
  Everything an author writes a section against is imported from the package root —
14
15
  `@homepages/template-kit`, no subpath imports. The two CSS entries
15
16
  (`./styles.css`, `./base.css`) are the deliberate exception.
16
17
 
18
+ ## Start here
19
+
20
+ To build a section, start at [Quickstart](quickstart.md), then the task
21
+ [Recipes](recipes/INDEX.md) ("how do I X" → the recipe). For the mental model,
22
+ read [Overview](overview.md). For the closed sets — slot types, `direct()`
23
+ sources, `derived()` transforms — see [Vocabulary](vocabulary.md). When a lint
24
+ or `check` prints a rule id, resolve it at `rules/<id>.md`.
25
+
17
26
  ## Docs
18
27
 
28
+ - [Quickstart](quickstart.md): the cold-start tutorial — create a workspace,
29
+ preview the starter's `hero` section, read its four files, make one edit,
30
+ and check it. Owns the author-loop anchor every recipe ends with.
31
+ - [Overview](overview.md): the one-page mental model — workspace → templates →
32
+ sections, the four reserved files, and the server-vs-client mapping.
19
33
  - [Schema system](schema-system.md): the section authoring contract. A section is
20
34
  four files — `schema.ts` (slots + options + `meta.groups`), `fill-spec.ts` (how AI
21
35
  fills them), `fixtures.ts` (preview content, built on the kit's golden property
@@ -23,6 +37,9 @@ Everything an author writes a section against is imported from the package root
23
37
  `SectionProps<typeof schema>`). Covers the `as const satisfies` requirement, binding
24
38
  slots to property facts with `direct()`/`derived()` over a closed vocabulary, and
25
39
  fact-driven variants.
40
+ - [Vocabulary](vocabulary.md): the closed authoring vocabulary — the 9 slot
41
+ types, the `direct()` source fields, and the `derived()` transforms, with
42
+ selection semantics (which one to pick).
26
43
  - [Contract primitives](primitives.md): the only five components the kit ships —
27
44
  `Section` (full-bleed section root), `Slot` (`data-slot-id`), `SlotItem` (per-item
28
45
  handle in an `object_list` / `image_collection`), `SlotGroup` (`data-slot-group`),
@@ -41,6 +58,10 @@ Everything an author writes a section against is imported from the package root
41
58
  rendered inside the section.
42
59
  - [README](../README.md): install, exports, and the release flow.
43
60
  - [Islands](islands.md): interactivity — client components, the serializable-props contract, editor options, and the browser loader.
61
+ - [Static assets](assets.md): shipping a logo, texture, icon, or brand font with a
62
+ template — where an asset file lives (template level, or a section folder if the
63
+ section references it), referencing it from a section's CSS with `url()`/`@font-face`
64
+ or importing it into a component, and the asset-vs-content-image distinction.
44
65
  - [ESLint preset](eslint.md): `@homepages/template-kit/eslint`, the flat-config preset —
45
66
  a two-line config with no parser wiring, over an ESLint + `@typescript-eslint/parser`
46
67
  install of your own (the kit depends on neither — both resolve from your workspace).
@@ -49,7 +70,24 @@ Everything an author writes a section against is imported from the package root
49
70
  TypeScript, ESLint and esbuild, resolved from your workspace), its six per-template
50
71
  stages, the once-per-invocation dependency gates, `--all`/`--json`, the report shape,
51
72
  and the exit code. Thresholds are fixed platform acceptance criteria, not configurable.
73
+ - [Dev](dev.md): the `template-kit dev` CLI command — the local preview server. Its
74
+ home launcher with a full template viewer (`/template/<template>`) and a canvas
75
+ mirror (`/canvas/<template>/<section>/<fixture>`), live reload, the Vite + Tailwind
76
+ toolchain it resolves from your workspace, and the `inspect` / `render` / `screenshot`
77
+ endpoints an agent uses to verify a render from a terminal.
78
+ - [Pack](pack.md): the `template-kit pack <template>` CLI command — builds a template's
79
+ submission zip (the template's source, the workspace lockfile, and a stamped
80
+ `pack-manifest.json`), and the conditions it refuses to pack under: a failing
81
+ `check`, a missing lockfile, or a local `file:`/`link:` dependency.
82
+ - [Adding templates](new.md): the `template-kit new <template>` and
83
+ `template-kit new-section <template>/<section>` CLI commands — scaffolding a new
84
+ template with a working starter section, or adding a bare section (the four
85
+ reserved contract files, no island) to an existing template. Both refuse to
86
+ overwrite an existing folder.
52
87
  - [Rules](rules/INDEX.md): every rule-id a lint or `check` failure can print, under the
53
88
  `template-kit/<id>` namespace, one page each at `docs/rules/<id>.md` — Rule / Reason /
54
89
  Fix / Before→After. **The loop:** see an id in a failure → read `docs/rules/<id>.md` →
55
90
  fix. Nothing else to search for; the id is the filename.
91
+ - [Recipes](recipes/INDEX.md): task recipes routed by "how do I…" — each one a minimal,
92
+ complete delta from the starter's `hero` section (never a from-scratch file), ending in
93
+ the [author loop](quickstart.md#the-author-loop).
package/docs/new.md ADDED
@@ -0,0 +1,27 @@
1
+ ---
2
+ purpose: Add a template or a section to a workspace with template-kit new / new-section.
3
+ status: living
4
+ related: [INDEX.md, dev.md, check.md]
5
+ updated: 2026-07-15
6
+ ---
7
+
8
+ # Adding templates and sections
9
+
10
+ A workspace starts with one template (`templates/starter/`). Add more without
11
+ duplicating any shared context — templates are plain folders, and the kit,
12
+ configs, and docs stay at the workspace root.
13
+
14
+ ## `template-kit new <template>`
15
+
16
+ Creates `templates/<template>/` with a working starter section (a text slot, an
17
+ image slot, a fact-bound slot, an option, fixtures, a fill spec, a nested
18
+ `components/` folder, and a `"use client"` island), plus `theme.ts`/`theme.css`
19
+ and a `manifest.json`. It passes `check` as-is; edit it into your template.
20
+
21
+ ## `template-kit new-section <template>/<section>`
22
+
23
+ Adds `templates/<template>/sections/<section>/` — the four reserved contract
24
+ files (`Renderer.tsx`, `schema.ts`, `fill-spec.ts`, `fixtures.ts`), no island.
25
+ Reference the new section by name in the template's `manifest.json`.
26
+
27
+ Both refuse to overwrite an existing folder.
@@ -0,0 +1,57 @@
1
+ ---
2
+ purpose: The mental model in one page — workspace, templates, sections, and the server-vs-client mapping.
3
+ status: living
4
+ related: [schema-system.md, islands.md, rules/server-vs-client.md]
5
+ updated: 2026-07-15
6
+ ---
7
+ # Overview
8
+
9
+ ## Workspace → templates → sections
10
+
11
+ A **workspace** is the container: one kit version, one shared config. A
12
+ **template** is the authored, submitted unit — a folder of sections plus a
13
+ manifest and theme. A **section** is four reserved files in an otherwise
14
+ free-form folder:
15
+
16
+ ```
17
+ templates/<template>/sections/<section>/
18
+ Renderer.tsx
19
+ schema.ts
20
+ fill-spec.ts
21
+ fixtures.ts
22
+ ```
23
+
24
+ Everything else in that folder — extra components, an island, helpers — is
25
+ yours. See [Adding templates and sections](new.md).
26
+
27
+ ## The four files
28
+
29
+ | File | Declares | Reference |
30
+ |---|---|---|
31
+ | `schema.ts` | The slots and options the section has | [schema-system.md](schema-system.md#schemats--what-the-section-contains) |
32
+ | `fill-spec.ts` | How AI fills those slots | [schema-system.md](schema-system.md#fill-spects--how-ai-fills-the-slots) |
33
+ | `fixtures.ts` | Sample content for local preview | [schema-system.md](schema-system.md#fixturests--content-for-local-preview) |
34
+ | `Renderer.tsx` | The markup | [schema-system.md](schema-system.md#renderertsx--props-derived-never-hand-written) |
35
+
36
+ ## Slots, options, fill, fixtures
37
+
38
+ A **slot** is editable content; an **option** is a behavior switch — see
39
+ [the schema system](schema-system.md#schemats--what-the-section-contains) for
40
+ the distinction and [the closed vocabulary](vocabulary.md) for the types.
41
+ **Fill** is how AI assigns slot values via `fill-spec.ts`'s decisions, covered
42
+ in [schema-system.md](schema-system.md#fill-spects--how-ai-fills-the-slots).
43
+ **Fixtures** are the sample content a section previews against, built on the
44
+ kit's golden property dataset — see
45
+ [schema-system.md](schema-system.md#fixturests--content-for-local-preview).
46
+
47
+ ## Server vs client — your Next.js instinct, mapped
48
+
49
+ A `Renderer.tsx` is a server component: a pure function of props, ships no
50
+ JS. Interactivity goes in a `"use client"` island, hydrated in the browser.
51
+ The directive is **per file**. See [server vs client](rules/server-vs-client.md)
52
+ for the precise rule and [Islands](islands.md) for how to write one.
53
+
54
+ ## The author loop
55
+
56
+ For the write → preview → check cycle, see
57
+ [the author loop](quickstart.md#the-author-loop).