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/AGENTS.md +413 -0
- package/LICENSE +21 -0
- package/PLANNER.md +285 -0
- package/README.md +326 -0
- package/bin/templa.js +549 -0
- package/examples/_partials/about-body.html +8 -0
- package/examples/_partials/common-footer.html +6 -0
- package/examples/_partials/common-head.html +4 -0
- package/examples/_partials/common-header.html +15 -0
- package/examples/_partials/common-layout.html +5 -0
- package/examples/_partials/common-subhero.html +7 -0
- package/examples/_partials/index-cta.html +8 -0
- package/examples/_partials/index-features.html +13 -0
- package/examples/_partials/index-hero.html +9 -0
- package/examples/about.html +14 -0
- package/examples/css/style.css +16 -0
- package/examples/index.html +15 -0
- package/examples/serve.json +1 -0
- package/package.json +40 -0
- package/templa.js +266 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
# AGENTS.md — guide for AI agents working with templa
|
|
2
|
+
|
|
3
|
+
This file is for AI coding agents (Claude Code, Cursor, Aider, Copilot, etc.) tasked with editing a templa-based website. **Read it before touching files.** It is structured as a workflow first, reference second.
|
|
4
|
+
|
|
5
|
+
## TL;DR
|
|
6
|
+
|
|
7
|
+
templa is a tiny HTML template loader giving you:
|
|
8
|
+
|
|
9
|
+
- `<template src="...">` to inline another HTML file
|
|
10
|
+
- `{{var}}` for variables; `<template if="key">` / `<template unless="key">` for existence-based conditionals
|
|
11
|
+
- `<slot>` and `<slot name="X">` for Web Components-style layouts
|
|
12
|
+
- A build CLI (`npx templa-js build`) that statically expands all of the above
|
|
13
|
+
|
|
14
|
+
Same syntax works at runtime (browser) and at build time. Zero dependencies.
|
|
15
|
+
|
|
16
|
+
## When templa fits
|
|
17
|
+
|
|
18
|
+
| Task | templa? |
|
|
19
|
+
|---|---|
|
|
20
|
+
| Marketing site / landing page (1–30 pages) | ✅ great |
|
|
21
|
+
| Documentation site, portfolio, personal blog | ✅ great |
|
|
22
|
+
| Page-driven static site that needs SEO | ✅ build mode |
|
|
23
|
+
| App with auth, DB, server-rendered data | ❌ pick Next.js / Astro |
|
|
24
|
+
| SPA with heavy client state | ❌ pick React / Vue / Svelte |
|
|
25
|
+
| Quick interactive widgets on otherwise static pages | ✅ templa + Alpine.js |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## How to build a site — the two-phase rule
|
|
30
|
+
|
|
31
|
+
Building a coherent site requires **two distinct phases**, in order. Skipping or interleaving them produces visually fragmented sites where every section reinvents its own typography, spacing, and component shapes.
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
┌────────────────────────────┐ ┌────────────────────────────┐
|
|
35
|
+
│ Phase 1 — skeleton │ │ Phase 2 — section fill │
|
|
36
|
+
│ serial, orchestrator only │ → │ parallel, sub-agents │
|
|
37
|
+
│ │ │ │
|
|
38
|
+
│ • design tokens │ │ • one sub-agent per │
|
|
39
|
+
│ • common-* templates │ │ [page]-[section].html │
|
|
40
|
+
│ • page entry HTML │ │ • full HTML + co-located │
|
|
41
|
+
│ • empty section files │ │ style per section │
|
|
42
|
+
└────────────────────────────┘ └────────────────────────────┘
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Phase 1 — skeleton (serial, orchestrator only)
|
|
46
|
+
|
|
47
|
+
Lock down everything Phase 2 sub-agents will *consume*. **One agent in charge. No parallel work yet.**
|
|
48
|
+
|
|
49
|
+
> 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
|
+
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
|
+
|
|
53
|
+
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
|
+
| File | Purpose |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `common-head.html` | Everything inside `<head>` except doctype/title — charset, viewport, font links, stylesheet link |
|
|
58
|
+
| `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-footer.html` | The site `<footer>` element |
|
|
61
|
+
| `common-subhero.html` | Shared inner-page subhero, parameterized via `title` (and optionally `bg`) |
|
|
62
|
+
|
|
63
|
+
A project may add more `common-*` files (e.g. `common-cta-banner.html`) only by orchestrator decision — never by sub-agent invention.
|
|
64
|
+
|
|
65
|
+
3. **Page entry HTML files** — each `src/[pagename].html` is a thin composition: an HTML5 document whose `<head>` calls `common-head`, whose `<body>` calls `common-layout` containing an ordered list of `<template src="_partials/[pagename]-[section].html">` lines. No inline section content.
|
|
66
|
+
|
|
67
|
+
4. **Empty section files** — every `_partials/[pagename]-[section].html` referenced by a page exists as a placeholder file (a single HTML comment is enough) so `npx templa-js build` succeeds against the skeleton.
|
|
68
|
+
|
|
69
|
+
Phase 1 is complete when:
|
|
70
|
+
|
|
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
|
+
- The output renders cohesively even with placeholder section content.
|
|
73
|
+
- Every section file the pages reference exists on disk.
|
|
74
|
+
|
|
75
|
+
### Phase 2 — section fill (parallel, sub-agents)
|
|
76
|
+
|
|
77
|
+
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
|
+
A sub-agent's brief is exactly:
|
|
80
|
+
- The single file path it owns (e.g. `_partials/index-hero.html`).
|
|
81
|
+
- A 2–3 line description of what the section should contain.
|
|
82
|
+
- The page's existing wireframe / surrounding context (so it knows what comes before and after, but not so it can edit those).
|
|
83
|
+
|
|
84
|
+
Sub-agents may:
|
|
85
|
+
- Write the full HTML for their section, including a `<style data-merge="css/style.css">` block with section-scoped rules.
|
|
86
|
+
- Use design tokens defined in `style.css` (`var(--space-*)`, color tokens, etc.).
|
|
87
|
+
- Pick a class name matching the file (`.index-hero` for `index-hero.html`).
|
|
88
|
+
|
|
89
|
+
Sub-agents must **not**:
|
|
90
|
+
- Touch `style.css` (design tokens — locked in Phase 1).
|
|
91
|
+
- Touch any `common-*.html` file.
|
|
92
|
+
- Touch any other `[pagename]-[section].html` owned by another sub-agent.
|
|
93
|
+
- Touch page entry HTML files (`src/index.html`, `src/about.html`, …).
|
|
94
|
+
- Reference another section file via `<template src>` (sections are leaves, not composers).
|
|
95
|
+
- Invent a new `common-*` template. If a sub-agent decides one is needed, **stop, escalate to the orchestrator** for a brief Phase 1.5 (extend the skeleton serially), then resume Phase 2.
|
|
96
|
+
|
|
97
|
+
### Why this order matters
|
|
98
|
+
|
|
99
|
+
Parallel sub-agents have no shared visual context. Started before the skeleton locks the design tokens and chrome, each agent will:
|
|
100
|
+
|
|
101
|
+
- Pick its own typography and spacing → site looks Frankensteined.
|
|
102
|
+
- Reinvent header/footer shapes → CSS bloats, components don't compose.
|
|
103
|
+
- Make inconsistent inner-page headers → no rhythm between pages.
|
|
104
|
+
|
|
105
|
+
The **`common-subhero.html`** pattern is the canonical example. Define it once in Phase 1; every inner page calls it in Phase 2 → consistent inner-page chrome with zero coordination overhead between sub-agents.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## File conventions
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
src/
|
|
113
|
+
├── index.html ← page entry (written to dist/)
|
|
114
|
+
├── about.html ← page entry
|
|
115
|
+
├── css/style.css ← design tokens + base + chrome (locked in Phase 1)
|
|
116
|
+
└── _partials/ ← partials (skipped from output)
|
|
117
|
+
├── common-head.html ← <head> chrome
|
|
118
|
+
├── common-layout.html ← body skeleton with <main><slot/></main>
|
|
119
|
+
├── common-header.html ← <header> chrome
|
|
120
|
+
├── common-footer.html ← <footer> chrome
|
|
121
|
+
├── common-subhero.html ← shared inner-page subhero
|
|
122
|
+
├── index-hero.html ← page-specific section (sub-agent owned)
|
|
123
|
+
├── index-features.html
|
|
124
|
+
├── index-cta.html
|
|
125
|
+
└── about-body.html
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- Files and directories starting with `_` are partials and are **not copied** to the build output. Reference them with relative paths.
|
|
129
|
+
- Entry pages live at any non-underscore path under the source root.
|
|
130
|
+
- `<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">`.
|
|
131
|
+
- 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.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Composition pattern
|
|
137
|
+
|
|
138
|
+
Every page is a thin HTML5 document whose body is a `common-layout` invocation containing an ordered list of section partials.
|
|
139
|
+
|
|
140
|
+
```html
|
|
141
|
+
<!-- src/index.html -->
|
|
142
|
+
<!DOCTYPE html>
|
|
143
|
+
<html lang="en">
|
|
144
|
+
<head>
|
|
145
|
+
<template src="_partials/common-head.html" title="Home"></template>
|
|
146
|
+
</head>
|
|
147
|
+
<body>
|
|
148
|
+
<template src="_partials/common-layout.html">
|
|
149
|
+
<template src="_partials/index-hero.html"></template>
|
|
150
|
+
<template src="_partials/index-features.html"></template>
|
|
151
|
+
<template src="_partials/index-cta.html"></template>
|
|
152
|
+
</template>
|
|
153
|
+
<script src="./js/templa.js"></script>
|
|
154
|
+
<script type="module">await templa.start();</script>
|
|
155
|
+
</body>
|
|
156
|
+
</html>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```html
|
|
160
|
+
<!-- src/_partials/common-layout.html — body fragment, NOT a full document -->
|
|
161
|
+
<template src="common-header.html"></template>
|
|
162
|
+
<main>
|
|
163
|
+
<slot></slot>
|
|
164
|
+
</main>
|
|
165
|
+
<template src="common-footer.html"></template>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
```html
|
|
169
|
+
<!-- src/_partials/index-hero.html — focused, self-styling, leaf section -->
|
|
170
|
+
<style data-merge="css/style.css">
|
|
171
|
+
.index-hero { padding: var(--space-6) 0 var(--space-5); text-align: center; }
|
|
172
|
+
.index-hero h1 { margin: 0 0 var(--space-2); font-size: 2rem; }
|
|
173
|
+
</style>
|
|
174
|
+
<section class="index-hero">
|
|
175
|
+
<h1>Hello, templa</h1>
|
|
176
|
+
<p>A tiny HTML template loader.</p>
|
|
177
|
+
</section>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
A page's diff when "add a section" is requested: 1 new file in `_partials/` plus 1 new line in the page's body.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Syntax reference
|
|
185
|
+
|
|
186
|
+
### Passing data to a partial
|
|
187
|
+
|
|
188
|
+
Each attribute on the calling `<template>` becomes a string data key inside the partial. That covers the common case:
|
|
189
|
+
|
|
190
|
+
```html
|
|
191
|
+
<template src="_partials/card.html" title="Tiny" body="Light, ~3KB."></template>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Inside `card.html`, `{{title}}` resolves to `Tiny` and `{{body}}` to `Light, ~3KB.`.
|
|
195
|
+
|
|
196
|
+
Reserved attributes — these are NOT collected as data:
|
|
197
|
+
|
|
198
|
+
| Attribute | Purpose |
|
|
199
|
+
|---|---|
|
|
200
|
+
| `src` | path of the partial to fetch |
|
|
201
|
+
| `slot` | slot filler name (see Layouts) |
|
|
202
|
+
| `if` / `unless` | existence-based conditional markers (see Conditionals below) |
|
|
203
|
+
|
|
204
|
+
Any `data-*` attribute on a `<template>` is also skipped from data collection — it's reserved as HTML metadata convention.
|
|
205
|
+
|
|
206
|
+
Strings handle every common case. For conditionals, the value is checked existentially (truthy unless empty), so `featured="yes"` is enough. There's no typed-value escape hatch in the core; if a project needs typed values (numbers, booleans, arrays, objects, computed values), that goes through a plugin.
|
|
207
|
+
|
|
208
|
+
A few patterns to avoid (they are silently ignored):
|
|
209
|
+
|
|
210
|
+
```html
|
|
211
|
+
<!-- Vue/Alpine binding prefix — not recognized by templa. -->
|
|
212
|
+
<template src="x.html" :params="{ ... }"></template>
|
|
213
|
+
|
|
214
|
+
<!-- Pre-0.2.0 syntax — REMOVED. Use multi-attribute. -->
|
|
215
|
+
<template src="x.html" params="{ ... }"></template>
|
|
216
|
+
|
|
217
|
+
<!-- Pre-0.5.0 typed escape — REMOVED. data-* attributes are now metadata. -->
|
|
218
|
+
<template src="x.html" data-params="{ count: 3 }"></template>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Variables in templates
|
|
222
|
+
|
|
223
|
+
Once data is passed in, partials read it with these placeholders:
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
{{key}} HTML-escaped variable
|
|
227
|
+
{{{key}}} raw variable (use only for trusted HTML)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Co-located styles (`<style data-merge="...">`)
|
|
231
|
+
|
|
232
|
+
A partial can carry its own CSS in a `<style>` block tagged with `data-merge="<file>"`. templa extracts it once per partial and appends it to the named output stylesheet, so the same partial used 100 times still contributes its rules exactly once.
|
|
233
|
+
|
|
234
|
+
```html
|
|
235
|
+
<!-- _partials/card.html -->
|
|
236
|
+
<style data-merge="css/style.css">
|
|
237
|
+
.card { background: #fff; border: 1px solid #ddd; padding: 1rem; }
|
|
238
|
+
.card h3 { margin: 0 0 .5rem; }
|
|
239
|
+
</style>
|
|
240
|
+
<article class="card">
|
|
241
|
+
<h3>{{title}}</h3>
|
|
242
|
+
<p>{{body}}</p>
|
|
243
|
+
</article>
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**Build mode:** the `<style>` block is removed from each card's expanded HTML and appended to `dist/css/style.css` (created if missing) with a `/* templa merged */` marker.
|
|
247
|
+
|
|
248
|
+
**Runtime mode:** on the first expansion of a partial with a merge style, the `<style>` block stays in place (browsers handle `<style>` globally regardless of position); subsequent expansions of the same partial drop it. The `data-merge` attribute is the dedupe signal — the target value is a build-only hint.
|
|
249
|
+
|
|
250
|
+
A `<style>` without `data-merge` stays inline as written.
|
|
251
|
+
|
|
252
|
+
When to use: any partial whose presence implies its own visual rules. Co-locating keeps each section a single self-contained unit and matches Phase 1's design token discipline.
|
|
253
|
+
|
|
254
|
+
### Conditionals
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
<template if="key">…</template>
|
|
258
|
+
<template unless="key">…</template>
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Existence-based only.** The value of `if`/`unless` is a single key looked up in the surrounding data. There are no expressions, no operators, no helpers, no `else`. If you need an else branch, write the inverse pair:
|
|
262
|
+
|
|
263
|
+
```html
|
|
264
|
+
<template if="loggedIn"><a href="/logout">Logout</a></template>
|
|
265
|
+
<template unless="loggedIn"><a href="/login">Login</a></template>
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Conditionals can be nested. Resolution iterates until stable.
|
|
269
|
+
|
|
270
|
+
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.
|
|
271
|
+
|
|
272
|
+
### Layouts and slots
|
|
273
|
+
|
|
274
|
+
```html
|
|
275
|
+
<!-- _partials/page.html -->
|
|
276
|
+
<header><slot name="nav"></slot></header>
|
|
277
|
+
<main><slot></slot></main>
|
|
278
|
+
<footer>© 2026</footer>
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
```html
|
|
282
|
+
<!-- a page using the layout -->
|
|
283
|
+
<template src="_partials/page.html">
|
|
284
|
+
<template slot="nav">…</template> <!-- → <slot name="nav"> -->
|
|
285
|
+
<h1>…</h1> <!-- → <slot> (default) -->
|
|
286
|
+
<p>…</p> <!-- → <slot> (default) -->
|
|
287
|
+
</template>
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Anything inside the calling `<template src>` not wrapped in a `<template slot="X">` becomes the default slot's payload. A slot's own children are the **fallback** used when no filler is supplied.
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Static composition vs runtime interactivity
|
|
295
|
+
|
|
296
|
+
Decision rule:
|
|
297
|
+
|
|
298
|
+
- **Anything resolvable at build time / page load** → templa
|
|
299
|
+
- **Anything that depends on user input or runtime state** → Alpine.js
|
|
300
|
+
|
|
301
|
+
Pair templa with [Alpine.js](https://alpinejs.dev/) for interactive bits (modals, dropdowns, accordions, tabs, form behaviour). Add Alpine once in your meta partial or page `<head>`:
|
|
302
|
+
|
|
303
|
+
```html
|
|
304
|
+
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3/dist/cdn.min.js" defer></script>
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Use `x-*` attributes inside any partial:
|
|
308
|
+
|
|
309
|
+
```html
|
|
310
|
+
<section x-data="{ open: false }">
|
|
311
|
+
<button @click="open = !open" x-text="open ? 'Hide' : 'Show'">Show</button>
|
|
312
|
+
<div x-show="open">Hidden content here.</div>
|
|
313
|
+
</section>
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Do not use Alpine to compose the page (use templa). Do not use templa for runtime state (use Alpine).
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Runtime vs build
|
|
321
|
+
|
|
322
|
+
| Mode | Setup | Best for |
|
|
323
|
+
|---|---|---|
|
|
324
|
+
| **Runtime** | `<script src="…/templa.min.js"></script><script type="module">await templa.start();</script>` | Local dev, prototypes, internal tools, pages where SEO doesn't matter |
|
|
325
|
+
| **Build** | `npx templa-js build -i ./src -o ./dist` | Production, SEO, social previews, fastest first paint |
|
|
326
|
+
|
|
327
|
+
Layout source files must be **body fragments** (no `<html>` / `<body>` wrapper) so they expand correctly in both modes.
|
|
328
|
+
|
|
329
|
+
For SEO-sensitive pages, always build before deploy. Crawlers other than Googlebot frequently do not run JavaScript, and social-preview crawlers (Twitter, Facebook, Slack) never do.
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## Build CLI
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
npx templa-js build # default: ./src → ./dist
|
|
337
|
+
npx templa-js build -i pages -o public
|
|
338
|
+
npx templa-js --help
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
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.
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Pitfalls — read before debugging
|
|
346
|
+
|
|
347
|
+
1. **Started Phase 2 too early.** If sub-agents are producing visually inconsistent sections, your skeleton wasn't done. Stop, finish Phase 1, restart Phase 2.
|
|
348
|
+
|
|
349
|
+
2. **Sub-agent invented a new `common-*` template.** This silently breaks the orchestrator-only contract. Audit `_partials/common-*.html` for new files added during Phase 2; revert them and escalate to Phase 1.5 instead.
|
|
350
|
+
|
|
351
|
+
3. **Forgot the `_` prefix.** A partial referenced by other pages but living at `src/header.html` will be written to `dist/header.html`, leaking your shell. Rename or move into `_partials/`.
|
|
352
|
+
|
|
353
|
+
4. **Wrong relative path in a partial.** `<template src="X">` is resolved relative to the file containing the tag. A partial in a nested directory referencing `_partials/x.html` as a bare name resolves wrongly (templa looks under the nested dir, not the project root). Keep partials flat in `_partials/` and reference siblings by bare filename to avoid this.
|
|
354
|
+
|
|
355
|
+
5. **`{{key}}` placeholders surviving into the output as literal text.** The partial received no value for that key. Pass it as a regular attribute (`title="Home"`). The pre-0.2.0 `params="{ ... }"` and pre-0.5.0 `data-params="{ ... }"` forms have been removed; all data flows through plain attributes.
|
|
356
|
+
|
|
357
|
+
6. **Conditional that always renders or never renders.** `<template if>` is existence-based and any non-empty string is truthy, including the literal string `"false"`. Omit the attribute (or set it to empty `""`) to express "no". For inverse, use `<template unless="key">`.
|
|
358
|
+
|
|
359
|
+
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.
|
|
360
|
+
|
|
361
|
+
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.
|
|
362
|
+
|
|
363
|
+
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>`.
|
|
364
|
+
|
|
365
|
+
10. **Runtime + Alpine timing.** Alpine auto-initializes before templa finishes expanding body templates, so x-data inside expanded content is missed. Pattern:
|
|
366
|
+
|
|
367
|
+
```html
|
|
368
|
+
<script src="../templa.js"></script>
|
|
369
|
+
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3/dist/cdn.min.js" defer></script>
|
|
370
|
+
<script type="module">
|
|
371
|
+
await templa.start();
|
|
372
|
+
window.Alpine && Alpine.initTree(document.body);
|
|
373
|
+
</script>
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
In build mode, Alpine attributes are pre-rendered into the static HTML and Alpine inits normally — no coordination needed.
|
|
377
|
+
|
|
378
|
+
11. **Runtime bootstrap leaks into build output.** The build CLI strips the canonical loader and start call automatically:
|
|
379
|
+
|
|
380
|
+
| Source | Build output |
|
|
381
|
+
|---|---|
|
|
382
|
+
| `<script src="…/templa.js"></script>` | removed |
|
|
383
|
+
| `<script src="…/templa.js" data-keep></script>` | preserved (opt-out) |
|
|
384
|
+
| `<script type="module">await templa.start();</script>` | tag removed |
|
|
385
|
+
| `<script>templa.start();</script>` | tag removed |
|
|
386
|
+
| multi-line module with `await templa.start();` followed by post-init | only the `await` line is removed; post-init survives |
|
|
387
|
+
| `templa.start().then(...)`, `const p = templa.start()`, etc. | left alone — write these only when you intentionally want runtime behaviour preserved |
|
|
388
|
+
|
|
389
|
+
Stick to the canonical `await templa.start();` form unless you have a reason not to. Anything more complex falls outside the strip and will run at the consumer's page if your build output keeps it.
|
|
390
|
+
|
|
391
|
+
12. **Sub-agent referenced another section file with `<template src>`.** Sections are leaves; they don't compose other sections. If two sections share content, that content belongs in a `common-*.html` template introduced via Phase 1.5.
|
|
392
|
+
|
|
393
|
+
13. **Class name doesn't match the filename.** `_partials/index-hero.html` styles must be scoped under `.index-hero`. Different names invite cross-page class collisions and break the audit-by-grep workflow.
|
|
394
|
+
|
|
395
|
+
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.
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## Quick command reference
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
# Install
|
|
403
|
+
npm install templa-js
|
|
404
|
+
|
|
405
|
+
# Build
|
|
406
|
+
npx templa-js build -i ./src -o ./dist
|
|
407
|
+
|
|
408
|
+
# Version
|
|
409
|
+
npx templa-js --version
|
|
410
|
+
|
|
411
|
+
# CDN (runtime)
|
|
412
|
+
# <script src="https://cdn.jsdelivr.net/npm/templa-js/templa.min.js"></script>
|
|
413
|
+
```
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 yjmtmtk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|