templa-js 0.10.1 → 0.13.1

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/AGENTS.md CHANGED
@@ -9,6 +9,7 @@ templa is a tiny HTML template loader giving you:
9
9
  - `<template src="...">` to inline another HTML file
10
10
  - `{{var}}` for variables; `<template if="key">` / `<template unless="key">` for existence-based conditionals
11
11
  - `<slot>` and `<slot name="X">` for Web Components-style layouts
12
+ - Automatic active-nav marking: an `<a>` inside `<nav>` pointing at the current page gets `aria-current="page"` — style it with one CSS rule
12
13
  - A build CLI (`npx templa-js build`) that statically expands all of the above
13
14
 
14
15
  Same syntax works at runtime (browser) and at build time. Zero dependencies.
@@ -48,7 +49,7 @@ Lock down everything Phase 2 sub-agents will *consume*. **One agent in charge. N
48
49
 
49
50
  > Need a structured way to design the skeleton from a brief? Use `PLANNER.md` — an instruction prompt that walks through the same checklist below and produces a `plan.md` for the project before any file is written.
50
51
 
51
- 1. **Design tokens in `src/css/style.css`** — base typography, color scale, spacing scale (`--space-1` … `--space-7`), radius / shadow values, and document chrome (`<header>`, `<footer>`, layout grid helpers). Per-section visual rules do NOT live here — they ship co-located inside each section's partial via `<style data-merge="css/style.css">`. The build merges them into `style.css` automatically.
52
+ 1. **Design tokens in `src/css/style.css`** — base typography, color scale, spacing scale (`--space-1` … `--space-7`), radius / shadow values, and document chrome (`<header>`, `<footer>`, layout grid helpers). Per-section visual rules do NOT live here — they ship co-located inside each section's partial via `<style data-merge="css/style.css">`. The build merges them into `style.css` automatically. Keep the `:root` token block **first in the file**, closed with a `/* ── tokens end ── */` marker: sub-agents read only that block, so Phase 2's token cost stays constant however large the stylesheet grows.
52
53
 
53
54
  2. **The five `common-*` templates in `src/_partials/`** — these are the project's chrome and shared section vocabulary. Lock them in Phase 1; sub-agents do not touch them.
54
55
 
@@ -56,7 +57,7 @@ Lock down everything Phase 2 sub-agents will *consume*. **One agent in charge. N
56
57
  |---|---|
57
58
  | `common-head.html` | Everything inside `<head>` except doctype/title — charset, viewport, font links, stylesheet link |
58
59
  | `common-layout.html` | Body skeleton: invokes `common-header`, wraps a default `<slot>` in `<main>`, invokes `common-footer` |
59
- | `common-header.html` | The site `<header>` element (brand + nav) |
60
+ | `common-header.html` | The site `<header>` element (brand + nav). The current page's link is auto-marked `aria-current="page"`; style it with a single `nav a[aria-current="page"]` rule — never enumerate pages in CSS and never thread a `page` variable |
60
61
  | `common-footer.html` | The site `<footer>` element |
61
62
  | `common-subhero.html` | Shared inner-page subhero, parameterized via `title` (and optionally `bg`) |
62
63
 
@@ -68,7 +69,7 @@ Lock down everything Phase 2 sub-agents will *consume*. **One agent in charge. N
68
69
 
69
70
  Phase 1 is complete when:
70
71
 
71
- - `npx templa-js build` succeeds — **this is the gate; do not dispatch Phase 2 sub-agents until it passes.** Phase 2 assumes a sound skeleton; starting parallel work on a broken one produces incoherent output that's hard to recover from.
72
+ - `npx templa-js check` exits 0 — **this is the gate; do not dispatch Phase 2 sub-agents until it passes.** `check` runs the build pipeline without writing anything and exits 1 listing every problem (missing partial, unresolved `{{key}}`, page-level conditional, unclosed `<template>`). Phase 2 assumes a sound skeleton; starting parallel work on a broken one produces incoherent output that's hard to recover from.
72
73
  - The output renders cohesively even with placeholder section content.
73
74
  - Every section file the pages reference exists on disk.
74
75
 
@@ -77,8 +78,9 @@ Phase 1 is complete when:
77
78
  With the skeleton locked, dispatch sub-agents to fill in each section. **One sub-agent per `_partials/[pagename]-[section].html` file, all running concurrently.**
78
79
 
79
80
  A sub-agent's brief is exactly:
81
+ - `SECTION.md` (project root) as its only required reading — do **not** hand sub-agents this whole file; SECTION.md carries the same rules at a fraction of the tokens, and the savings multiply by the number of agents.
80
82
  - The single file path it owns (e.g. `_partials/index-hero.html`).
81
- - A 2–3 line description of what the section should contain.
83
+ - A 2–3 line description of what the section should contain, including the attribute keys it will receive (if any).
82
84
  - The page's existing wireframe / surrounding context (so it knows what comes before and after, but not so it can edit those).
83
85
 
84
86
  Sub-agents may:
@@ -128,8 +130,9 @@ src/
128
130
  - Files and directories starting with `_` are partials and are **not copied** to the build output. Reference them with relative paths.
129
131
  - Entry pages live at any non-underscore path under the source root.
130
132
  - `<template src="X">` inside a partial is resolved relative to **the file containing the tag**, not the entry page. `common-layout.html` lives in `_partials/` so it references its siblings as `<template src="common-header.html">` (no `_partials/` prefix). Page entries live at `src/`, so they reference partials as `<template src="_partials/common-head.html">`.
133
+ - **Asset URLs in a partial are also partial-relative.** `src` (any element), `href` on `<link>`/`<use>`, `srcset`, `poster`, and CSS `url()` (in `<style>` blocks and `style=""`, including co-located `<style data-merge>`) are resolved relative to the partial and rewritten to root-absolute paths (so `../css/style.css` in `_partials/common-head.html` → `/css/style.css`), build and runtime alike. NOT rebased: `<a href>` (write nav links root-absolute like `/about.html`), `{{templated}}` URLs, `data-*`, `url(#id)`, and anything already absolute/scheme/hash — leave those as you mean them.
131
134
  - Section file class names match the filename: `_partials/index-hero.html` styles `.index-hero`. This makes the cascade easy to audit and prevents class collisions across pages.
132
- - The `data-merge` attribute on a section's `<style>` block is the **dist-relative path** to the linked stylesheet — e.g. `data-merge="css/style.css"` when the page links `./css/style.css`. Mismatched paths cause merged styles to land in a file the page doesn't load.
135
+ - The `data-merge` attribute on a section's `<style>` block is the **dist-relative path** to the linked stylesheet — e.g. `data-merge="css/style.css"` when the page loads `/css/style.css`. Mismatched paths cause merged styles to land in a file the page doesn't load.
133
136
 
134
137
  ---
135
138
 
@@ -269,7 +272,7 @@ When to use: any partial whose presence implies its own visual rules. Co-locatin
269
272
 
270
273
  Conditionals can be nested. Resolution iterates until stable.
271
274
 
272
- For **conditional attributes** (`disabled` on/off, `target="_blank"` only when external, etc.), templa core does not provide a built-in mechanism — that is intentional. Use a plugin (`templa-plugin-attrs` or similar) when a project needs them. Keeping the core to existence-based block conditionals preserves the "templa looks like HTML the platform should ship" stance.
275
+ For **conditional attributes** (`disabled` on/off, `target="_blank"` only when external, etc.), templa core does not provide a built-in mechanism — that is intentional. When a project needs one, bind the attribute with Alpine (e.g. `:disabled="..."`) at runtime, or write the variants as two existence-based blocks. Keeping the core to existence-based block conditionals preserves the "templa looks like HTML the platform should ship" stance.
273
276
 
274
277
  ### Layouts and slots
275
278
 
@@ -337,11 +340,16 @@ For SEO-sensitive pages, always build before deploy. Crawlers other than Googleb
337
340
  ```bash
338
341
  npx templa-js build # default: ./src → ./dist
339
342
  npx templa-js build -i pages -o public
343
+ npx templa-js build --strict # exit 1 on any problem (CI / agent loops)
344
+ npx templa-js build --no-format # skip the prettier pass (faster iteration)
345
+ npx templa-js check # verify without writing; always strict
340
346
  npx templa-js --help
341
347
  ```
342
348
 
343
349
  The CLI walks every `.html` file in the source tree (skipping `_*` files and directories), expands templates and slots recursively, and writes the result to the output directory. Other files are copied as-is.
344
350
 
351
+ **Agents: gate on exit codes, not log output.** `check` (and `build --strict`) exit 1 when a partial is missing, a `{{key}}` survives into the output, a page-level `<template if/unless>` remains, or a `<template>` is left unclosed — each problem is listed with its page. A plain `build` only warns and still exits 0.
352
+
345
353
  ---
346
354
 
347
355
  ## Pitfalls — read before debugging
@@ -360,7 +368,7 @@ The CLI walks every `.html` file in the source tree (skipping `_*` files and dir
360
368
 
361
369
  7. **`<title>` / `<meta og:*>` in a head partial when running in runtime mode.** SNS crawlers and Google's first wave don't run JS, so they will see no title and no description. Either build, or keep these tags inline in the page.
362
370
 
363
- 8. **Strict CSP without `'unsafe-eval'`.** The runtime evaluates `params` via `new Function`. If the deployment forbids `'unsafe-eval'`, switch to build mode — the output HTML contains no template syntax and needs no runtime.
371
+ 8. **Assuming the runtime needs `'unsafe-eval'`.** It doesn't — the runtime never calls `eval` or `new Function`, so a plain `script-src 'self'` CSP works. Don't add `'unsafe-eval'` for templa. If the page should ship with no runtime at all, build with `npx templa-js build` — the output is plain HTML with no template syntax left.
364
372
 
365
373
  9. **`<slot name="head">` inside a body-fragment layout.** `<slot>` only fills where it sits in the DOM. To inject head content per-page, write it directly in the page's `<head>`.
366
374
 
@@ -396,6 +404,8 @@ The CLI walks every `.html` file in the source tree (skipping `_*` files and dir
396
404
 
397
405
  14. **Page entry file edited during Phase 2.** Page files are part of the skeleton; only the orchestrator adds, removes, or reorders the `<template src>` lines. A sub-agent that wants to add a new section asks the orchestrator to wire it in, then writes the new partial.
398
406
 
407
+ 15. **Hand-rolled active-nav state.** Don't thread a `page` variable through the layout or enumerate pages in CSS selectors. templa marks the current page's `<nav>` link with `aria-current="page"` automatically (build and runtime); one `nav a[aria-current="page"]` rule covers every page, present and future.
408
+
399
409
  ---
400
410
 
401
411
  ## Quick command reference
@@ -407,6 +417,9 @@ npm install templa-js
407
417
  # Build
408
418
  npx templa-js build -i ./src -o ./dist
409
419
 
420
+ # Verify without writing (exit 0 = sound; the Phase 1 → 2 gate)
421
+ npx templa-js check -i ./src
422
+
410
423
  # Version
411
424
  npx templa-js --version
412
425
 
package/PLANNER.md CHANGED
@@ -122,13 +122,15 @@ Pick concrete values; do not stay vague. Provide all of:
122
122
 
123
123
  If the brief was vague on aesthetics, infer from the project type and brand vibe and note explicitly that the values are an inference the user can override.
124
124
 
125
+ Place the `:root` token block first in `style.css`, closed with a `/* ── tokens end ── */` marker — section sub-agents read only that block.
126
+
125
127
  ### 6. Decide the `common-*` templates
126
128
 
127
129
  (All paths below are project-root relative.)
128
130
 
129
- - **`src/_partials/common-head.html`** — `<head>` chrome: charset, viewport, theme-color, font preconnect/links, `./css/style.css` link. Title comes from the page's `<template src="…common-head.html" title="…">` attribute.
131
+ - **`src/_partials/common-head.html`** — `<head>` chrome: charset, viewport, theme-color, font preconnect/links, `../css/style.css` link (partial-relative → rewritten to `/css/style.css`). Title comes from the page's `<template src="…common-head.html" title="…">` attribute.
130
132
  - **`src/_partials/common-layout.html`** — body fragment: invokes `common-header`, wraps a default `<slot>` in `<main>`, invokes `common-footer`.
131
- - **`src/_partials/common-header.html`** — `<header>` element: brand + navigation anchors, each with `data-nav="<page-id>"` for active styling driven by `body[data-page="..."]` selectors in `src/css/style.css`.
133
+ - **`src/_partials/common-header.html`** — `<header>` element: brand + navigation anchors. The current page's link is auto-marked `aria-current="page"` by templa (build and runtime); style it with a single `nav a[aria-current="page"]` rule — no per-page selectors, no `page` variable.
132
134
  - **`src/_partials/common-footer.html`** — `<footer>` element with copyright/social, identical on every page.
133
135
  - **`src/_partials/common-subhero.html`** — `<section>` for inner-page headers, parameterized by `title` and optionally `bg`. Used by every page except the home page.
134
136
 
@@ -142,7 +144,7 @@ A complete listing of every file the skeleton will create, **paths relative to t
142
144
  src/css/style.css // tokens + base + chrome (locked in Phase 1)
143
145
  src/_partials/common-head.html // <head> chrome (charset, viewport, fonts, css link)
144
146
  src/_partials/common-layout.html // body skeleton: header / main slot / footer
145
- src/_partials/common-header.html // <header>: brand + nav with data-nav
147
+ src/_partials/common-header.html // <header>: brand + nav (active link auto-marked aria-current)
146
148
  src/_partials/common-footer.html // <footer>: copyright + socials
147
149
  src/_partials/common-subhero.html // shared inner-page subhero (title, optional bg)
148
150
  src/_partials/index-hero.html // index — landing hero (sub-agent owned)
@@ -161,7 +163,7 @@ Every `[pagename]-[sectionname].html` listed above is a unit of Phase 2 sub-agen
161
163
 
162
164
  ### 8. Sketch the section-fill dispatch
163
165
 
164
- One sub-agent per `[pagename]-[sectionname].html` file. For each, give a 2–3-line brief: what the section contains, which design tokens to reach for, any image/asset references, any Alpine.js bits to author inline.
166
+ One sub-agent per `[pagename]-[sectionname].html` file. Each sub-agent reads `SECTION.md` (project root) as its only required reading, plus a 2–3-line brief from you: what the section contains, which design tokens to reach for, any image/asset references, any Alpine.js bits to author inline.
165
167
 
166
168
  The dispatch is genuinely parallel — none of the section files share file content, so sub-agents never collide.
167
169
 
@@ -179,10 +181,10 @@ Example:
179
181
  End the plan with this line, verbatim:
180
182
 
181
183
  ```bash
182
- npx templa-js build -i ./src -o ./dist
184
+ npx templa-js check -i ./src
183
185
  ```
184
186
 
185
- This is the gate that separates skeleton from content fill. Content sub-agents must not be dispatched until this command succeeds against the placeholder skeleton.
187
+ This is the gate that separates skeleton from content fill. `check` runs the build pipeline without writing anything and exits 1 listing every problem. Content sub-agents must not be dispatched until this command exits 0 against the placeholder skeleton.
186
188
 
187
189
  ## Output format — your reply takes this shape
188
190
 
@@ -250,9 +252,9 @@ Fonts: Inter (body), Lora (headings).
250
252
  (Note: tokens inferred from the "warm, slow" vibe; override if you want.)
251
253
 
252
254
  ## 6. common-* templates
253
- - src/_partials/common-head.html — charset, viewport, theme-color, fonts, ./css/style.css
255
+ - src/_partials/common-head.html — charset, viewport, theme-color, fonts, ../css/style.css (→ /css/style.css)
254
256
  - src/_partials/common-layout.html — header / <main><slot/></main> / footer
255
- - src/_partials/common-header.html — brand + nav with data-nav
257
+ - src/_partials/common-header.html — brand + nav (active link auto-marked aria-current)
256
258
  - src/_partials/common-footer.html — copyright + socials
257
259
  - src/_partials/common-subhero.html — inner-page subhero (title attribute)
258
260
 
@@ -269,7 +271,7 @@ Fonts: Inter (body), Lora (headings).
269
271
 
270
272
  ## 9. Build gate
271
273
  ```bash
272
- npx templa-js build -i ./src -o ./dist
274
+ npx templa-js check -i ./src
273
275
  ```
274
276
 
275
277
  ## Open questions
package/README.md CHANGED
@@ -42,6 +42,7 @@ It works in two modes:
42
42
  - **Standard HTML** — uses the native `<template>` element, not custom tags
43
43
  - **Tiny** — ~240 lines of source, ~3.5KB gzipped
44
44
  - **HTML-native** — `{{var}}` for values; `<template if>` / `<template unless>` for conditionals
45
+ - **Active-nav aware** — `<nav>` links to the current page get `aria-current="page"` automatically
45
46
  - **Recursive** — partials inside partials just work
46
47
  - **Resource-aware** — waits for `<link rel="stylesheet">` and `<script src>` inside partials before resolving
47
48
 
@@ -71,11 +72,11 @@ Bootstrap a new project in the current directory:
71
72
 
72
73
  ```bash
73
74
  npx templa-js init # minimal src/ tree
74
- npx templa-js init --ai # also write AGENTS.md and PLANNER.md
75
+ npx templa-js init --ai # also write the AI agent guides
75
76
  npx templa-js init --force # overwrite existing files
76
77
  ```
77
78
 
78
- The result is a buildable project: run `npx templa-js build` immediately afterwards and `dist/` will be produced. The `--ai` flag adds two project-root markdown files that brief AI coding agents on templa's conventions and a planning prompt for site skeletons.
79
+ The result is a buildable project: run `npx templa-js build` immediately afterwards and `dist/` will be produced. The `--ai` flag adds four project-root markdown files that brief AI coding agents on templa's conventions: `AGENTS.md` (orchestrator guide), `PLANNER.md` (skeleton planning prompt), `SECTION.md` (compact sub-agent brief), and `CLAUDE.md` (pointer so Claude Code finds the others).
79
80
 
80
81
  ## Usage
81
82
 
@@ -172,6 +173,36 @@ Rules:
172
173
 
173
174
  This pattern works identically with the build CLI — `npx templa-js build` inlines the layout into the output and you can drop the script tags.
174
175
 
176
+ ### Active navigation links
177
+
178
+ Any `<a>` inside `<nav>` whose `href` resolves to the current page is automatically marked with `aria-current="page"` — at build time (against the output path) and at runtime (against `location`). Style the active link with one CSS rule; no per-page selectors, no passing a `page` variable around:
179
+
180
+ ```html
181
+ <!-- _partials/common-header.html -->
182
+ <style data-merge="css/style.css">
183
+ nav a[aria-current="page"] { font-weight: bold; }
184
+ </style>
185
+ <header>
186
+ <nav>
187
+ <a href="/">Home</a>
188
+ <a href="/about.html">About</a>
189
+ </nav>
190
+ </header>
191
+ ```
192
+
193
+ Matching rules:
194
+
195
+ | Case | Behaviour |
196
+ |---|---|
197
+ | `/` vs `index.html` | equivalent — a trailing `/index.html` normalizes to `/` |
198
+ | Pretty URLs | `/about` ≡ `/about.html` ≡ `/about/`, so `.html` hrefs still match on hosts that serve extensionless paths (Netlify, Vercel) and vice versa |
199
+ | Query strings and hashes | ignored; comparison is by path only |
200
+ | Pure-hash links (`#section`) | skipped, so same-page TOC links stay unmarked |
201
+ | External / cross-origin links | never match |
202
+ | Hand-written `aria-current` | respected — templa won't touch that link |
203
+
204
+ Links outside `<nav>` are never marked: per the ARIA definition, `aria-current="page"` identifies the current page *within a set of navigation links*. As a side effect, screen readers announce the active link correctly for free.
205
+
175
206
  ### Nested partials
176
207
 
177
208
  Partials can include other partials. `templa` keeps expanding until no `<template src>` remains.
@@ -180,11 +211,30 @@ Partials can include other partials. `templa` keeps expanding until no `<templat
180
211
 
181
212
  `templa.start()` kicks off head expansion **synchronously** so its fetches overlap with the rest of HTML parsing — critical when partials in `<head>` include `<link rel="stylesheet">`. Body expansion waits for `DOMContentLoaded`. The returned Promise resolves once both phases complete.
182
213
 
183
- ### Relative paths in nested partials
214
+ ### Relative paths in partials
184
215
 
185
- `<template src>` inside a partial is resolved relative to **that partial's URL**, not the page. So `_partials/layout.html` can reference `<template src="./header.html">` and it will resolve to `_partials/header.html` correctly.
216
+ URLs authored **inside a partial are relative to that partial's own location**, not to the page that includes it — so a partial can live in `_partials/` and be shared by pages at any directory depth, resolving the same from every page.
186
217
 
187
- Other resources (`<img src>`, `<link href>`, `<script src>`) still resolve against the page URL use absolute or root-relative paths for those.
218
+ This applies to `<template src>` **and** to asset URLs: `src` (on any element), `href` on `<link>`/`<use>`, `srcset`, `poster`, and **CSS `url()`** inside `<style>` blocks and `style=""` attributes (so co-located [`<style data-merge>`](#co-located-styles) keeps working). templa rewrites them to root-absolute paths during expansion (`../css/style.css` in `_partials/head.html` → `/css/style.css`), identically at build time and runtime.
219
+
220
+ ```html
221
+ <!-- _partials/common-head.html -->
222
+ <link rel="stylesheet" href="../css/style.css" /> <!-- → /css/style.css -->
223
+ <style data-merge="css/style.css">
224
+ .hero { background: url(../img/hero.png); } <!-- → url(/img/hero.png) -->
225
+ </style>
226
+ ```
227
+
228
+ Left untouched (write these as you mean them):
229
+
230
+ | Not rebased | Why / what to write |
231
+ | --- | --- |
232
+ | `<a href="…">` | Link targets are pages, not co-located assets. Use **root-absolute** (`/about.html`) so a shared nav works from every page. |
233
+ | `#fragment`, `url(#id)`, `mailto:`/`tel:`/`https:`/`data:`, `//host`, `/already-absolute` | Already unambiguous. |
234
+ | `{{templated}}` URLs | Filled by `render` afterwards — you own the value (pass it root-absolute). |
235
+ | `data-*` attributes | Reserved as metadata. |
236
+
237
+ > **Note:** root-absolute output assumes the site is served from the domain root. At runtime any deploy sub-path is carried automatically via `location`; the static build assumes root (a configurable base path is not yet supported).
188
238
 
189
239
  ### Co-located styles
190
240
 
@@ -231,6 +281,8 @@ For pages that don't need a runtime at all, build with `npx templa-js build` —
231
281
 
232
282
  - `{{key}}` HTML-escapes its value. If you need to inject HTML, use `{{{key}}}` and make sure the value is trusted.
233
283
  - Original `<template src>` elements are removed from the DOM after expansion.
284
+ - `if` / `unless` are resolved against the data passed by the calling `<template src>`, so conditionals only make sense **inside a partial**. A bare `<template if="…">` written at the page top level has no calling data — it is left untouched (and a `<template>` renders its content inertly, i.e. invisibly). Both the build CLI and the runtime warn when they find one.
285
+ - Only the exact attribute names `if` / `unless` / `src` / `slot` are special. Framework directives that merely end in one of those (Alpine's `<template x-if>`, Vue's `<template v-if>`, any `data-*`) are left completely alone, so templa partials can host them.
234
286
  - This project is in early development (0.0.x). API may change.
235
287
 
236
288
  ## Build (CLI)
@@ -268,9 +320,19 @@ Reference partials with a relative path:
268
320
  |---|---|---|
269
321
  | `-i <dir>` | `./src` | Source directory |
270
322
  | `-o <dir>` | `./dist` | Output directory (cleared before each build) |
323
+ | `--strict` | off | Exit 1 on any problem: missing partial, unresolved `{{key}}` in output, page-level `<template if/unless>`, unclosed `<template>` |
324
+ | `--no-format` | off | Skip the automatic prettier pass on the output |
271
325
  | `--version` | | Print version |
272
326
  | `--help` | | Print usage |
273
327
 
328
+ ### Check (dry run)
329
+
330
+ ```bash
331
+ npx templa-js check -i ./src
332
+ ```
333
+
334
+ Runs the full build pipeline without writing anything and always behaves as `--strict`: exit 0 means the source is sound, exit 1 means problems (each one listed). Designed as a machine-readable gate for CI and AI agents — verify a skeleton before filling sections in parallel, or verify a single section after writing it.
335
+
274
336
  ### Build vs runtime
275
337
 
276
338
  Both modes share the same template syntax, so a partial works in either:
@@ -303,10 +365,12 @@ Decision rule: templa for everything that can be resolved before the user clicks
303
365
 
304
366
  ## Working with AI agents
305
367
 
306
- If you use Claude Code, Cursor, Aider, Copilot, or similar AI tools, two files do most of the work:
368
+ If you use Claude Code, Cursor, Aider, Copilot, or similar AI tools, `npx templa-js init --ai` drops four files that do most of the work:
307
369
 
308
- - [`AGENTS.md`](./AGENTS.md) — file conventions, two-phase workflow, syntax, common pitfalls. Drop it into a templa project root and the agent reads it as the source of truth.
370
+ - [`AGENTS.md`](./AGENTS.md) — file conventions, two-phase workflow, syntax, common pitfalls. The orchestrating agent's source of truth.
309
371
  - [`PLANNER.md`](./PLANNER.md) — an instruction prompt that turns a free-form site brief into a concrete `plan.md` (page set, wireframes, section list, design tokens, file inventory) before any file is written.
372
+ - [`SECTION.md`](./SECTION.md) — a compact brief for parallel section sub-agents. Hand each sub-agent this file instead of AGENTS.md: the same rules they need, at a fraction of the tokens per agent.
373
+ - `CLAUDE.md` — a generated pointer file, because Claude Code auto-loads CLAUDE.md (not AGENTS.md); Cursor/Codex-style tools read AGENTS.md directly.
310
374
 
311
375
  ## Philosophy
312
376
 
@@ -318,6 +382,7 @@ Concrete consequences of that stance:
318
382
 
319
383
  - Attribute names follow the platform: `<template src>` mirrors `<img src>` / `<script src>` / `<iframe src>`. We deliberately don't use `data-src`. The bare `src` lets editors and IDEs treat it like a real file reference (path completion, jump-to-file, refactor-rename).
320
384
  - Conditionals are written as `<template if="key">…</template>` and `<template unless="key">…</template>` — no `{{#if}}` Mustache block, no expressions, no helpers. They are existence-based only.
385
+ - Active nav links get `aria-current="page"` automatically. CSS Selectors Level 4 specced `:local-link` for exactly this and no browser shipped it; templa bridges the gap using the ARIA attribute HTML authors already write by hand.
321
386
  - Co-located styles use `data-merge="style.css"` — a `data-*` attribute, because that is HTML's documented hook for component-private metadata.
322
387
  - Anything beyond block-level conditionals (conditional attributes, dynamic class lists, loops) is out of scope for the core and lives in plugins.
323
388
 
package/SECTION.md ADDED
@@ -0,0 +1,37 @@
1
+ # SECTION.md — brief for section sub-agents
2
+
3
+ You are filling in ONE section of a templa site. You own exactly one file:
4
+ the `_partials/[page]-[section].html` you were assigned. This brief is all
5
+ you need — AGENTS.md (the full guide) is for the orchestrator, not you.
6
+
7
+ ## Write
8
+
9
+ The complete HTML for your section, with its styles co-located on top:
10
+
11
+ ```html
12
+ <style data-merge="css/style.css">
13
+ .index-hero { padding: var(--space-6) 0; text-align: center; }
14
+ .index-hero h1 { margin: 0 0 var(--space-2); }
15
+ </style>
16
+ <section class="index-hero">
17
+ <h1>{{heading}}</h1>
18
+ </section>
19
+ ```
20
+
21
+ Rules:
22
+
23
+ - **Root element class = your filename.** `index-hero.html` → `<section class="index-hero">`, and every CSS rule is scoped under that class.
24
+ - **Use the design tokens** from the `:root` block at the top of `css/style.css` (`var(--space-*)`, color and font tokens). No hardcoded colors or ad-hoc spacing values.
25
+ - **Dynamic values** arrive as attributes on the calling `<template>`: read them with `{{kebab-case-key}}` (HTML-escaped) or `{{{key}}}` (raw — trusted HTML only). Optional blocks: `<template if="key">…</template>` / `<template unless="key">…</template>` (existence-based, no expressions).
26
+ - **Interactivity** (toggles, accordions, tabs) is inline Alpine.js (`x-data` etc.) inside your section.
27
+
28
+ ## Never
29
+
30
+ 1. Edit any other file — not `css/style.css`, not `common-*.html`, not page entries, not other sections.
31
+ 2. Reference another section with `<template src>` — sections are leaves, not composers.
32
+ 3. Invent a new `common-*` template. If your section seems to need one, STOP and report back; the orchestrator decides.
33
+ 4. Use a `{{key}}` the caller doesn't pass — only the keys listed in your assignment brief exist.
34
+
35
+ ## Done when
36
+
37
+ Your file is self-contained and `npx templa-js check` exits 0 from the project root.