tkeron 6.0.0 → 6.0.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.
Files changed (41) hide show
  1. package/changelog.md +18 -0
  2. package/examples/init_sample/websrc/about.html +19 -0
  3. package/examples/init_sample/websrc/components/content/about-intro.com.md +7 -0
  4. package/examples/init_sample/websrc/components/layout/hero-section.com.html +88 -0
  5. package/examples/init_sample/websrc/components/layout/site-footer.com.html +20 -0
  6. package/examples/init_sample/websrc/components/layout/site-header.com.html +44 -0
  7. package/examples/init_sample/websrc/components/ui/counter-button.com.html +48 -0
  8. package/examples/init_sample/websrc/components/ui/crypto-prices-card.com.html +52 -0
  9. package/examples/init_sample/websrc/{info-card.com.html → components/ui/info-card.com.html} +10 -6
  10. package/examples/init_sample/websrc/components/ui/markdown-card.com.html +21 -0
  11. package/examples/init_sample/websrc/{pre-render-card.com.html → components/ui/pre-render-card.com.html} +6 -3
  12. package/examples/init_sample/websrc/components/ui/quote-card.com.html +32 -0
  13. package/examples/init_sample/websrc/components/ui/styles-injector.com.ts +5 -0
  14. package/examples/init_sample/websrc/{ts-components-card.com.html → components/ui/ts-components-card.com.html} +2 -2
  15. package/examples/init_sample/websrc/components/ui/user-badge.com.ts +30 -0
  16. package/examples/init_sample/websrc/docs.html +33 -0
  17. package/examples/init_sample/websrc/index.html +10 -242
  18. package/examples/init_sample/websrc/index.post.ts +70 -0
  19. package/examples/init_sample/websrc/index.pre.ts +10 -66
  20. package/examples/init_sample/websrc/index.ts +9 -7
  21. package/examples/init_sample/websrc/styles/global-styles.com.ts +5 -0
  22. package/examples/init_sample/websrc/styles/main.css +112 -0
  23. package/examples/init_sample/websrc/{api-service.ts → utils/api-service.ts} +32 -37
  24. package/examples/with_global_styles/websrc/bad.html +23 -0
  25. package/examples/with_global_styles/websrc/global-styles.com.ts +5 -0
  26. package/examples/with_global_styles/websrc/good.html +22 -0
  27. package/examples/with_global_styles/websrc/index.html +25 -0
  28. package/examples/with_global_styles/websrc/styles.css +24 -0
  29. package/examples/with_style_dedup/websrc/tag-chip.com.html +15 -0
  30. package/examples/with_style_dedup/websrc/tag-chip.com.ts +2 -18
  31. package/package.json +1 -1
  32. package/skills/tkeron/SKILL.md +4 -3
  33. package/skills/tkeron-organization/SKILL.md +6 -5
  34. package/skills/tkeron-patterns/SKILL.md +109 -17
  35. package/skills/tkeron-troubleshooting/SKILL.md +7 -3
  36. package/examples/init_sample/websrc/counter-card.com.html +0 -12
  37. package/examples/init_sample/websrc/favicon.ico +0 -0
  38. package/examples/init_sample/websrc/hero-section.com.html +0 -53
  39. package/examples/init_sample/websrc/user-badge.com.ts +0 -17
  40. /package/examples/init_sample/websrc/{profile.png → assets/profile.png} +0 -0
  41. /package/examples/init_sample/websrc/{html-components-card.com.html → components/ui/html-components-card.com.html} +0 -0
@@ -0,0 +1,5 @@
1
+ import { join } from "path";
2
+
3
+ const css = await Bun.file(join(__dirname, "styles.css")).text();
4
+
5
+ com.innerHTML = `<style>${css}</style>`;
@@ -0,0 +1,22 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Good — global-styles component</title>
7
+ <global-styles></global-styles>
8
+ </head>
9
+ <body>
10
+ <h1>Good: <code>&lt;global-styles&gt;</code></h1>
11
+ <p>
12
+ The <code>&lt;global-styles&gt;</code> component reads
13
+ <code>styles.css</code> at build time and injects it as a
14
+ <code>&lt;style&gt;</code> block directly in <code>&lt;head&gt;</code>.
15
+ </p>
16
+ <p>
17
+ Check <code>web/good.html</code>: no external CSS file, all styles are
18
+ inline. Zero extra HTTP requests.
19
+ </p>
20
+ <a href="index.html">Back to index</a>
21
+ </body>
22
+ </html>
@@ -0,0 +1,25 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Global Styles — Overview</title>
7
+ </head>
8
+ <body>
9
+ <h1>Global Styles Import</h1>
10
+ <p>
11
+ Two approaches for adding shared CSS to a tkeron page. Compare the
12
+ compiled output in <code>web/</code>.
13
+ </p>
14
+ <ul>
15
+ <li>
16
+ <a href="bad.html">Bad — <code>&lt;link rel="stylesheet"&gt;</code></a>:
17
+ CSS ends up in a separate file (extra HTTP request).
18
+ </li>
19
+ <li>
20
+ <a href="good.html">Good — <code>&lt;global-styles&gt;</code></a>:
21
+ CSS is inlined at build time (no extra request).
22
+ </li>
23
+ </ul>
24
+ </body>
25
+ </html>
@@ -0,0 +1,24 @@
1
+ body {
2
+ font-family: system-ui, sans-serif;
3
+ max-width: 640px;
4
+ margin: 2rem auto;
5
+ padding: 0 1rem;
6
+ color: #1a1a1a;
7
+ line-height: 1.6;
8
+ }
9
+
10
+ h1 {
11
+ font-size: 1.5rem;
12
+ margin-bottom: 0.5rem;
13
+ }
14
+
15
+ p {
16
+ margin: 0.75rem 0;
17
+ }
18
+
19
+ code {
20
+ background: #f0f0f0;
21
+ padding: 0.1em 0.35em;
22
+ border-radius: 3px;
23
+ font-size: 0.875em;
24
+ }
@@ -0,0 +1,15 @@
1
+ <style>
2
+ .tag-chip {
3
+ display: inline-block;
4
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
5
+ color: white;
6
+ padding: 0.2rem 0.65rem;
7
+ border-radius: 999px;
8
+ font-size: 0.75rem;
9
+ font-weight: 600;
10
+ margin: 0.2rem;
11
+ font-family: monospace;
12
+ letter-spacing: 0.02em;
13
+ }
14
+ </style>
15
+ <span class="tag-chip">label...</span>
@@ -1,19 +1,3 @@
1
1
  const label = com.getAttribute("label") || "tag";
2
-
3
- com.innerHTML = `
4
- <style>
5
- .tag-chip {
6
- display: inline-block;
7
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
8
- color: white;
9
- padding: 0.2rem 0.65rem;
10
- border-radius: 999px;
11
- font-size: 0.75rem;
12
- font-weight: 600;
13
- margin: 0.2rem;
14
- font-family: monospace;
15
- letter-spacing: 0.02em;
16
- }
17
- </style>
18
- <span class="tag-chip">${label}</span>
19
- `;
2
+ const span = com.querySelector(".tag-chip");
3
+ if (span) span.textContent = label;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tkeron",
3
- "version": "6.0.0",
3
+ "version": "6.0.1",
4
4
  "description": "CLI build tool for vanilla web development with TypeScript.",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -181,7 +181,7 @@ Limits: max **10 iterations**, max **50 nesting levels**, no circular dependenci
181
181
  <head>
182
182
  <meta charset="UTF-8" />
183
183
  <title>My Site</title>
184
- <link rel="stylesheet" href="./styles.css" />
184
+ <global-styles></global-styles>
185
185
  </head>
186
186
  <body>
187
187
  <site-header></site-header>
@@ -191,10 +191,11 @@ Limits: max **10 iterations**, max **50 nesting levels**, no circular dependenci
191
191
  </html>
192
192
  ```
193
193
 
194
- **Two non-negotiable rules**:
194
+ **Three non-negotiable rules**:
195
195
 
196
196
  1. **`src="index.ts"` with `type="module"`**, NEVER `.js`. Tkeron compiles and rewrites the reference.
197
- 2. **Relative paths only** (`./`, `../`). NEVER absolute (`/styles.css`) — Bun cannot resolve them at build time. Applies to `.html`, `.com.html`, and `url()` in CSS.
197
+ 2. **Relative paths only** (`./`, `../`). NEVER absolute (`/file.ext`) — Bun cannot resolve them at build time. Applies to `.html`, `.com.html`, and `url()` in CSS.
198
+ 3. **CSS via components, never `<link rel="stylesheet">`**. A `<global-styles>` component reads the `.css` file at build time and inlines it as `<style>` — no extra request, dedup-friendly. See `tkeron-patterns` → "CSS via components".
198
199
 
199
200
  ---
200
201
 
@@ -39,9 +39,10 @@ websrc/
39
39
  │ ├── hero-text.com.md
40
40
  │ └── about-section.com.md
41
41
 
42
- ├── styles/
43
- ├── main.css # Global styles
44
- └── components.css # Component styles
42
+ ├── styles/ # OPTIONAL — .css source files (or place them
43
+ │ anywhere). Never loaded via <link>; a
44
+ .com.ts component reads and inlines them.
45
+ │ └── main.css # Tokens + resets + body typography (small!)
45
46
 
46
47
  └── utils/
47
48
  ├── format.ts # Helpers (imported by .com.ts or .pre.ts)
@@ -164,7 +165,7 @@ thing.com.html → No clear purpose
164
165
  ✅ GOOD:
165
166
  index.html, about.html, blog-post.html
166
167
  api-service.ts, format-utils.ts
167
- main.css, components.css
168
+ main.css, tokens.css
168
169
 
169
170
  ❌ BAD:
170
171
  Index.html → Always lowercase
@@ -198,7 +199,7 @@ websrc/assets/hero-image.png
198
199
  | Per-page `.pre.ts`/`.post.ts` | Same directory as the paired `.html` |
199
200
  | Shared components | `websrc/components/<domain>/` (`layout/`, `ui/`, `content/`) |
200
201
  | Local-override components | Same directory as the page that uses them (wins by priority) |
201
- | Global styles | `websrc/styles/` |
202
+ | Global styles | Free choice (root of `websrc/`, a `styles/` folder, next to the loader component). Source `.css` files are NOT loaded via `<link>`; a `.com.ts` reads them and inlines as `<style>` (see `tkeron-patterns` → "CSS via components"). Per-component CSS lives **inside** each `.com.html`. |
202
203
  | Build-time utils (Bun) | `websrc/utils/` — imported by `.pre.ts`, `.post.ts`, `.com.ts` |
203
204
  | Static assets | `websrc/assets/` (or wherever — they are copied as-is) |
204
205
  | Output | `web/` — **NEVER edit, always in `.gitignore`** |
@@ -211,25 +211,102 @@ if (!isProd) {
211
211
  }
212
212
  ```
213
213
 
214
- ### CSS classes, not repeated inline styles
214
+ ### CSS via components (NEVER `<link rel="stylesheet">`)
215
215
 
216
- ```css
217
- /* styles/components.css */
218
- :root {
219
- --primary: #3b82f6;
220
- --spacing: 1rem;
221
- }
216
+ **Rule**: CSS in tkeron lives inside `<style>` blocks emitted by components. Pages MUST NOT use `<link rel="stylesheet">`. This unlocks three properties:
222
217
 
223
- .card {
224
- padding: var(--spacing);
225
- border: 1px solid #e5e7eb;
226
- border-radius: 8px;
227
- }
218
+ 1. **Auto-dedup**: tkeron keeps a single `<style data-tk-com="...">` in `<head>` no matter how many times the component is used.
219
+ 2. **Tree-shaking**: if a component is not used on a page, its CSS does not ship to that page.
220
+ 3. **Zero extra requests**: CSS travels inside the HTML download → first paint without waiting for another request.
221
+
222
+ #### Per-component CSS — in `.com.html`
223
+
224
+ ```html
225
+ <!-- card.com.html -->
226
+ <style>
227
+ .card { padding: 1rem; border: 1px solid #e5e7eb; border-radius: 8px; }
228
+ </style>
229
+ <div class="card"></div>
228
230
  ```
229
231
 
230
232
  ```typescript
231
- // card.com.ts
232
- com.innerHTML = `<div class="card">${content}</div>`;
233
+ // card.com.ts (optional — only if logic is needed)
234
+ const body = com.querySelector(".card");
235
+ if (body) body.textContent = com.getAttribute("text") || "";
236
+ ```
237
+
238
+ > If a `.com.html` exists next to a `.com.ts`, the HTML is loaded into `com.innerHTML` **before** the `.com.ts` runs. The `<style>` is preserved and dedup applies. Do NOT re-emit the `<style>` from `.com.ts`.
239
+
240
+ **Build output — dedup in action.** Component used 6 times → `<head>` receives exactly **one** `<style data-tk-com="tag-chip">` block:
241
+
242
+ ```html
243
+ <!-- websrc/index.html — source has 6 <tag-chip> uses -->
244
+ <tag-chip label="TypeScript"></tag-chip>
245
+ <tag-chip label="HTML"></tag-chip>
246
+ <tag-chip label="CSS"></tag-chip>
247
+ <tag-chip label="Bun"></tag-chip>
248
+ <tag-chip label="tkeron"></tag-chip>
249
+ <tag-chip label="zero deps"></tag-chip>
250
+ ```
251
+
252
+ ```html
253
+ <!-- web/index.html — tkeron output -->
254
+ <head>
255
+ <!-- ONE style block, regardless of how many times the component was used -->
256
+ <style data-tk-com="tag-chip">
257
+ .tag-chip { display: inline-block; background: ...; color: white; ... }
258
+ </style>
259
+ </head>
260
+ <body>
261
+ <span class="tag-chip">TypeScript</span>
262
+ <span class="tag-chip">HTML</span>
263
+ <span class="tag-chip">CSS</span>
264
+ <span class="tag-chip">Bun</span>
265
+ <span class="tag-chip">tkeron</span>
266
+ <span class="tag-chip">zero deps</span>
267
+ </body>
268
+ ```
269
+
270
+ #### Global CSS — keep `.css` file, inline via a component
271
+
272
+ For truly shared CSS (resets, tokens, body typography) you can keep a real `.css` file in source (so the editor gives you CSS tooling). Do **not** import it with `<link>`. Inline it through a build-time component — a `.com.ts` is the natural fit because it can read files at build time:
273
+
274
+ ```typescript
275
+ // any-name.com.ts (e.g. global-styles.com.ts, site-css.com.ts — name is free,
276
+ // just respect the custom-element hyphen rule)
277
+ import { join } from "path";
278
+ const css = await Bun.file(join(__dirname, "main.css")).text();
279
+ com.innerHTML = `<style>${css}</style>`;
280
+ ```
281
+
282
+ ```html
283
+ <!-- any page -->
284
+ <head>
285
+ <global-styles></global-styles>
286
+ </head>
287
+ ```
288
+
289
+ The `.css` file can live wherever the user prefers (root of `websrc/`, next to the component, in a `styles/` folder — free choice). What matters is that the page references the **component**, not the `.css` file. Keep the global CSS **small**: it ships on every page. Anything component-specific belongs in that component's `.com.html`.
290
+
291
+ **Build output — `<link>` vs component.** Both source patterns compile, but the output is fundamentally different:
292
+
293
+ ```html
294
+ <!-- ❌ websrc/page.html uses <link rel="stylesheet" href="./styles.css"> -->
295
+ <!-- web/page.html output: -->
296
+ <link rel="stylesheet" href="./page.css" />
297
+ <!-- + a separate page.css file is emitted next to page.html -->
298
+ <!-- = 1 extra HTTP request, blocks first paint -->
299
+ ```
300
+
301
+ ```html
302
+ <!-- ✅ websrc/page.html uses <global-styles></global-styles> -->
303
+ <!-- web/page.html output: -->
304
+ <style>
305
+ /* full content of styles.css inlined — zero extra files, zero extra requests */
306
+ :root { --accent: #38bdf8; }
307
+ body { margin: 0; font-family: system-ui; }
308
+ </style>
309
+ <!-- no .css file emitted alongside page.html -->
233
310
  ```
234
311
 
235
312
  ---
@@ -298,6 +375,20 @@ Tkeron serves the output from a subdirectory. Absolute paths (`/assets/...`) poi
298
375
 
299
376
  Applies to **every `href`, `src` and `url()`** pointing to local assets in `websrc/`.
300
377
 
378
+ ### ❌ `<link rel="stylesheet">` in HTML
379
+
380
+ Generates an extra HTTP request, blocks first paint, and bypasses tkeron's per-component dedup and tree-shaking.
381
+
382
+ ```html
383
+ <!-- ❌ BAD — extra request, no dedup, no tree-shaking -->
384
+ <link rel="stylesheet" href="./styles.css" />
385
+
386
+ <!-- ✅ GOOD — inlined at build time via a component -->
387
+ <global-styles></global-styles>
388
+ ```
389
+
390
+ See "CSS via components" above for the `global-styles.com.ts` pattern.
391
+
301
392
  ### ❌ Event listeners in `.com.ts`
302
393
 
303
394
  ```typescript
@@ -470,9 +561,10 @@ if (isNaN(count) || count < 0 || count > 100) {
470
561
 
471
562
  ### Output size
472
563
 
473
- - CSS classes instead of inline styles
564
+ - CSS classes instead of repeated inline `style="..."`
474
565
  - Lean components (little HTML per component)
475
- - Shared external CSS (`components.css`)
566
+ - CSS lives in `<style>` inside `.com.html` → auto-dedup + tree-shaking
567
+ - Global CSS via `<global-styles>` component (inlined), NEVER `<link rel="stylesheet">`
476
568
  - CSS variables for colors/spacing
477
569
 
478
570
  ---
@@ -487,7 +579,7 @@ When creating/editing a tkeron project:
487
579
  4. ✅ Browser code → `.ts`. Build code → `.com.ts` / `.pre.ts` / `.post.ts`
488
580
  5. ✅ Build-time fetch → always with AbortController + timeout
489
581
  6. ✅ Escape dynamic content (`escapeHtml`)
490
- 7. ✅ CSS classes, not repeated inline styles
582
+ 7. ✅ CSS via components (`<style>` in `.com.html`), never `<link rel="stylesheet">`
491
583
  8. ✅ **Maximum pre-render**: HTML structure in `.html` / components, JS only fills data
492
584
  9. ✅ npm packages only at build time, CDN for the browser
493
585
  10. ✅ `tk build` after every edit, then verify `web/`. NEVER `tk dev` synchronously in agent sessions
@@ -35,13 +35,17 @@ Something is wrong
35
35
 
36
36
  ### `Cannot resolve "/styles.css"`
37
37
 
38
- **Cause**: absolute path. Bun resolves from the source file, not from the server root.
38
+ **Cause**: absolute path in a `<link rel="stylesheet">`. Two problems: the path is absolute (Bun resolves from the source file, not the server root), and `<link rel="stylesheet">` is itself an anti-pattern in tkeron.
39
+
40
+ **Fix**: replace the `<link>` with a `<global-styles>` component that inlines the CSS at build time. See `tkeron-patterns` → "CSS via components".
39
41
 
40
42
  ```html
41
- <!-- ❌ -->
43
+ <!-- ❌ BAD -->
42
44
  <link rel="stylesheet" href="/styles.css" />
43
- <!-- -->
45
+ <!-- ⚠️ Works but anti-pattern (extra request, no dedup) -->
44
46
  <link rel="stylesheet" href="./styles.css" />
47
+ <!-- ✅ GOOD — inlined, no extra request, dedup-friendly -->
48
+ <global-styles></global-styles>
45
49
  ```
46
50
 
47
51
  ### `Component name must contain hyphen`
@@ -1,12 +0,0 @@
1
- <section>
2
- <h2>🎯 Client-side TypeScript</h2>
3
- <p style="margin-bottom: 0.75rem">Interactive features in the browser:</p>
4
- <div class="counter">
5
- <button id="increment">Click me!</button>
6
- <div class="count">Clicks: <span id="count">0</span></div>
7
- </div>
8
- <p style="font-size: 0.85rem; color: #666; margin-top: 0.75rem">
9
- ✓ Event handling & DOM manipulation<br />✓ Regular TypeScript for
10
- interactivity
11
- </p>
12
- </section>
@@ -1,53 +0,0 @@
1
- <div class="hero">
2
- <div>
3
- <img src="./profile.png" alt="tkeron logo" class="logo" />
4
- <h1>tkeron</h1>
5
- <p class="subtitle">Build-time TypeScript for modern web development</p>
6
- </div>
7
- <div class="features">
8
- <ul style="list-style: none">
9
- <li>⚡ Fast builds with Bun</li>
10
- <li>🎯 Build-time HTML generation</li>
11
- <li>🧩 HTML & TypeScript components</li>
12
- <li>🔧 Pure TypeScript & HTML</li>
13
- </ul>
14
- </div>
15
- <div style="margin-top: 1.5rem; display: flex; gap: 0.75rem; width: 100%">
16
- <a
17
- href="https://www.npmjs.com/package/tkeron"
18
- target="_blank"
19
- style="
20
- flex: 1;
21
- padding: 0.65rem;
22
- background: rgba(203, 56, 55, 0.9);
23
- color: white;
24
- text-decoration: none;
25
- border-radius: 6px;
26
- text-align: center;
27
- font-weight: 600;
28
- font-size: 0.85rem;
29
- transition: background 0.2s;
30
- "
31
- >
32
- 📦 npm
33
- </a>
34
- <a
35
- href="https://github.com/tkeron/tkeron"
36
- target="_blank"
37
- style="
38
- flex: 1;
39
- padding: 0.65rem;
40
- background: rgba(36, 41, 46, 0.9);
41
- color: white;
42
- text-decoration: none;
43
- border-radius: 6px;
44
- text-align: center;
45
- font-weight: 600;
46
- font-size: 0.85rem;
47
- transition: background 0.2s;
48
- "
49
- >
50
- ⭐ GitHub
51
- </a>
52
- </div>
53
- </div>
@@ -1,17 +0,0 @@
1
- // TypeScript component - runs at build time with logic
2
- const count = com.getAttribute("count") || "3";
3
- const numCount = parseInt(count, 10);
4
-
5
- // Generate list dynamically
6
- const items = [];
7
- for (let i = 1; i <= numCount; i++) {
8
- items.push(`<li>✓ Item ${i}</li>`);
9
- }
10
-
11
- com.innerHTML = `
12
- <div style="background: #f0fdf4; border-left: 3px solid #22c55e; padding: 0.75rem; border-radius: 4px;">
13
- <ul style="list-style: none; padding: 0; margin: 0; font-size: 0.9rem; color: #166534;">
14
- ${items.join("")}
15
- </ul>
16
- </div>
17
- `;