@homepages/template-kit 0.2.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 +222 -0
- package/README.md +96 -17
- package/dist/asset-modules.d.ts +81 -0
- package/dist/base.css +9 -0
- package/dist/cli/check/config.js +41 -0
- package/dist/cli/check/css.js +131 -0
- package/dist/cli/check/diagnostics.js +32 -0
- package/dist/cli/check/index.js +87 -0
- package/dist/cli/check/loader.js +146 -0
- package/dist/cli/check/paths.js +15 -0
- package/dist/cli/check/relativize.js +18 -0
- package/dist/cli/check/render-invariants.js +24 -0
- package/dist/cli/check/resolve-tool.js +38 -0
- package/dist/cli/check/section-assets.js +58 -0
- package/dist/cli/check/stages/deps.js +337 -0
- package/dist/cli/check/stages/lint.js +46 -0
- package/dist/cli/check/stages/render.js +101 -0
- package/dist/cli/check/stages/size.js +274 -0
- package/dist/cli/check/stages/tree.js +206 -0
- package/dist/cli/check/stages/typecheck.js +46 -0
- package/dist/cli/check/stages/validate/catalog-exhaustiveness.js +18 -0
- package/dist/cli/check/stages/validate/facts.js +18 -0
- package/dist/cli/check/stages/validate/formatter-contract.js +14 -0
- package/dist/cli/check/stages/validate/group-contract.js +21 -0
- package/dist/cli/check/stages/validate/index.js +66 -0
- package/dist/cli/check/stages/validate/list-scalar-contract.js +22 -0
- package/dist/cli/check/stages/validate/nav-contract.js +93 -0
- package/dist/cli/check/stages/validate/nested-slot-contract.js +12 -0
- package/dist/cli/check/stages/validate/object-list-contract.js +31 -0
- package/dist/cli/check/stages/validate/orchestrator.js +241 -0
- package/dist/cli/check/stages/validate/produced-by-contract.js +18 -0
- package/dist/cli/check/stages/validate/row-contract.js +31 -0
- package/dist/cli/check/stages/validate/select-contract.js +13 -0
- package/dist/cli/check/stages/validate/source-contract.js +21 -0
- package/dist/cli/check/stages/validate/variant-contract.js +15 -0
- package/dist/cli/check/workspace.js +86 -0
- 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/theme/generate.js +43 -0
- package/dist/cli/theme/index.js +33 -0
- package/dist/cli/theme/load-theme.js +69 -0
- package/dist/cli.js +100 -16
- package/dist/contracts/fill-treatments.js +47 -0
- package/dist/design-system/theme.d.ts +30 -300
- package/dist/design-system/theme.js +112 -90
- 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/eslint/rules/no-hex.js +78 -6
- package/dist/eslint.js +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -2
- 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/primitives/Image.js +1 -1
- package/dist/schema/fill-spec.d.ts +80 -590
- package/dist/schema/fixture-schema.d.ts +5 -81
- package/dist/schema/manifest.d.ts +39 -475
- package/dist/schema/resolve-section-ref.js +10 -0
- package/dist/schema/rows.js +10 -0
- package/dist/schema/section-nav.js +9 -0
- package/dist/schema/section-schema.d.ts +51 -437
- package/dist/schema/slot-types.d.ts +12 -16
- package/dist/ssr.d.ts +31 -0
- package/dist/ssr.js +46 -0
- package/dist/styles.css +31 -88
- package/docs/INDEX.md +12 -3
- package/docs/assets.md +128 -0
- package/docs/check.md +124 -0
- package/docs/dev.md +187 -0
- package/docs/eslint.md +18 -8
- package/docs/islands.md +5 -2
- package/docs/llms.txt +59 -9
- package/docs/new.md +27 -0
- package/docs/overview.md +57 -0
- package/docs/pack.md +46 -0
- package/docs/primitives.md +10 -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 +14 -5
- package/docs/rules/bundle-binary-asset.md +83 -0
- package/docs/rules/bundle-incomplete.md +2 -2
- package/docs/rules/css-reason.md +3 -3
- package/docs/rules/fixtures-invalid.md +96 -0
- package/docs/rules/license-denied.md +11 -3
- package/docs/rules/manifest-invalid.md +99 -0
- 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/no-hex.md +76 -10
- package/docs/rules/no-templates.md +87 -0
- package/docs/rules/parse-error.md +76 -0
- package/docs/rules/schema-invalid.md +96 -0
- 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 +157 -92
- package/docs/vocabulary.md +98 -0
- package/package.json +14 -11
- package/tsconfig.json +1 -1
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/dist/styles.css
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* @homepages/template-kit —
|
|
2
|
+
* @homepages/template-kit — the global Tailwind preamble.
|
|
3
3
|
*
|
|
4
|
-
* Import this AFTER `@import "tailwindcss";` in your own CSS entry
|
|
4
|
+
* Import this AFTER `@import "tailwindcss";` in your own CSS entry, and import your
|
|
5
|
+
* template's compiled theme after it:
|
|
5
6
|
*
|
|
6
7
|
* @import "tailwindcss";
|
|
7
8
|
* @import "@homepages/template-kit/styles.css";
|
|
9
|
+
* @import "./theme.css"; // compileThemeToCss(yourTheme) output
|
|
8
10
|
*
|
|
9
11
|
* This file must NOT import Tailwind itself. Exactly one `@import "tailwindcss"` may
|
|
10
12
|
* exist per build graph — your entry owns it. Two double-emit Tailwind's preflight
|
|
11
13
|
* reset (box-sizing declared twice, ~4 KB of duplicate base layer).
|
|
12
14
|
*
|
|
15
|
+
* WHAT IS HERE: only what is true of EVERY template — the breakpoint ladder, the
|
|
16
|
+
* spacing base unit, and the motion defaults.
|
|
17
|
+
*
|
|
18
|
+
* WHAT IS NOT: your design tokens. Colors, fonts, type, radii and shadows are yours.
|
|
19
|
+
* Declare them in `theme.ts` under names you choose, and `compileThemeToCss` generates
|
|
20
|
+
* the `@theme inline` alias layer that turns each one into a utility. Nothing about
|
|
21
|
+
* your visual system is shared, and there is no global file for you to edit to make a
|
|
22
|
+
* token of yours usable.
|
|
23
|
+
*
|
|
13
24
|
* The @source below resolves relative to THIS FILE'S SHIPPED LOCATION (dist/styles.css),
|
|
14
25
|
* registering the kit's compiled JS for class scanning — that is how the utility classes
|
|
15
26
|
* baked into the kit's primitives reach your compiled CSS. It also overrides Tailwind's
|
|
@@ -23,107 +34,39 @@
|
|
|
23
34
|
* utilities and 19 stray @property registrations, ~2.5 KB. If a primitive ever moves out
|
|
24
35
|
* of primitives/, widen this — an unstyled <Image> in every template is the failure mode,
|
|
25
36
|
* and scripts/verify-consumer.mjs is what catches it.
|
|
26
|
-
*
|
|
27
|
-
* The @theme block aliases Tailwind's utility namespace onto the `--tr-*` custom
|
|
28
|
-
* properties your template's theme.ts supplies (`text-ink` → `var(--tr-color-ink)`).
|
|
29
|
-
* The aliases are the cross-template contract; the values behind them are yours.
|
|
30
37
|
*/
|
|
31
38
|
@source "./primitives/**/*.js";
|
|
32
39
|
|
|
40
|
+
/* `@theme inline` so a spacing/breakpoint value is substituted straight into the
|
|
41
|
+
* utilities Tailwind generates (`.gap-2 { gap: .5rem }`), rather than emitted as a
|
|
42
|
+
* `:root` custom property the utilities then read through `var()`. These are fixed
|
|
43
|
+
* global constants, not per-page-overridable tokens, so inlining is both correct and
|
|
44
|
+
* what keeps the compiled output stable. Nothing here may reference a token a theme.ts
|
|
45
|
+
* defines. */
|
|
33
46
|
@theme inline {
|
|
34
|
-
/* ----------
|
|
35
|
-
--color-ink: var(--tr-color-ink);
|
|
36
|
-
--color-ink-soft: var(--tr-color-ink-soft);
|
|
37
|
-
--color-ink-mute: var(--tr-color-ink-mute);
|
|
38
|
-
--color-background: var(--tr-color-background);
|
|
39
|
-
--color-surface: var(--tr-color-surface);
|
|
40
|
-
--color-surface-alt: var(--tr-color-surface-alt);
|
|
41
|
-
--color-border: var(--tr-color-border);
|
|
42
|
-
--color-border-strong: var(--tr-color-border-strong);
|
|
43
|
-
--color-accent: var(--tr-color-accent);
|
|
44
|
-
--color-accent-strong: var(--tr-color-accent-strong);
|
|
45
|
-
--color-accent-contrast: var(--tr-color-accent-contrast);
|
|
46
|
-
--color-primary: var(--tr-color-primary);
|
|
47
|
-
--color-primary-fg: var(--tr-color-primary-fg);
|
|
48
|
-
--color-primary-darker: var(--tr-color-primary-darker);
|
|
49
|
-
--color-overlay: var(--tr-color-overlay);
|
|
50
|
-
--color-overlay-strong: var(--tr-color-overlay-strong);
|
|
51
|
-
--color-overlay-light: var(--tr-color-overlay-light);
|
|
52
|
-
--color-footer-bg: var(--tr-color-footer-bg);
|
|
53
|
-
--color-footer-fg: var(--tr-color-footer-fg);
|
|
54
|
-
--color-footer-fg-mute: var(--tr-color-footer-fg-mute);
|
|
55
|
-
--color-footer-divider: var(--tr-color-footer-divider);
|
|
56
|
-
|
|
57
|
-
/* Decorative brand swatches — the open `palette` escape hatch in TokenTheme.
|
|
58
|
-
* A theme that omits one leaves its utility resolving to an unset custom
|
|
59
|
-
* property; only reach for these when a semantic role genuinely doesn't fit. */
|
|
60
|
-
--color-yellow-deep: var(--tr-color-yellow-deep);
|
|
61
|
-
--color-yellow-200: var(--tr-color-yellow-200);
|
|
62
|
-
--color-yellow-100: var(--tr-color-yellow-100);
|
|
63
|
-
--color-brown: var(--tr-color-brown);
|
|
64
|
-
--color-green: var(--tr-color-green);
|
|
65
|
-
--color-blue: var(--tr-color-blue);
|
|
66
|
-
--color-rule: var(--tr-color-rule);
|
|
67
|
-
--color-gray-light: var(--tr-color-gray-light);
|
|
68
|
-
--color-team-gold: var(--tr-color-team-gold);
|
|
69
|
-
--color-mute: var(--tr-color-mute);
|
|
70
|
-
--color-scrim: var(--tr-color-scrim);
|
|
71
|
-
--color-footer-line: var(--tr-color-footer-line);
|
|
72
|
-
--color-footer-mute: var(--tr-color-footer-mute);
|
|
73
|
-
--color-divider-40: var(--tr-color-divider-40);
|
|
74
|
-
--color-contact-deep: var(--tr-color-contact-deep);
|
|
75
|
-
--color-header-deep: var(--tr-color-header-deep);
|
|
76
|
-
|
|
77
|
-
/* ---------- Fonts ---------- */
|
|
78
|
-
--font-sans: var(--tr-font-sans);
|
|
79
|
-
--font-display: var(--tr-font-sans);
|
|
80
|
-
--font-serif: var(--tr-font-serif);
|
|
81
|
-
--font-num: var(--tr-font-num);
|
|
82
|
-
--font-label: var(--tr-font-label);
|
|
83
|
-
|
|
84
|
-
/* ---------- Type scale (mobile baseline; layer up with md:/lg:) ---------- */
|
|
85
|
-
--text-xs: var(--tr-text-xs);
|
|
86
|
-
--text-sm: var(--tr-text-sm);
|
|
87
|
-
--text-base: var(--tr-text-base);
|
|
88
|
-
--text-lg: var(--tr-text-lg);
|
|
89
|
-
--text-xl: var(--tr-text-xl);
|
|
90
|
-
--text-2xl: var(--tr-text-2xl);
|
|
91
|
-
--text-3xl: var(--tr-text-3xl);
|
|
92
|
-
--text-4xl: var(--tr-text-4xl);
|
|
93
|
-
--text-5xl: var(--tr-text-5xl);
|
|
94
|
-
--text-display: var(--tr-text-display);
|
|
95
|
-
|
|
96
|
-
/* ---------- Spacing (Tailwind's own scale — no custom aliases) ---------- */
|
|
47
|
+
/* ---------- Spacing ---------- */
|
|
97
48
|
--spacing: 0.25rem; /* v4 base unit; Tailwind multiplies (e.g. p-4 = 1rem) */
|
|
98
49
|
|
|
99
|
-
/* ----------
|
|
100
|
-
|
|
101
|
-
--radius-md: var(--tr-radius-md);
|
|
102
|
-
--radius-lg: var(--tr-radius-lg);
|
|
103
|
-
--radius-full: var(--tr-radius-full);
|
|
104
|
-
|
|
105
|
-
/* ---------- Shadows ---------- */
|
|
106
|
-
--shadow-sm: var(--tr-shadow-sm);
|
|
107
|
-
--shadow-md: var(--tr-shadow-md);
|
|
108
|
-
--shadow-lg: var(--tr-shadow-lg);
|
|
109
|
-
|
|
110
|
-
/* ---------- Motion ---------- */
|
|
50
|
+
/* ---------- Motion ----------
|
|
51
|
+
* The --tr-* values these read are defined in base.css, which every page loads. */
|
|
111
52
|
--default-transition-duration: var(--tr-duration-fast);
|
|
112
53
|
--default-transition-timing-function: var(--tr-ease);
|
|
113
54
|
|
|
114
55
|
/* ---------- Canonical breakpoint ladder ----------
|
|
115
|
-
* Eight canonical widths — the same set the preview grid and the screenshot
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
56
|
+
* Eight canonical widths — the same set the preview grid and the screenshot harness
|
|
57
|
+
* verify a section against. `VERIFICATION_WIDTHS` (design-system/breakpoints.ts) is
|
|
58
|
+
* the matching TS constant; CSS cannot import it, so `src/css/styles.test.ts` fails
|
|
59
|
+
* the build if the two ever drift.
|
|
60
|
+
*
|
|
61
|
+
* 350 is the mobile-first BASE (no prefix); the other seven are named breakpoints:
|
|
119
62
|
*
|
|
120
63
|
* bp390 (390) · bp425 (425) · bp512 (512) · md (768) · lg (1024)
|
|
121
64
|
* · bp1440 (1440) · bp1920 (1920)
|
|
122
65
|
*
|
|
123
66
|
* `md` and `lg` keep Tailwind's own names and values. The off-ladder defaults
|
|
124
|
-
* (sm/xl/2xl) are reset to `initial` so a section cannot come to depend on a
|
|
125
|
-
*
|
|
126
|
-
*
|
|
67
|
+
* (sm/xl/2xl) are reset to `initial` so a section cannot come to depend on a width
|
|
68
|
+
* nobody screenshots. A swap-heavy section may still reach for a raw `@media`
|
|
69
|
+
* micro-breakpoint at an off-ladder width (e.g. 640 / 769).
|
|
127
70
|
*/
|
|
128
71
|
--breakpoint-sm: initial;
|
|
129
72
|
--breakpoint-bp390: 390px;
|
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,10 +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 |
|
|
19
|
-
| `
|
|
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 |
|
|
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
|
-
| `eslint.md` | The `@homepages/template-kit/eslint` preset:
|
|
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 |
|
|
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 |
|
|
22
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
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
purpose: The `template-kit check` command — what it runs, its flags, its output shape, and its exit code.
|
|
3
|
+
status: living
|
|
4
|
+
related: [INDEX.md, rules/INDEX.md, eslint.md, theme-and-css.md, dev.md]
|
|
5
|
+
updated: 2026-07-15
|
|
6
|
+
---
|
|
7
|
+
# template-kit check
|
|
8
|
+
|
|
9
|
+
`template-kit check` is the one command that answers "will the platform accept this
|
|
10
|
+
template?" — the same gate you run locally and the one the registry re-runs when you
|
|
11
|
+
submit. Every failure prints a stable `template-kit/<id>`, a `file:line` where one
|
|
12
|
+
applies, and a one-line fix hint. The id resolves to its own page under
|
|
13
|
+
[`rules/`](rules/INDEX.md).
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
template-kit check # infer the template from cwd, or the workspace's one template
|
|
19
|
+
template-kit check <template> # check one named template by its key
|
|
20
|
+
template-kit check --all # check every template in the workspace
|
|
21
|
+
template-kit check --json # machine-readable report (combine with any of the above)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Run it from the workspace root, or from inside a template's own folder — a bare `check`
|
|
25
|
+
infers which template you mean. A workspace with more than one template must either name
|
|
26
|
+
one or pass `--all`.
|
|
27
|
+
|
|
28
|
+
### What it needs installed
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm i -D typescript eslint @typescript-eslint/parser esbuild
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`check` runs **your** toolchain, not a copy of its own: it resolves TypeScript, ESLint
|
|
35
|
+
and esbuild from your workspace, so the compiler that judges your types is the one you
|
|
36
|
+
pinned, and a linter and a bundler never end up in the production install of a package
|
|
37
|
+
whose job is to render sections. A missing one is reported by name, with the install
|
|
38
|
+
line to fix it.
|
|
39
|
+
|
|
40
|
+
## What it runs
|
|
41
|
+
|
|
42
|
+
Two granularities, because a workspace has one `package.json` and one lockfile, but can
|
|
43
|
+
hold many templates.
|
|
44
|
+
|
|
45
|
+
**Six authoring stages**, run in order, once per selected template. None of them abort
|
|
46
|
+
the run — a template with several unrelated problems reports all of them in one pass, not
|
|
47
|
+
one class of fix at a time:
|
|
48
|
+
|
|
49
|
+
1. **typecheck** — the workspace's own TypeScript compiles the template's files, judged
|
|
50
|
+
against the workspace's own `tsconfig.json`.
|
|
51
|
+
2. **lint** — the workspace's own ESLint, running the kit's shipped preset over the
|
|
52
|
+
template's files. See [the ESLint preset](eslint.md) for the rules this stage shares
|
|
53
|
+
with your editor.
|
|
54
|
+
3. **validate** — the schema, fill-spec, fixtures, and manifest contracts: every
|
|
55
|
+
cross-reference between a section's contract files, and a template's `manifest.json`
|
|
56
|
+
against the sections it composes.
|
|
57
|
+
4. **tree** — the rules a one-file-at-a-time linter cannot express: a missing contract
|
|
58
|
+
file, a slot declared in `schema.ts` with no marker anywhere in the section's JSX, a
|
|
59
|
+
hand-written CSS file's own rules.
|
|
60
|
+
5. **render** — a determinism probe (every fixture is rendered twice; the two outputs
|
|
61
|
+
must be byte-identical) and a scan of the rendered HTML for objective defects — a
|
|
62
|
+
stray `[object Object]`, a bare `null`/`undefined`/`NaN` leaf, a `src`-less `<img>` —
|
|
63
|
+
plus, for the `typical` fixture, that the editor's sidebar order matches the page's
|
|
64
|
+
real render order.
|
|
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.
|
|
68
|
+
|
|
69
|
+
**The dependency gates** run once per invocation, never once per template — they are
|
|
70
|
+
properties of the workspace's one lockfile and installed tree, not of any one template:
|
|
71
|
+
a lockfile present and satisfying `package.json`, exactly one copy of React installed,
|
|
72
|
+
no `npm audit` finding at high or critical severity, and every shipped dependency's
|
|
73
|
+
license on an allowlist.
|
|
74
|
+
|
|
75
|
+
A run that selects zero templates to check is itself a failure —
|
|
76
|
+
[`no-templates`](rules/no-templates.md) — never a silent pass.
|
|
77
|
+
|
|
78
|
+
## Output
|
|
79
|
+
|
|
80
|
+
Human-readable output prints one line per template (`✓ <key>`, or `✗ <key> — N
|
|
81
|
+
problem(s)` followed by each finding), then any workspace-level findings from the
|
|
82
|
+
dependency gates, then `check passed` if everything passed.
|
|
83
|
+
|
|
84
|
+
`--json` prints one report object instead, keyed per template so a single-template
|
|
85
|
+
consumer (the registry, ingesting one submission) and an all-templates consumer (your
|
|
86
|
+
own CI) parse the same shape:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
{
|
|
90
|
+
ok: boolean,
|
|
91
|
+
kitVersion: string,
|
|
92
|
+
templates: {
|
|
93
|
+
[templateKey: string]: { ok: boolean, diagnostics: Diagnostic[] },
|
|
94
|
+
},
|
|
95
|
+
// Dependency-gate findings — evaluated once, not per template.
|
|
96
|
+
workspace: { ok: boolean, diagnostics: Diagnostic[] },
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Each `Diagnostic` carries a `ruleId` (`template-kit/<id>`), the `template` it belongs to,
|
|
101
|
+
an optional `section` and `file`/`line`, a `message`, and a `fix`.
|
|
102
|
+
|
|
103
|
+
## Exit code
|
|
104
|
+
|
|
105
|
+
`0` iff every selected template and the workspace pass. `1` otherwise — including when
|
|
106
|
+
the run itself can't proceed at all (an unknown template name, no `templates/` folder
|
|
107
|
+
in the workspace).
|
|
108
|
+
|
|
109
|
+
## Thresholds are not configurable
|
|
110
|
+
|
|
111
|
+
Every byte budget, severity floor, and license allowlist `check` enforces is a platform
|
|
112
|
+
acceptance criterion, not a per-workspace preference — there is no config file or flag
|
|
113
|
+
that raises them. If a template has a legitimate reason to need an exception, ask the
|
|
114
|
+
platform team; a workaround that quietly disables a gate will simply fail again at
|
|
115
|
+
submission.
|
|
116
|
+
|
|
117
|
+
## See also
|
|
118
|
+
|
|
119
|
+
- [Rules](rules/INDEX.md) — every id `check` (and the lint preset) can print, one page
|
|
120
|
+
each, Rule / Reason / Fix.
|
|
121
|
+
- [ESLint preset](eslint.md) — the one-file-at-a-time half of the same rule set, run
|
|
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.
|