@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.
- package/CHANGELOG.md +71 -0
- package/README.md +39 -4
- package/dist/asset-modules.d.ts +81 -0
- package/dist/cli/check/config.js +5 -1
- package/dist/cli/check/css.js +27 -13
- package/dist/cli/check/loader.js +4 -0
- package/dist/cli/check/section-assets.js +58 -0
- package/dist/cli/check/stages/size.js +124 -8
- package/dist/cli/check/stages/tree.js +26 -27
- package/dist/cli/dev/build-css.js +91 -0
- package/dist/cli/dev/catalog.js +35 -0
- package/dist/cli/dev/compose-template.js +38 -0
- package/dist/cli/dev/content-override.js +58 -0
- package/dist/cli/dev/discover.js +35 -0
- package/dist/cli/dev/fill-state.js +75 -0
- package/dist/cli/dev/index.js +32 -0
- package/dist/cli/dev/inspect.js +38 -0
- package/dist/cli/dev/island-bootstrap.js +38 -0
- package/dist/cli/dev/island-map.js +35 -0
- package/dist/cli/dev/island-transform.js +31 -0
- package/dist/cli/dev/manifest-instances.js +31 -0
- package/dist/cli/dev/render-section.js +24 -0
- package/dist/cli/dev/screenshot-target.js +24 -0
- package/dist/cli/dev/section-page.js +54 -0
- package/dist/cli/dev/server.js +480 -0
- package/dist/cli/dev/slot-schema.js +12 -0
- package/dist/cli/dev/structure-summary.js +118 -0
- package/dist/cli/dev/tailwind.js +32 -0
- package/dist/cli/dev/vite-server.js +33 -0
- package/dist/cli/dev/workspace.js +63 -0
- package/dist/cli/link/index.js +74 -0
- package/dist/cli/link/overlay.js +59 -0
- package/dist/cli/link/watch.js +25 -0
- package/dist/cli/new/emit.js +48 -0
- package/dist/cli/new/index.js +69 -0
- package/dist/cli/new/scaffold/section/_Renderer.tsx +20 -0
- package/dist/cli/new/scaffold/section/_fill-spec.ts +18 -0
- package/dist/cli/new/scaffold/section/_fixtures.ts +13 -0
- package/dist/cli/new/scaffold/section/_schema.ts +24 -0
- package/dist/cli/new/scaffold/template/_manifest.json +9 -0
- package/dist/cli/new/scaffold/template/sections/hero/ExpandableText.tsx +45 -0
- package/dist/cli/new/scaffold/template/sections/hero/Renderer.tsx +40 -0
- package/dist/cli/new/scaffold/template/sections/hero/components/Feature.tsx +14 -0
- package/dist/cli/new/scaffold/template/sections/hero/fill-spec.ts +29 -0
- package/dist/cli/new/scaffold/template/sections/hero/fixtures.ts +41 -0
- package/dist/cli/new/scaffold/template/sections/hero/schema.ts +60 -0
- package/dist/cli/new/scaffold/template/theme.css +24 -0
- package/dist/cli/new/scaffold/template/theme.ts +27 -0
- package/dist/cli/new/scaffold-assets.js +20 -0
- package/dist/cli/pack/collect.js +44 -0
- package/dist/cli/pack/guards.js +57 -0
- package/dist/cli/pack/index.js +66 -0
- package/dist/cli/pack/manifest.js +15 -0
- package/dist/cli/pack/zip.js +86 -0
- package/dist/cli.js +80 -14
- package/dist/dev-client/assets/index-DfEfrp6P.css +1 -0
- package/dist/dev-client/assets/index-bpMP0b0W.js +50 -0
- package/dist/dev-client/index.html +13 -0
- package/dist/eslint/is-client-file.d.ts +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/islands/detect.d.ts +16 -0
- package/dist/islands/detect.js +22 -0
- package/dist/islands/discover.d.ts +5 -0
- package/dist/islands/discover.js +19 -0
- package/dist/islands/esbuild-plugin.d.ts +13 -0
- package/dist/islands/esbuild-plugin.js +39 -0
- package/dist/islands/wrap.d.ts +11 -0
- package/dist/islands/wrap.js +48 -0
- package/dist/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +77 -0
- package/dist/node_modules/magic-string/dist/magic-string.es.js +939 -0
- package/dist/package.js +1 -1
- package/dist/ssr.d.ts +31 -0
- package/dist/ssr.js +46 -0
- package/docs/INDEX.md +9 -1
- package/docs/assets.md +128 -0
- package/docs/check.md +6 -3
- package/docs/dev.md +187 -0
- package/docs/islands.md +5 -2
- package/docs/llms.txt +41 -3
- package/docs/new.md +27 -0
- package/docs/overview.md +57 -0
- package/docs/pack.md +46 -0
- package/docs/primitives.md +5 -6
- package/docs/quickstart.md +102 -0
- package/docs/recipes/INDEX.md +27 -0
- package/docs/recipes/bind-property-fact.md +58 -0
- package/docs/recipes/collection-slot.md +100 -0
- package/docs/recipes/fill-spec-decision.md +66 -0
- package/docs/recipes/fixture-states.md +68 -0
- package/docs/recipes/image-slot-crop.md +98 -0
- package/docs/recipes/interactive-island.md +100 -0
- package/docs/recipes/organize-section-folder.md +72 -0
- package/docs/recipes/prepare-submission.md +55 -0
- package/docs/recipes/second-template.md +49 -0
- package/docs/recipes/select-slot.md +59 -0
- package/docs/recipes/static-asset.md +103 -0
- package/docs/recipes/third-party-package.md +90 -0
- package/docs/rules/INDEX.md +4 -2
- package/docs/rules/bundle-binary-asset.md +33 -52
- package/docs/rules/bundle-incomplete.md +2 -2
- package/docs/rules/css-reason.md +3 -3
- package/docs/rules/no-bare-css-import.md +8 -8
- package/docs/rules/no-css-import-from-render-path.md +7 -8
- package/docs/rules/size-assets.md +88 -0
- package/docs/rules/size-island-bundle.md +123 -0
- package/docs/rules/size-section-css.md +19 -14
- package/docs/schema-system.md +28 -28
- package/docs/theme-and-css.md +2 -2
- package/docs/vocabulary.md +98 -0
- package/package.json +11 -3
- package/tsconfig.json +1 -1
package/dist/package.js
CHANGED
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-
|
|
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
|
|
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-
|
|
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 `
|
|
10
|
-
[Theme and CSS](theme-and-css.md#generating-themecss)
|
|
11
|
-
|
|
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.
|
package/docs/overview.md
ADDED
|
@@ -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).
|