templa-js 0.10.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/PLANNER.md ADDED
@@ -0,0 +1,285 @@
1
+ # PLANNER.md — AI orchestrator prompt for templa project skeletons
2
+
3
+ You are about to design the **skeleton** of a templa project from a free-form site brief. Read this entire file before doing anything. Your single deliverable is a written plan in chat — the user will save it as `plan.md` at their project root if they accept it. **You will not create or modify any files in this step.**
4
+
5
+ If you have not already, also read `AGENTS.md` (same directory). It is the source of truth for templa's two-phase workflow, syntax, and conventions. This file complements `AGENTS.md`; it does not replace it.
6
+
7
+ ## Your role
8
+
9
+ You are the orchestrator. Given a free-form site brief, your job is to produce a complete, executable plan — detailed enough that downstream sub-agents (or you, in a follow-up step) can implement the skeleton without asking another question.
10
+
11
+ The plan covers the serial part of building a templa site: design tokens, layout, chrome partials, and empty page shells. Parallel content fill happens after this plan is approved and the skeleton built.
12
+
13
+ ## Hard rules
14
+
15
+ 1. **Do not create or modify any file.** Output the plan only. The user reviews and approves before any implementation step.
16
+ 2. **Do not run any build, `npm`, or shell command.** This is a planning step.
17
+ 3. **Default to the canonical `common-*` set** — `common-head`, `common-layout`, `common-header`, `common-footer`, `common-subhero`. Add a new `common-*` template only when the brief unambiguously needs one (e.g. a recurring site-wide CTA banner). Each addition is a cost, not a default.
18
+ 4. **Honor templa conventions.** `common-layout.html` is a body fragment (no `<html>` / `<body>`). Partials receive data via plain HTML attributes; `data-*` attributes are reserved as metadata and skipped. Section files ship with co-located `<style data-merge="css/style.css">` blocks and class names matching the filename.
19
+ 5. **If something in the brief is ambiguous, list it under "Open questions" at the end of the plan.** Do not invent and do not guess silently.
20
+
21
+ ## Project layout assumed by this plan
22
+
23
+ All paths in the plan are relative to the **project root**. The expected layout:
24
+
25
+ ```
26
+ project-root/
27
+ ├── plan.md ← this plan, lives here
28
+ ├── src/ ← templa source (build input)
29
+ │ ├── css/style.css
30
+ │ ├── _partials/
31
+ │ ├── assets/ ← images, copied to dist/ as-is
32
+ │ └── *.html ← entry pages
33
+ └── dist/ ← build output (gitignored, not authored)
34
+ ```
35
+
36
+ The build command is therefore `npx templa-js build -i ./src -o ./dist` (the templa CLI defaults). `plan.md` stays at root and is never inside `src/` (so it never leaks into the build output).
37
+
38
+ ## Input you will receive
39
+
40
+ A free-form site brief from the user. It may include any of:
41
+
42
+ - Project name and one-line pitch
43
+ - Target audience
44
+ - Brand vibe (warm / clean / playful / minimal / dark / editorial / …)
45
+ - Color or typography hints
46
+ - Pages they want
47
+ - Content sections per page
48
+ - Specific features (FAQ, contact form, image gallery, blog, pricing table, …)
49
+
50
+ It may be a single paragraph or several pages. Read it twice. Extract intent before writing.
51
+
52
+ ## Process — walk these in order
53
+
54
+ ### 1. Restate the brief in 3–5 lines
55
+
56
+ Compress the user's brief to its essentials. Confirms you understood and surfaces any gap.
57
+
58
+ ### 2. Decide the page set
59
+
60
+ List every entry HTML file the site will produce. Default minimum: `index.html`. Add pages named or strongly implied by the brief (`about.html`, `contact.html`, `products.html`, `pricing.html`, …). Pages are bare filenames at the source root.
61
+
62
+ ### 3. Wireframe + section list per page
63
+
64
+ For each page, produce **both**:
65
+
66
+ (a) A small ASCII wireframe showing the visual structure top-to-bottom, including grid arrangements (e.g. cards in 3 columns), and labelling each section. Keep it readable — boxes drawn with `┌─┬─┐` style, ~40–60 chars wide. Show the layout chrome (header, footer) so the sub-agent sees the full page envelope.
67
+
68
+ (b) A concise section list under the wireframe with one-line notes per section (filename, purpose, `common-*` template used if any, attributes it accepts).
69
+
70
+ Example for a home page:
71
+
72
+ ```text
73
+ ┌────────────────────────────────────────┐
74
+ │ HEADER (brand · nav) │
75
+ ├────────────────────────────────────────┤
76
+ │ │
77
+ │ HERO │
78
+ │ [bg image] │
79
+ │ Tea, slowly │
80
+ │ [Browse our teas] │
81
+ │ │
82
+ ├────────────────────────────────────────┤
83
+ │ A few favourites │
84
+ │ ┌──────┐ ┌──────┐ ┌──────┐ │
85
+ │ │ card │ │ card │ │ card │ │
86
+ │ └──────┘ └──────┘ └──────┘ │
87
+ ├────────────────────────────────────────┤
88
+ │ Questions │
89
+ │ ▶ Q1 │
90
+ │ ▶ Q2 │
91
+ │ ▶ Q3 │
92
+ ├────────────────────────────────────────┤
93
+ │ FOOTER │
94
+ └────────────────────────────────────────┘
95
+ ```
96
+
97
+ Sections (top → bottom):
98
+ - `_partials/index-hero.html` — landing hero, attrs image / heading / subheading / ctaLabel / ctaHref. Optional fields wrapped in `<template if="ctaLabel">` inside the section.
99
+ - `_partials/index-favourites.html` — section with `<h2>` + grid of 3 inline `<article>` cards (no separate template; markup lives in this section file)
100
+ - `_partials/index-faq.html` — inline Alpine accordion (lives only on this page)
101
+
102
+ The wireframe is for both you (the orchestrator confirming structure) and the content sub-agent who will implement the page.
103
+
104
+ ### 4. Decide the section list per page
105
+
106
+ For each page in §2, list the section files that will exist as `_partials/[pagename]-[sectionname].html`. The wireframe in §3 gives you the order; this step turns that into a flat file list.
107
+
108
+ Use the shared `common-subhero.html` for inner-page headers — do not introduce per-page subhero variants. Anything else gets its own `[pagename]-[sectionname].html` file.
109
+
110
+ If a non-trivial section type would recur across two or more pages (testimonials, blog post snippets, team-member cards, pricing rows), surface it as an "open question" at the end of the plan rather than silently inventing a new `common-*`. The orchestrator decides whether to extend Phase 1 with a new `common-*` template or to inline the duplication across pages.
111
+
112
+ ### 5. Decide design tokens
113
+
114
+ Pick concrete values; do not stay vague. Provide all of:
115
+
116
+ - **Color** — 4–6 CSS-variable tokens with real hex codes that suit the inferred vibe:
117
+ `--color-bg`, `--color-surface`, `--color-text`, `--color-muted`, `--color-accent`, `--color-accent-dark`, `--color-border`.
118
+ - **Typography** — one serif heading family + one sans body family. Use real Google Fonts names with the matching `family=` URL.
119
+ - **Spacing scale** — `--space-1` … `--space-7` (`.25rem` … ~`4rem`). The default scaffold ships these; tune values to fit the brief.
120
+ - **Radius / shadow** — 1–2 values each.
121
+ - **Container widths** — `--max-w` (text column, ~720px) and optionally `--max-w-wide` (~1100px).
122
+
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
+
125
+ ### 6. Decide the `common-*` templates
126
+
127
+ (All paths below are project-root relative.)
128
+
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.
130
+ - **`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`.
132
+ - **`src/_partials/common-footer.html`** — `<footer>` element with copyright/social, identical on every page.
133
+ - **`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
+
135
+ A project may add more `common-*` (rare). If you add any, justify it in §7's open questions.
136
+
137
+ ### 7. Produce the file inventory
138
+
139
+ A complete listing of every file the skeleton will create, **paths relative to the project root**. Each line: path + a one-line role comment. Group by purpose. Example skeleton for a 3-page site (home, about, contact):
140
+
141
+ ```text
142
+ src/css/style.css // tokens + base + chrome (locked in Phase 1)
143
+ src/_partials/common-head.html // <head> chrome (charset, viewport, fonts, css link)
144
+ src/_partials/common-layout.html // body skeleton: header / main slot / footer
145
+ src/_partials/common-header.html // <header>: brand + nav with data-nav
146
+ src/_partials/common-footer.html // <footer>: copyright + socials
147
+ src/_partials/common-subhero.html // shared inner-page subhero (title, optional bg)
148
+ src/_partials/index-hero.html // index — landing hero (sub-agent owned)
149
+ src/_partials/index-features.html // index — features list
150
+ src/_partials/index-cta.html // index — closing CTA
151
+ src/_partials/about-body.html // about — prose / company story
152
+ src/_partials/contact-form.html // contact — contact form
153
+ src/_partials/contact-map.html // contact — embedded map
154
+ src/index.html // page entry — sections: hero, features, cta
155
+ src/about.html // page entry — sections: subhero, body
156
+ src/contact.html // page entry — sections: subhero, form, map
157
+ src/assets/ // images / fonts / etc., copied to dist/ as-is
158
+ ```
159
+
160
+ Every `[pagename]-[sectionname].html` listed above is a unit of Phase 2 sub-agent work. Every `common-*.html` is locked after Phase 1.
161
+
162
+ ### 8. Sketch the section-fill dispatch
163
+
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.
165
+
166
+ The dispatch is genuinely parallel — none of the section files share file content, so sub-agents never collide.
167
+
168
+ Example:
169
+
170
+ - Sub-agent A → `src/_partials/index-hero.html` — large landing hero, headline + subhead + CTA button. Use `--space-7` top padding, `--font-display` for the headline.
171
+ - Sub-agent B → `src/_partials/index-features.html` — 3-column grid of feature cards (inline `<article>` markup; no shared partial). Use `--space-5` between rows.
172
+ - Sub-agent C → `src/_partials/index-cta.html` — closing CTA strip with `--color-accent` background.
173
+ - Sub-agent D → `src/_partials/about-body.html` — prose-style "company story" article with one image.
174
+ - Sub-agent E → `src/_partials/contact-form.html` — `<form action="contact.php">` with name/email/message fields, inline Alpine validation if the brief asks.
175
+ - Sub-agent F → `src/_partials/contact-map.html` — Google Maps `<iframe>` embed.
176
+
177
+ ### 9. State the build gate command
178
+
179
+ End the plan with this line, verbatim:
180
+
181
+ ```bash
182
+ npx templa-js build -i ./src -o ./dist
183
+ ```
184
+
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.
186
+
187
+ ## Output format — your reply takes this shape
188
+
189
+ ```markdown
190
+ # Plan: <project name>
191
+
192
+ ## 1. Brief, restated
193
+
194
+
195
+ ## 2. Page set
196
+ - src/index.html — …
197
+ - src/about.html — …
198
+
199
+ ## 3. Wireframes + sections per page
200
+
201
+ ### index.html
202
+ ```text
203
+ ┌──────────────────────────────────────┐
204
+ │ HEADER │
205
+ ├──────────────────────────────────────┤
206
+ │ HERO │
207
+ ├──────────────────────────────────────┤
208
+ │ A few favourites │
209
+ │ ┌────┐ ┌────┐ ┌────┐ │
210
+ │ └────┘ └────┘ └────┘ │
211
+ ├──────────────────────────────────────┤
212
+ │ Questions (FAQ accordion) │
213
+ ├──────────────────────────────────────┤
214
+ │ FOOTER │
215
+ └──────────────────────────────────────┘
216
+ ```
217
+ - `_partials/index-hero.html` — landing hero, attrs image/heading/subheading/ctaLabel/ctaHref
218
+ - `_partials/index-favourites.html` — 3-column grid of inline `<article>` cards
219
+ - faq — inline Alpine accordion
220
+
221
+ ### about.html
222
+ ```text
223
+ ┌──────────────────────────────────────┐
224
+ │ HEADER │
225
+ ├──────────────────────────────────────┤
226
+ │ SUB-HERO │
227
+ ├──────────────────────────────────────┤
228
+ │ PROSE │
229
+ │ ¶ ¶ [img] ¶ ¶ │
230
+ ├──────────────────────────────────────┤
231
+ │ FOOTER │
232
+ └──────────────────────────────────────┘
233
+ ```
234
+ - `_partials/common-subhero.html` — shared subhero, attrs title/tagline
235
+ - prose — `<article class="prose">` with 4 paragraphs and 1 image
236
+
237
+ ## 4. Section list per page
238
+ - index — sections: index-hero, index-features, index-cta
239
+ - about — sections: common-subhero (title="About"), about-body
240
+
241
+ ## 5. Design tokens
242
+ ```css
243
+ :root {
244
+ --color-bg: #faf8f3;
245
+ --color-text: #1f1f1c;
246
+ /* … */
247
+ }
248
+ ```
249
+ Fonts: Inter (body), Lora (headings).
250
+ (Note: tokens inferred from the "warm, slow" vibe; override if you want.)
251
+
252
+ ## 6. common-* templates
253
+ - src/_partials/common-head.html — charset, viewport, theme-color, fonts, ./css/style.css
254
+ - src/_partials/common-layout.html — header / <main><slot/></main> / footer
255
+ - src/_partials/common-header.html — brand + nav with data-nav
256
+ - src/_partials/common-footer.html — copyright + socials
257
+ - src/_partials/common-subhero.html — inner-page subhero (title attribute)
258
+
259
+ ## 7. File inventory
260
+ ```text
261
+ … full listing per the example above …
262
+ ```
263
+
264
+ ## 8. Section-fill dispatch
265
+ - Sub-agent A → src/_partials/index-hero.html — landing hero with image + CTA
266
+ - Sub-agent B → src/_partials/index-features.html — 3-column features grid
267
+ - Sub-agent C → src/_partials/index-cta.html — closing CTA strip
268
+ - Sub-agent D → src/_partials/about-body.html — prose "about us" article
269
+
270
+ ## 9. Build gate
271
+ ```bash
272
+ npx templa-js build -i ./src -o ./dist
273
+ ```
274
+
275
+ ## Open questions
276
+ - (only if any; otherwise omit this section)
277
+ ```
278
+
279
+ End the plan with one line:
280
+
281
+ > Plan complete — awaiting user approval before any file is written.
282
+
283
+ ## After you finish
284
+
285
+ Stop. Wait for the next instruction. Implementation (writing files, building, dispatching content sub-agents) is a separate step driven by a different prompt or an upcoming SKILL.
package/README.md ADDED
@@ -0,0 +1,326 @@
1
+ # templa
2
+
3
+ > 🍤 A tiny HTML template loader. Pronounced **"tempura"**.
4
+ >
5
+ > Wraps your data in `<template src>` like batter wraps ingredients.
6
+
7
+ `templa` is a tiny dependency-free script that lets you split HTML into reusable partials, pass parameters, and use Handlebars-like syntax — all powered by the native `<template>` element.
8
+
9
+ It works in two modes:
10
+ - **Runtime** (`templa.js`) — partials are fetched and inlined in the browser
11
+ - **Build** (`npx templa-js build`) — partials are inlined ahead of time into static HTML
12
+
13
+ ```html
14
+ <!-- index.html -->
15
+ <body>
16
+ <template src="_partials/header.html" title="Home" loggedIn="yes"></template>
17
+ <main>...</main>
18
+ <template src="_partials/footer.html"></template>
19
+
20
+ <script src="templa.js"></script>
21
+ <script type="module">await templa.start();</script>
22
+ </body>
23
+ ```
24
+
25
+ ```html
26
+ <!-- _partials/header.html -->
27
+ <header>
28
+ <h1>{{title}}</h1>
29
+ <template if="loggedIn">
30
+ <a href="/logout">Logout</a>
31
+ </template>
32
+ <template unless="loggedIn">
33
+ <a href="/login">Login</a>
34
+ </template>
35
+ </header>
36
+ ```
37
+
38
+ ## Why templa?
39
+
40
+ - **No build step** — drop in a `<script>` tag
41
+ - **No dependencies** — pure vanilla JavaScript
42
+ - **Standard HTML** — uses the native `<template>` element, not custom tags
43
+ - **Tiny** — ~240 lines of source, ~3.5KB gzipped
44
+ - **HTML-native** — `{{var}}` for values; `<template if>` / `<template unless>` for conditionals
45
+ - **Recursive** — partials inside partials just work
46
+ - **Resource-aware** — waits for `<link rel="stylesheet">` and `<script src>` inside partials before resolving
47
+
48
+ ## Install
49
+
50
+ ### Via `<script>` tag (CDN)
51
+
52
+ ```html
53
+ <!-- minified (auto-generated by jsDelivr) -->
54
+ <script src="https://cdn.jsdelivr.net/npm/templa-js/templa.min.js"></script>
55
+
56
+ <!-- or unminified, for debugging -->
57
+ <script src="https://cdn.jsdelivr.net/npm/templa-js/templa.js"></script>
58
+ ```
59
+
60
+ > jsDelivr serves `templa.min.js` by minifying the source on the fly, so there is no separate minified file to maintain. Pin a version with `templa-js@0.0.1` if you want immutable URLs.
61
+
62
+ ### Via npm
63
+
64
+ ```bash
65
+ npm install templa-js
66
+ ```
67
+
68
+ ## Init
69
+
70
+ Bootstrap a new project in the current directory:
71
+
72
+ ```bash
73
+ 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 --force # overwrite existing files
76
+ ```
77
+
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
+
80
+ ## Usage
81
+
82
+ ### Basic
83
+
84
+ Mark any place you want a partial with a `<template>` element:
85
+
86
+ ```html
87
+ <template src="_partials/nav.html"></template>
88
+ ```
89
+
90
+ Then call `templa.start()` to expand all of them:
91
+
92
+ ```html
93
+ <script src="templa.js"></script>
94
+ <script type="module">
95
+ await templa.start();
96
+ // any post-init: Alpine.initTree(document.body), AOS.init(), Swiper, …
97
+ </script>
98
+ ```
99
+
100
+ `templa.start()` returns a Promise that resolves once head + body partials have been expanded. The module-script form is required for top-level `await`. Anything written after the `await` runs once the DOM has been fully assembled — perfect for plugins like Alpine.js, AOS, Swiper, etc.
101
+
102
+ ### Passing data
103
+
104
+ Each attribute on the calling `<template>` becomes a string-valued data key inside the partial.
105
+
106
+ ```html
107
+ <template src="_partials/card.html" title="Hello" body="Welcome."></template>
108
+ ```
109
+
110
+ Conditionals (`<template if="key">`) are existence-based, so any non-empty string is truthy — `featured="yes"` is enough to enable a block.
111
+
112
+ Reserved attributes — these are not collected as data: `src` (the partial path), `slot` (slot filler name), `if` / `unless` (conditional markers). Any `data-*` attribute is also skipped, by HTML metadata convention.
113
+
114
+ ### Template syntax
115
+
116
+ | Syntax | Effect |
117
+ |---|---|
118
+ | `{{key}}` | HTML-escaped variable |
119
+ | `{{{key}}}` | Raw variable (no escape) — use only for trusted HTML |
120
+ | `<template if="key">…</template>` | Block kept when `data[key]` is truthy |
121
+ | `<template unless="key">…</template>` | Block kept when `data[key]` is falsy |
122
+
123
+ Conditionals can be nested. Variables fall through unchanged when the key is missing from `data`.
124
+
125
+ ### Layouts and slots
126
+
127
+ A partial can declare insertion points with `<slot>`. Pages fill those slots by writing content inside the calling `<template src>`.
128
+
129
+ Keep layouts as **body fragments** (no `<!DOCTYPE>` / `<html>` / `<head>` / `<body>` wrapper) so they work in both runtime and build modes. Each page provides its own document skeleton and embeds the layout where the body content goes.
130
+
131
+ ```html
132
+ <!-- _layouts/main.html (body fragment) -->
133
+ <header><slot name="nav">Default Nav</slot></header>
134
+ <main><slot></slot></main>
135
+ <footer><slot name="footer">&copy; 2026</slot></footer>
136
+ ```
137
+
138
+ ```html
139
+ <!-- page.html -->
140
+ <!DOCTYPE html>
141
+ <html>
142
+ <head>
143
+ <title>Home</title>
144
+ </head>
145
+ <body>
146
+ <template src="_layouts/main.html">
147
+ <template slot="nav">
148
+ <a href="/">Home</a>
149
+ <a href="/about">About</a>
150
+ </template>
151
+
152
+ <h1>Welcome</h1>
153
+ <p>Anything outside &lt;template slot&gt; goes into the default slot.</p>
154
+
155
+ <!-- footer slot is omitted, so its fallback renders -->
156
+ </template>
157
+
158
+ <script src="https://cdn.jsdelivr.net/npm/templa-js/templa.min.js"></script>
159
+ <script type="module">await templa.start();</script>
160
+ </body>
161
+ </html>
162
+ ```
163
+
164
+ Rules:
165
+
166
+ - `<slot>` (no name) receives every node from the calling `<template>` that is not wrapped in `<template slot="...">`.
167
+ - `<slot name="X">` receives the content of the matching `<template slot="X">` filler.
168
+ - A slot's own children are the **fallback** — they render when no filler is supplied.
169
+ - Slot fillers may themselves contain `<template src="...">` partials; they are expanded recursively in the call site's directory context.
170
+
171
+ 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.
172
+
173
+ ### Nested partials
174
+
175
+ Partials can include other partials. `templa` keeps expanding until no `<template src>` remains.
176
+
177
+ ### Loading order
178
+
179
+ `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.
180
+
181
+ ### Relative paths in nested partials
182
+
183
+ `<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.
184
+
185
+ Other resources (`<img src>`, `<link href>`, `<script src>`) still resolve against the page URL — use absolute or root-relative paths for those.
186
+
187
+ ### Co-located styles
188
+
189
+ A partial can carry its own CSS in a `<style>` block tagged with `data-merge="<file>"`. The build CLI extracts it once per partial and appends it to the named output stylesheet — even if the partial is used 100 times, its rules are written exactly once.
190
+
191
+ ```html
192
+ <!-- _partials/card.html -->
193
+ <style data-merge="style.css">
194
+ .card { background: #fff; border: 1px solid #ddd; padding: 1rem; }
195
+ </style>
196
+ <article class="card">
197
+ <h3>{{title}}</h3>
198
+ </article>
199
+ ```
200
+
201
+ At runtime, the same dedupe applies: the `<style>` block stays in the DOM for the first expansion of a given partial and is stripped on subsequent ones. A `<style>` without `data-merge` is treated as plain inline CSS (existing behaviour).
202
+
203
+ ### Caching and recursion
204
+
205
+ - Identical `<template src>` URLs are fetched once per page (in-memory cache).
206
+ - A safety guard stops expansion after 50 passes to prevent infinite loops from circular includes.
207
+
208
+ ## API
209
+
210
+ ### `templa.start()`
211
+
212
+ Loads all `<template src>` elements (head first, then body). Returns a Promise that resolves once everything is mounted. Use with `await` from a `<script type="module">`.
213
+
214
+ ### `templa.run(selector?)`
215
+
216
+ Lower-level: runs a single expansion pass against `selector` (default: `'template[src]'`). Returns a Promise. Useful if you load partials dynamically.
217
+
218
+ ```js
219
+ await templa.run('#my-region template[src]');
220
+ ```
221
+
222
+ ## Content Security Policy
223
+
224
+ The runtime never calls `eval` or `new Function`. There is no `'unsafe-eval'` requirement; a plain `script-src 'self'` works.
225
+
226
+ For pages that don't need a runtime at all, build with `npx templa-js build` — the output is plain HTML with no template syntax left.
227
+
228
+ ## Caveats
229
+
230
+ - `{{key}}` HTML-escapes its value. If you need to inject HTML, use `{{{key}}}` and make sure the value is trusted.
231
+ - Original `<template src>` elements are removed from the DOM after expansion.
232
+ - This project is in early development (0.0.x). API may change.
233
+
234
+ ## Build (CLI)
235
+
236
+ For static deployment, expand all `<template src>` ahead of time:
237
+
238
+ ```bash
239
+ npx templa-js build -i ./src -o ./dist
240
+ ```
241
+
242
+ The CLI reads every `.html` file under the source directory, recursively inlines its partials with the same syntax as the runtime, and writes the result to the output directory.
243
+
244
+ ### File convention
245
+
246
+ Files and directories whose names start with `_` are treated as partials and are **not** copied to the output:
247
+
248
+ ```
249
+ src/
250
+ ├── index.html ← entry, written to dist/
251
+ ├── about.html ← entry, written to dist/
252
+ └── _partials/ ← skipped (partials only)
253
+ ├── header.html
254
+ └── footer.html
255
+ ```
256
+
257
+ Reference partials with a relative path:
258
+
259
+ ```html
260
+ <template src="_partials/header.html" title="Home"></template>
261
+ ```
262
+
263
+ ### Options
264
+
265
+ | Flag | Default | Description |
266
+ |---|---|---|
267
+ | `-i <dir>` | `./src` | Source directory |
268
+ | `-o <dir>` | `./dist` | Output directory (cleared before each build) |
269
+ | `--version` | | Print version |
270
+ | `--help` | | Print usage |
271
+
272
+ ### Build vs runtime
273
+
274
+ Both modes share the same template syntax, so a partial works in either:
275
+
276
+ | | Runtime (`templa.js`) | Build (`npx templa-js`) |
277
+ |---|---|---|
278
+ | When partials are inlined | At page load, in the browser | Once, ahead of time |
279
+ | Dependencies | none | none |
280
+ | Output | dynamic DOM | static HTML files |
281
+ | Use case | quick prototypes, dev | production deploy, SEO, static hosting |
282
+
283
+ You can also use both: ship the static HTML for first paint and keep `templa.js` for any partials you want to load dynamically later.
284
+
285
+ ## Recommended companion: Alpine.js
286
+
287
+ `templa` handles **composition** (partials, layouts, variables, conditionals at build or load time). For **interactive behaviour** (modals, dropdowns, counters, form state), pair it with [Alpine.js](https://alpinejs.dev/) — also HTML-first, no build step, ~15&nbsp;KB.
288
+
289
+ ```html
290
+ <!-- somewhere in your <head> -->
291
+ <script src="https://cdn.jsdelivr.net/npm/alpinejs@3/dist/cdn.min.js" defer></script>
292
+
293
+ <!-- in any partial -->
294
+ <section x-data="{ open: false }">
295
+ <button @click="open = !open">Toggle</button>
296
+ <div x-show="open">Hidden until clicked.</div>
297
+ </section>
298
+ ```
299
+
300
+ Decision rule: templa for everything that can be resolved before the user clicks; Alpine for everything that depends on user interaction.
301
+
302
+ ## Working with AI agents
303
+
304
+ If you use Claude Code, Cursor, Aider, Copilot, or similar AI tools, two files do most of the work:
305
+
306
+ - [`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.
307
+ - [`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.
308
+
309
+ ## Philosophy
310
+
311
+ templa is not trying to replace HTML. It exists because HTML does not yet have a native way to include partials, compose layouts, and pass small pieces of data between templates.
312
+
313
+ **The biggest competitor is native HTML. That is also the goal.** If one day HTML supports this natively, templa has done its job. Until then, templa is a tiny bridge.
314
+
315
+ Concrete consequences of that stance:
316
+
317
+ - 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).
318
+ - 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.
319
+ - Co-located styles use `data-merge="style.css"` — a `data-*` attribute, because that is HTML's documented hook for component-private metadata.
320
+ - Anything beyond block-level conditionals (conditional attributes, dynamic class lists, loops) is out of scope for the core and lives in plugins.
321
+
322
+ The core stays small enough to read in one sitting. Everything else is a plugin.
323
+
324
+ ## License
325
+
326
+ [MIT](./LICENSE)