@madojs/mado 0.5.0 → 0.5.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 (37) hide show
  1. package/AGENTS.md +23 -1
  2. package/CHANGELOG.md +35 -0
  3. package/README.md +23 -2
  4. package/ROADMAP.md +12 -2
  5. package/dist/src/forms.d.ts +4 -3
  6. package/dist/src/forms.js +3 -2
  7. package/dist/src/forms.js.map +1 -1
  8. package/dist/src/router/navigation.js +2 -9
  9. package/dist/src/router/navigation.js.map +1 -1
  10. package/docs/en/05-why-mado.md +1 -1
  11. package/docs/en/06-for-backenders.md +1 -1
  12. package/docs/en/07-llm-pitfalls.md +1 -1
  13. package/docs/en/09-shadow-vs-light-dom.md +60 -0
  14. package/docs/fr/05-why-mado.md +1 -1
  15. package/docs/fr/06-for-backenders.md +1 -1
  16. package/docs/fr/07-llm-pitfalls.md +1 -1
  17. package/docs/fr/09-shadow-vs-light-dom.md +63 -0
  18. package/docs/ru/05-why-mado.md +2 -2
  19. package/docs/ru/06-for-backenders.md +1 -1
  20. package/docs/ru/09-shadow-vs-light-dom.md +60 -0
  21. package/docs/uk/06-for-backenders.md +2 -2
  22. package/docs/uk/09-shadow-vs-light-dom.md +91 -24
  23. package/llms.txt +15 -0
  24. package/package.json +1 -1
  25. package/scripts/bundle.mjs +3 -3
  26. package/scripts/cli.mjs +8 -1
  27. package/server/serve.mjs +19 -7
  28. package/starters/crud/README.md +14 -2
  29. package/starters/crud/package.json +2 -1
  30. package/starters/crud/src/components/app-shell.ts +13 -8
  31. package/starters/crud/src/main.ts +1 -4
  32. package/starters/crud/src/pages/ticket-detail.ts +1 -0
  33. package/starters/crud/src/pages/ticket-new.ts +1 -0
  34. package/starters/crud/src/pages/tickets.ts +1 -0
  35. package/starters/minimal/README.md +4 -2
  36. package/starters/minimal/package.json +2 -1
  37. package/starters/minimal/src/components/app-counter.ts +1 -1
package/AGENTS.md CHANGED
@@ -247,6 +247,28 @@ component("x-child", ({ host }) => {
247
247
  });
248
248
  ```
249
249
 
250
+ ### 13. Component registration imports
251
+
252
+ Custom elements are global after registration, but the browser never imports a
253
+ component file automatically.
254
+
255
+ ```ts
256
+ import "./components/app-shell.js";
257
+
258
+ render(html`<x-app-shell>${router.view}</x-app-shell>`, app);
259
+ ```
260
+
261
+ The import runs `customElements.define("x-app-shell", ...)`. After that,
262
+ `<x-app-shell>` works anywhere in the current document.
263
+
264
+ Rules:
265
+
266
+ - App shell / global providers → import in `main.ts`.
267
+ - Components used only by one page → import in that page.
268
+ - Components shared by a feature → import in the feature entry/page.
269
+ - Tiny leaf components used everywhere → importing in `main.ts` is acceptable.
270
+ - Do **not** bulk-import every component "just in case".
271
+
250
272
  ## SOFT GUIDELINES — recommended, but not critical
251
273
 
252
274
  - **TypeScript strict.** Use `noUncheckedIndexedAccess`-aware code (with `!` or a type guard).
@@ -263,7 +285,7 @@ src/
263
285
  ├── main.ts ← entry: mount to #app
264
286
  ├── pages/ ← one page = one file
265
287
  ├── components/ ← reusable x-* components
266
- ├── layouts/ ← layout for nested routes
288
+ ├── layouts/ ← optional route layout modules (`page({ child })`)
267
289
  └── lib/ ← API client, contexts, pure logic
268
290
  ```
269
291
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.1
4
+
5
+ Patch release focused on first-user DX after the public npm launch.
6
+
7
+ ### Fixed
8
+
9
+ - Generated starter apps can now run `mado dev` / `mado serve` from the app
10
+ root instead of assuming the framework repository layout.
11
+ - The CRUD starter shell is a real Web Component again, using Shadow DOM and
12
+ `<slot>` for route projection.
13
+ - Feature components in the CRUD starter are imported by the pages that render
14
+ them, avoiding a confusing "import everything in main" pattern.
15
+ - The minimal starter counter tag registration now matches the rendered
16
+ `<x-app-counter>` tag.
17
+ - The router default 404 view is rendered through `html` templates, not dynamic
18
+ `innerHTML`.
19
+ - `npm run bundle` defaults to the existing showcase entry instead of the old
20
+ removed `examples/main.ts`.
21
+
22
+ ### Changed
23
+
24
+ - Starter packages include `npm run dev` and generated `.gitignore` defaults.
25
+ - README and docs clarify that Mado works with browser primitives rather than
26
+ replacing the platform.
27
+ - Form documentation now describes `useForm()` as schema-based validation close
28
+ to HTML constraints, matching the implementation.
29
+ - Shadow DOM / Light DOM docs now explain layout components, `<slot>`, and
30
+ component registration imports.
31
+ - Deep imports are documented as internal/unstable before v1.
32
+ - CI workflows use Node24-based GitHub actions and the obsolete Cloudflare
33
+ showcase deploy workflow was removed.
34
+ - The release workflow updates npm before publish and creates GitHub releases
35
+ through the GitHub CLI.
36
+ - Browser regression now runs on a weekly schedule as well as manually.
37
+
3
38
  ## 0.5.0
4
39
 
5
40
  First public release preparation for Mado.
package/README.md CHANGED
@@ -19,6 +19,11 @@
19
19
 
20
20
  > A small native-web SPA framework you can read in an evening.
21
21
 
22
+ [![npm](https://img.shields.io/npm/v/@madojs/mado.svg)](https://www.npmjs.com/package/@madojs/mado)
23
+ [![CI](https://github.com/madojs/mado/actions/workflows/ci.yml/badge.svg)](https://github.com/madojs/mado/actions/workflows/ci.yml)
24
+ [![Browser Regression](https://github.com/madojs/mado/actions/workflows/browser.yml/badge.svg)](https://github.com/madojs/mado/actions/workflows/browser.yml)
25
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
26
+
22
27
  Mado is a thin TypeScript framework on top of the browser platform: Web
23
28
  Components, signals, tagged-template `html`, a router, resources, forms,
24
29
  context, persisted state and static prerender. No runtime dependencies, no
@@ -27,6 +32,12 @@ required bundler, no hidden build pipeline: `tsc → browser`.
27
32
  Mado (`窓`) means “window” in Japanese: a calm native-web window into an app,
28
33
  without dragging a whole frontend factory into the room.
29
34
 
35
+ Mado does not try to replace the browser platform. The browser already gives
36
+ us modules, custom elements, attributes, events, forms, CSS, URLs, history,
37
+ fetch and the DOM. Mado's job is to make those primitives comfortable enough
38
+ for small and serious applications, without taking away your understanding of
39
+ what is happening.
40
+
30
41
  ```txt
31
42
  Runtime budget after build:
32
43
  native ESM graph: ~60 KB raw, ~24 KB gzip across separate modules
@@ -314,8 +325,18 @@ html`
314
325
  `;
315
326
  ```
316
327
 
317
- Validation is based on native HTML constraint validation plus optional custom
318
- `validate(values)`.
328
+ Validation is schema-based and intentionally close to native HTML constraints:
329
+ `required`, `min`, `max`, `pattern`, `type=email/url/number`, plus optional
330
+ custom `validate(values)`.
331
+
332
+ Only the root import is the stable public API:
333
+
334
+ ```ts
335
+ import { html, component, signal } from "@madojs/mado";
336
+ ```
337
+
338
+ Deep imports may exist in the package for tooling and generated files, but they
339
+ are internal and can change before v1.
319
340
 
320
341
  ## Static HTML Without Hydration
321
342
 
package/ROADMAP.md CHANGED
@@ -14,11 +14,20 @@ readable and reliable under real application pressure.
14
14
 
15
15
  ## Before v1
16
16
 
17
+ - Generated app DX: `mado dev`/`serve`/starter scripts should feel as polished
18
+ outside the framework repo as they do inside it.
19
+ - Generated app production story: `mado bundle` and `mado preview` should work
20
+ from a starter app without repository-only assumptions.
17
21
  - Browser compatibility pass across current Chrome, Edge, Firefox and Safari.
18
22
  - Accessibility pass for examples and common component patterns.
19
23
  - Public API audit: names, warnings, lifecycle rules, docs coverage.
20
- - Release hygiene: npm provenance, GitHub repo metadata, tags and changelog.
24
+ - Public exports audit: root import is stable; deep imports must either become
25
+ explicit public subpaths or remain clearly unsupported before v1.
26
+ - Release hygiene: npm provenance / Trusted Publishing, GitHub repo metadata,
27
+ tags and changelog.
21
28
  - Size reporting command or CI summary with ESM and bundled/minified budgets.
29
+ - Public demo site built with Mado: docs, CRUD starter and showcase as a live
30
+ proof instead of another README claim.
22
31
  - Real commercial app test: validate auth, forms, tables, resources, route
23
32
  transitions and long-lived sessions outside toy examples.
24
33
 
@@ -43,8 +52,9 @@ readable and reliable under real application pressure.
43
52
  | 2026-06-03 | v0.3 hardening | Nested template cleanup, stale async route guard, Shadow DOM link/prefetch tests, scroll behavior, `warnOnce`, component reconnect/style tests. |
44
53
  | 2026-06-05 | v0.4 showcase max | `examples/showcase` became a SaaS CRM pressure app with accounts, deals, activity, nested routes, context services and browser regression. |
45
54
  | 2026-06-06 | v0.5 project shape | Unified `mado` CLI, dev server logs, docs language skeleton, examples cleanup (`basic`, `tickets`, `showcase`, `cloudflare`). |
46
- | 2026-06-06 | Mado rebrand | Public package/import name `madojs`, CLI `mado`, brand/docs/examples updated, internal legacy markers cleaned. |
55
+ | 2026-06-06 | Mado rebrand | Public package/import name `@madojs/mado`, CLI `mado`, brand/docs/examples updated, internal legacy markers cleaned. |
47
56
  | 2026-06-06 | Public polish | English public surface, localized docs, translated code comments/examples/templates/GitHub files. |
57
+ | 2026-06-07 | First npm release | Published `@madojs/mado@0.5.0` with the `mado` CLI, minimal/crud starters, CI and release workflow. |
48
58
 
49
59
  ## Future Ideas
50
60
 
@@ -1,5 +1,6 @@
1
1
  /**
2
- * Forms without pain. Built on top of native `<form>` + Constraint Validation API.
2
+ * Forms without pain. Built on native `<form>` submit/input events plus a
3
+ * small schema validator whose rules mirror common HTML constraints.
3
4
  *
4
5
  * const f = useForm({
5
6
  * email: { required: true, type: 'email' },
@@ -20,7 +21,7 @@
20
21
  * `;
21
22
  *
22
23
  * What's inside:
23
- * - validation is performed via standard browser attributes
24
+ * - validation rules mirror common HTML constraints
24
25
  * (required, min, max, pattern, type=email/url/number, etc.);
25
26
  * - custom rules via a validate(values) function returning
26
27
  * { field: 'msg' } or null;
@@ -34,7 +35,7 @@
34
35
  import { type Signal } from "./signal.js";
35
36
  export type FormValues = Record<string, string | number | boolean | undefined>;
36
37
  export type FormErrors = Record<string, string | undefined>;
37
- /** Field declaration. Attributes match HTML5. */
38
+ /** Field declaration. Rules intentionally match common HTML constraints. */
38
39
  export interface FieldSchema {
39
40
  required?: boolean;
40
41
  type?: "text" | "email" | "url" | "number" | "tel" | "password";
package/dist/src/forms.js CHANGED
@@ -1,5 +1,6 @@
1
1
  /**
2
- * Forms without pain. Built on top of native `<form>` + Constraint Validation API.
2
+ * Forms without pain. Built on native `<form>` submit/input events plus a
3
+ * small schema validator whose rules mirror common HTML constraints.
3
4
  *
4
5
  * const f = useForm({
5
6
  * email: { required: true, type: 'email' },
@@ -20,7 +21,7 @@
20
21
  * `;
21
22
  *
22
23
  * What's inside:
23
- * - validation is performed via standard browser attributes
24
+ * - validation rules mirror common HTML constraints
24
25
  * (required, min, max, pattern, type=email/url/number, etc.);
25
26
  * - custom rules via a validate(values) function returning
26
27
  * { field: 'msg' } or null;
@@ -1 +1 @@
1
- {"version":3,"file":"forms.js","sourceRoot":"","sources":["../../src/forms.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"forms.js","sourceRoot":"","sources":["../../src/forms.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,7 @@
6
6
  * so match.ts remains clean and testable without jsdom.
7
7
  */
8
8
  import { signal } from "./../signal.js";
9
+ import { html } from "../html.js";
9
10
  import { compile, matchRoute, } from "./match.js";
10
11
  /**
11
12
  * Minimal History API router.
@@ -166,15 +167,7 @@ function findAnchor(e, selector) {
166
167
  }
167
168
  /** Default 404, if the manifest had no `'*'`. */
168
169
  function defaultFallback() {
169
- return compile("*", () => {
170
- const tpl = document.createElement("template");
171
- tpl.innerHTML = `<pre>404: ${location.pathname}</pre>`;
172
- return {
173
- _mado: true,
174
- strings: Object.assign([""], { raw: [""] }),
175
- values: [tpl.content],
176
- };
177
- });
170
+ return compile("*", () => html `<pre>404: ${location.pathname}</pre>`);
178
171
  }
179
172
  // ---------- navigate() ----------
180
173
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../../src/router/navigation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../../src/router/navigation.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAe,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EACL,OAAO,EACP,UAAU,GAIX,MAAM,YAAY,CAAC;AA+BpB;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,MAAM,CACpB,MAAc,EACd,UAAyB,EAAE;IAE3B,MAAM,kBAAkB,GAAG,OAAO,CAAC,eAAe,KAAK,KAAK,CAAC;IAC7D,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACvE,MAAM,QAAQ,GACZ,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,GAAG,CAAC,IAAI,eAAe,EAAE,CAAC;IAE/D,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAsB,EAAE,CAAC;IACvC,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,cAAc;IACd,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,IAAI,QAAQ;YAAE,OAAO;QACrB,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC,CAAC;IACF,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC3C,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC;IAEnE,gDAAgD;IAChD,MAAM,OAAO,GAAG,CAAC,CAAQ,EAAE,EAAE;QAC3B,IAAI,QAAQ;YAAE,OAAO;QACrB,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;QACxC,IAAI,CAAC,CAAC;YAAE,OAAO;QACf,IAAK,CAAgB,CAAC,gBAAgB;YAAE,OAAO;QAC/C,IAAK,CAAgB,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC3C,MAAM,EAAE,GAAG,CAAe,CAAC;QAC3B,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,CAAC,QAAQ,IAAI,EAAE,CAAC,MAAM;YAAE,OAAO;QACjE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;YAAE,OAAO;QAC3C,CAAC,CAAC,cAAc,EAAE,CAAC;QACnB,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACrD,CAAC,CAAC;IACF,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC5C,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IAEpE,sEAAsE;IACtE,IAAI,aAAa,GAAyC,IAAI,CAAC;IAC/D,MAAM,WAAW,GAAG,CAAC,CAAQ,EAAE,EAAE;QAC/B,IAAI,QAAQ;YAAE,OAAO;QACrB,IAAI,CAAC,OAAO,CAAC,QAAQ;YAAE,OAAO;QAC9B,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,EAAE,sCAAsC,CAAC,CAAC;QAChE,IAAI,CAAC,CAAC;YAAE,OAAO;QACf,IAAI,aAAa;YAAE,YAAY,CAAC,aAAa,CAAC,CAAC;QAC/C,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;YAC9B,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;gBAC3C,IAAI,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;oBAAE,OAAO;gBAC3C,OAAO,CAAC,QAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAClC,CAAC;YAAC,MAAM,CAAC;gBACP,UAAU;YACZ,CAAC;QACH,CAAC,EAAE,EAAE,CAAC,CAAC;IACT,CAAC,CAAC;IACF,MAAM,UAAU,GAAG,GAAG,EAAE;QACtB,IAAI,aAAa,EAAE,CAAC;YAClB,YAAY,CAAC,aAAa,CAAC,CAAC;YAC5B,aAAa,GAAG,IAAI,CAAC;QACvB,CAAC;IACH,CAAC,CAAC;IACF,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACpD,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAClD,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE;QACjB,QAAQ,CAAC,mBAAmB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QACvD,QAAQ,CAAC,mBAAmB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QACrD,IAAI,aAAa;YAAE,YAAY,CAAC,aAAa,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,MAAM,GAAG,GAAc;QACrB,IAAI,EAAE,GAAG,EAAE;YACT,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;YACjB,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;YAClC,IAAI,CAAC;gBAAE,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YACxC,OAAO,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC9B,CAAC;QACD,IAAI;QACJ,QAAQ,CAAC,EAAE,EAAE,IAAI;YACf,MAAM,KAAK,GAAG,GAAG,EAAE;gBACjB,IAAI,IAAI,EAAE,OAAO;oBAAE,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;;oBACjD,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;gBACrC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBAC5B,WAAW,EAAE,CAAC;YAChB,CAAC,CAAC;YACF,wDAAwD;YACxD,+DAA+D;YAC/D,4CAA4C;YAC5C,MAAM,GAAG,GAAG,QAEX,CAAC;YACF,IAAI,kBAAkB,IAAI,OAAO,GAAG,CAAC,mBAAmB,KAAK,UAAU,EAAE,CAAC;gBACxE,GAAG,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;YACjC,CAAC;iBAAM,CAAC;gBACN,KAAK,EAAE,CAAC;YACV,CAAC;QACH,CAAC;QACD,OAAO;YACL,IAAI,QAAQ;gBAAE,OAAO;YACrB,QAAQ,GAAG,IAAI,CAAC;YAChB,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnC,IAAI,CAAC;oBACH,CAAC,EAAE,CAAC;gBACN,CAAC;gBAAC,MAAM,CAAC;oBACP,UAAU;gBACZ,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAC;IAEF,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,UAAU,CAAC,CAAQ,EAAE,QAAgB;IAC5C,MAAM,WAAW,GAAG,CAAC,IAAa,EAA4B,EAAE;QAC9D,IAAI,CAAC,CAAC,IAAI,YAAY,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACrE,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAC9C,OAAO,KAA0B,CAAC;IACpC,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,IAAI,OAAO,CAAC,CAAC,YAAY,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IACtD,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC1B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iDAAiD;AACjD,SAAS,eAAe;IACtB,OAAO,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAA,aAAa,QAAQ,CAAC,QAAQ,QAAQ,CAAC,CAAC;AACxE,CAAC;AAED,mCAAmC;AAEnC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAU,EAAE,IAA4B;IAC/D,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO;IAC5E,IAAI,IAAI,EAAE,OAAO;QAAE,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;;QACjD,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IACrC,WAAW,EAAE,CAAC;IACd,sEAAsE;IACtE,wEAAwE;IACxE,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,WAAW;IAClB,MAAM,WAAW,GAAG,MAEnB,CAAC;IACF,IAAI,OAAO,WAAW,CAAC,QAAQ,KAAK,UAAU;QAAE,OAAO;IACvD,IAAI,CAAC;QACH,WAAW,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,UAAU;IACZ,CAAC;AACH,CAAC;AAED,mCAAmC;AACnC,EAAE;AACF,2EAA2E;AAC3E,uEAAuE;AACvE,oEAAoE;AACpE,mEAAmE;AAEnE,IAAI,QAAQ,GAA0B,IAAI,CAAC;AAE3C,SAAS,cAAc;IACrB,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACnC,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,QAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1E,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,SAAS,CAAC,IAAqB,EAAE,IAAa;IACrD,MAAM,GAAG,GAAG,GAAG,QAAQ,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,CAC3E,KAAK,EACL,EAAE,CACH,CAAC;IACF,IAAI,IAAI;QAAE,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;;QACtC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;IACzC,cAAc,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AACxC,CAAC;AAOD;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,YAAY,GAAG,EAAE;IACxD,MAAM,GAAG,GAAG,cAAc,EAAE,CAAC;IAC7B,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE;QACjB,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;QAC3C,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC;IAC1C,CAAC,CAAe,CAAC;IAEjB,IAAI,CAAC,GAAG,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACzB,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACpD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE;YAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;;YACnD,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC7B,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,IAAI,KAAK,CAAC,CAAC;IACzC,CAAC,CAAC;IAEF,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -33,7 +33,7 @@ If your case does not fall into the last point — Mado is most likely not the b
33
33
  | Reactivity | `@property` decorators + manual `requestUpdate` | signals (`signal`/`computed`/`effect`) out of the box |
34
34
  | Router | none, you need to find one (`@lit-labs/router`, etc) | included: `routes()` + nested + prefetch + sync-cache |
35
35
  | Data fetching | none, you need to assemble it | `resource()` + `mutation()` + glob invalidation |
36
- | Forms | none | `useForm()` on native HTML5 validation |
36
+ | Forms | none | `useForm()` with HTML-like constraints |
37
37
  | SEO / static | complex (`@lit-labs/ssr`) | `bake` (linkedom) + edge-prerender |
38
38
  | Build | needs esbuild/rollup/webpack | `tsc` is enough |
39
39
  | Code style | classes + decorators | functions + tagged templates |
@@ -188,7 +188,7 @@ We register the `<x-counter>` tag in the browser — it becomes a "function" tha
188
188
 
189
189
  ## Forms — like `form.Validate()` on the backend
190
190
 
191
- Mado uses **native HTML5 validation**, plus adds state tracking.
191
+ Mado uses **schema-based validation close to native HTML constraints**, plus adds state tracking.
192
192
 
193
193
  ```ts
194
194
  import { useForm } from "@madojs/mado";
@@ -256,7 +256,7 @@ This means:
256
256
  // ❌ No such API
257
257
  const f = useForm({ resolver: zodResolver(schema) });
258
258
 
259
- // ✅ Correct: HTML5 validation via attributes
259
+ // ✅ Correct: HTML-like validation through useForm schema
260
260
  const f = useForm({
261
261
  email: { required: true, type: "email" },
262
262
  age: { required: true, type: "number", min: 18 },
@@ -6,6 +6,19 @@ an application.
6
6
 
7
7
  ## Rule of Thumb
8
8
 
9
+ In Mado, layouts are components too. If a file represents a visible reusable
10
+ part of the app tree — app shell, sidebar, modal, table, page section — prefer a
11
+ Web Component registered with `component()`.
12
+
13
+ Use plain functions only for small inline template helpers:
14
+
15
+ ```ts
16
+ const money = (value: number) => html`<span>${formatMoney(value)}</span>`;
17
+ ```
18
+
19
+ Do not use functions for app shells in public examples. They work, but they
20
+ hide the browser model instead of teaching it.
21
+
9
22
  Use **Shadow DOM** for leaf widgets:
10
23
 
11
24
  - buttons, badges, cards, metrics;
@@ -22,6 +35,15 @@ global CSS utilities:
22
35
  - components that intentionally share global layout, form and table utilities;
23
36
  - places where children should simply remain normal document DOM.
24
37
 
38
+ Use **Shadow DOM** for slot-based layouts:
39
+
40
+ - app shells that render `<slot>`;
41
+ - sidebar/content wrappers;
42
+ - reusable layout frames that own their own grid/header/sidebar CSS.
43
+
44
+ `<slot>` is a Shadow DOM feature. In a `shadow: false` component, `<slot>` is
45
+ just a normal element and does not move children into that position.
46
+
25
47
  ## The Footgun
26
48
 
27
49
  Global CSS does not cross a Shadow DOM boundary.
@@ -90,6 +112,18 @@ component("x-toast-stack", setup);
90
112
  This gives backend-admin screens predictable CSS while preserving encapsulation
91
113
  for reusable widgets and slot-based shells.
92
114
 
115
+ The import model is deliberately browser-native:
116
+
117
+ ```ts
118
+ import "./components/app-layout.js";
119
+
120
+ render(html`<x-app-layout>${router.view}</x-app-layout>`, app);
121
+ ```
122
+
123
+ The import registers the custom element with `customElements.define()`. The
124
+ template creates an `<x-app-layout>` element. The browser connects the two.
125
+ There is no React-style component value being passed around.
126
+
93
127
  If a layout does not need slot projection and should be styled entirely by
94
128
  global CSS, `shadow: false` can still be a good choice. If it contains
95
129
  `<slot>`, keep Shadow DOM and put the shell styles in that component.
@@ -107,6 +141,32 @@ component("x-card-link", () => () => html`
107
141
 
108
142
  The link can be in Shadow DOM; navigation still stays SPA.
109
143
 
144
+ ## Where To Import Components
145
+
146
+ Custom elements are global after registration, but registration is still an
147
+ explicit JavaScript import.
148
+
149
+ ```ts
150
+ // main.ts: global app frame
151
+ import "./components/app-shell.js";
152
+
153
+ // pages/tickets.ts: page-owned feature component
154
+ import "../components/ticket-list.js";
155
+ ```
156
+
157
+ The browser does **not** download `ticket-list.js` just because it sees
158
+ `<ticket-list>`. The file must be imported somewhere first. Once imported, it
159
+ calls `customElements.define(...)`, and the tag becomes known in the current
160
+ document.
161
+
162
+ Do not bulk-import every component in `main.ts` "just in case". It works for
163
+ tiny demos, but it hides ownership and defeats lazy route loading. Prefer:
164
+
165
+ - global app shell/providers in `main.ts`;
166
+ - page-owned components in that page file;
167
+ - feature-owned shared components in the feature entry page;
168
+ - truly global leaf components in `main.ts` only when they are used everywhere.
169
+
110
170
  ## Showcase Lesson
111
171
 
112
172
  `examples/showcase` uses this split deliberately:
@@ -33,7 +33,7 @@ Si votre cas ne tombe pas dans le dernier point — Mado n'est probablement pas
33
33
  | Réactivité | décorateurs `@property` + `requestUpdate` manuel | signals (`signal`/`computed`/`effect`) intégrés |
34
34
  | Router | aucun, vous devez en trouver un (`@lit-labs/router`, etc.) | inclus : `routes()` + nested + prefetch + sync-cache |
35
35
  | Chargement de données | aucun, vous devez l'assembler | `resource()` + `mutation()` + invalidation glob |
36
- | Forms | aucun | `useForm()` sur validation HTML5 native |
36
+ | Forms | aucun | `useForm()` avec contraintes proches du HTML |
37
37
  | SEO / statique | complexe (`@lit-labs/ssr`) | `bake` (linkedom) + edge-prerender |
38
38
  | Build | nécessite esbuild/rollup/webpack | `tsc` suffit |
39
39
  | Style de code | classes + décorateurs | fonctions + tagged templates |
@@ -195,7 +195,7 @@ Mado ne fait que le coller avec les signals.
195
195
 
196
196
  ## Forms — comme `form.Validate()` côté backend
197
197
 
198
- Mado utilise la **validation HTML5 native**, plus ajoute le suivi d'état.
198
+ Mado utilise une **validation par schéma proche des contraintes HTML natives**, plus le suivi d'état.
199
199
 
200
200
  ```ts
201
201
  import { useForm } from "@madojs/mado";
@@ -256,7 +256,7 @@ Cela signifie :
256
256
  // ❌ Pas une telle API
257
257
  const f = useForm({ resolver: zodResolver(schema) });
258
258
 
259
- // ✅ Correct : validation HTML5 via attributs
259
+ // ✅ Correct : validation proche du HTML via le schéma useForm
260
260
  const f = useForm({
261
261
  email: { required: true, type: "email" },
262
262
  age: { required: true, type: "number", min: 18 },
@@ -5,6 +5,19 @@ autonomes, mais ce n'est pas le bon défaut pour chaque composant dans une appli
5
5
 
6
6
  ## Règle générale
7
7
 
8
+ Dans Mado, un layout est aussi un composant. Si un fichier décrit une partie
9
+ visible et réutilisable de l'arbre UI — app shell, sidebar, modal, table,
10
+ section de page — préférez un Web Component déclaré avec `component()`.
11
+
12
+ Gardez les fonctions simples pour de petits helpers inline :
13
+
14
+ ```ts
15
+ const money = (value: number) => html`<span>${formatMoney(value)}</span>`;
16
+ ```
17
+
18
+ Ne faites pas d'app shell sous forme de fonction dans les exemples publics. Cela
19
+ fonctionne, mais cela cache le modèle du navigateur au lieu de l'enseigner.
20
+
8
21
  Utilisez **Shadow DOM** pour les widgets feuilles :
9
22
 
10
23
  - boutons, badges, cartes, métriques ;
@@ -22,6 +35,16 @@ les utilitaires CSS globaux :
22
35
  tableau ;
23
36
  - endroits où les enfants doivent simplement rester dans le DOM normal du document.
24
37
 
38
+ Utilisez **Shadow DOM** pour les layouts basés sur des slots :
39
+
40
+ - app shells qui rendent `<slot>` ;
41
+ - wrappers sidebar/contenu ;
42
+ - frames de layout réutilisables qui possèdent leur propre CSS grid/header/sidebar.
43
+
44
+ `<slot>` est une fonctionnalité Shadow DOM. Dans un composant `shadow: false`,
45
+ `<slot>` est juste un élément DOM normal et ne déplace pas les enfants à cet
46
+ endroit du layout.
47
+
25
48
  ## Le piège
26
49
 
27
50
  Le CSS global ne franchit pas une frontière Shadow DOM.
@@ -89,6 +112,18 @@ component("x-toast-stack", setup);
89
112
  Cela donne aux écrans d'admin backend un CSS prévisible tout en préservant l'encapsulation
90
113
  pour les widgets réutilisables et les shells basés sur slot.
91
114
 
115
+ Le modèle d'import est volontairement natif au navigateur :
116
+
117
+ ```ts
118
+ import "./components/app-layout.js";
119
+
120
+ render(html`<x-app-layout>${router.view}</x-app-layout>`, app);
121
+ ```
122
+
123
+ L'import enregistre le custom element avec `customElements.define()`. Le template
124
+ crée un élément `<x-app-layout>`. Le navigateur relie les deux. Il n'y a pas de
125
+ valeur de composant à la React que l'on passe comme fonction.
126
+
92
127
  Si un layout n'a pas besoin de projection slot et doit être entièrement stylé par du CSS
93
128
  global, `shadow: false` peut rester un bon choix. S'il contient `<slot>`, gardez Shadow DOM
94
129
  et mettez les styles du shell dans `styles: css\`\``.
@@ -107,6 +142,34 @@ component("x-card-link", () => () => html`
107
142
 
108
143
  Le lien peut être en Shadow DOM ; la navigation reste SPA.
109
144
 
145
+ ## Où importer les composants
146
+
147
+ Les custom elements sont globaux après leur enregistrement, mais cet
148
+ enregistrement reste un import JavaScript explicite.
149
+
150
+ ```ts
151
+ // main.ts : frame global de l'app
152
+ import "./components/app-shell.js";
153
+
154
+ // pages/tickets.ts : composant possédé par cette page
155
+ import "../components/ticket-list.js";
156
+ ```
157
+
158
+ Le navigateur ne télécharge **pas** `ticket-list.js` simplement parce qu'il voit
159
+ `<ticket-list>`. Le fichier doit d'abord être importé quelque part. Une fois
160
+ importé, il appelle `customElements.define(...)`, et le tag devient connu dans
161
+ le document courant.
162
+
163
+ N'importez pas tous les composants en masse dans `main.ts` "au cas où". Cela
164
+ fonctionne pour de petites démos, mais cache l'ownership et casse le chargement
165
+ paresseux des routes. Préférez :
166
+
167
+ - app shell/providers globaux dans `main.ts` ;
168
+ - composants utilisés par une seule page dans ce fichier page ;
169
+ - composants partagés d'une feature dans la page d'entrée de cette feature ;
170
+ - petits leaf components vraiment globaux dans `main.ts` seulement s'ils sont
171
+ utilisés partout.
172
+
110
173
  ## Leçon du Showcase
111
174
 
112
175
  `examples/showcase` utilise cette séparation délibérément :
@@ -33,7 +33,7 @@ Mado — не «убийца» React/Vue/Svelte. Это узкоспециали
33
33
  | Реактивность | декораторы `@property` + ручной `requestUpdate` | сигналы (`signal`/`computed`/`effect`) из коробки |
34
34
  | Роутер | нет, нужно искать (`@lit-labs/router`, etc) | в комплекте: `routes()` + nested + prefetch + sync-cache |
35
35
  | Data fetching | нет, нужно собирать | `resource()` + `mutation()` + glob-инвалидация |
36
- | Формы | нет | `useForm()` на нативной HTML5-валидации |
36
+ | Формы | нет | `useForm()` с HTML-like constraints |
37
37
  | SEO / static | сложно (`@lit-labs/ssr`) | `bake` (linkedom) + edge-prerender |
38
38
  | Билд | нужен esbuild/rollup/webpack | хватает `tsc` |
39
39
  | Стиль кода | классы + декораторы | функции + tagged templates |
@@ -190,4 +190,4 @@ Mado — это **узкий** инструмент с честным позиц
190
190
 
191
191
  Если хоть один пункт не про вас — берите альтернативу из таблицы выше. Не миритесь с инструментом, который не подходит.
192
192
 
193
- — Автор Mado, бывший React-разработчик, перешедший на бекенд и теперь склеивающий фронт в свободное время.
193
+ — Автор Mado, бывший React-разработчик, перешедший на бекенд и теперь склеивающий фронт в свободное время.
@@ -188,7 +188,7 @@ html`<x-counter></x-counter>`
188
188
 
189
189
  ## Формы — как `form.Validate()` на бекенде
190
190
 
191
- Mado использует **нативную HTML5-валидацию**, plus добавляет state-tracking.
191
+ Mado использует **schema-based validation в духе HTML constraints**, плюс добавляет state-tracking.
192
192
 
193
193
  ```ts
194
194
  import { useForm } from "@madojs/mado";
@@ -6,6 +6,19 @@ an application.
6
6
 
7
7
  ## Rule of Thumb
8
8
 
9
+ В Mado layout — это тоже component. Если файл описывает видимую переиспользуемую
10
+ часть UI-дерева — app shell, sidebar, modal, table, page section — по умолчанию
11
+ делайте Web Component через `component()`.
12
+
13
+ Обычные функции оставляйте для маленьких inline helpers:
14
+
15
+ ```ts
16
+ const money = (value: number) => html`<span>${formatMoney(value)}</span>`;
17
+ ```
18
+
19
+ Не стоит делать app shell функцией в публичных примерах. Это работает, но
20
+ прячет browser model вместо того, чтобы ее объяснять.
21
+
9
22
  Use **Shadow DOM** for leaf widgets:
10
23
 
11
24
  - buttons, badges, cards, metrics;
@@ -22,6 +35,15 @@ global CSS utilities:
22
35
  - components that intentionally share global layout, form and table utilities;
23
36
  - places where children should simply remain normal document DOM.
24
37
 
38
+ Use **Shadow DOM** для slot-based layouts:
39
+
40
+ - app shells с `<slot>`;
41
+ - sidebar/content wrappers;
42
+ - reusable layout frames, которые владеют своим grid/header/sidebar CSS.
43
+
44
+ `<slot>` — это feature Shadow DOM. В компоненте с `shadow: false` тег `<slot>`
45
+ становится обычным DOM-элементом и не переносит children в это место layout.
46
+
25
47
  ## The Footgun
26
48
 
27
49
  Global CSS does not cross a Shadow DOM boundary.
@@ -90,6 +112,18 @@ component("x-toast-stack", setup);
90
112
  This gives backend-admin screens predictable CSS while preserving encapsulation
91
113
  for reusable widgets and slot-based shells.
92
114
 
115
+ Import model специально browser-native:
116
+
117
+ ```ts
118
+ import "./components/app-layout.js";
119
+
120
+ render(html`<x-app-layout>${router.view}</x-app-layout>`, app);
121
+ ```
122
+
123
+ Import регистрирует custom element через `customElements.define()`. Template
124
+ создает `<x-app-layout>` element. Дальше браузер сам связывает тег с классом
125
+ компонента. Тут нет React-style component value, который передается как функция.
126
+
93
127
  If a layout does not need slot projection and should be styled entirely by
94
128
  global CSS, `shadow: false` can still be a good choice. If it contains
95
129
  `<slot>`, keep Shadow DOM and put the shell styles in that component.
@@ -107,6 +141,32 @@ component("x-card-link", () => () => html`
107
141
 
108
142
  The link can be in Shadow DOM; navigation still stays SPA.
109
143
 
144
+ ## Где импортировать компоненты
145
+
146
+ Custom elements становятся глобальными после регистрации, но регистрация все
147
+ равно остается явным JavaScript import.
148
+
149
+ ```ts
150
+ // main.ts: global app frame
151
+ import "./components/app-shell.js";
152
+
153
+ // pages/tickets.ts: component, которым владеет эта page
154
+ import "../components/ticket-list.js";
155
+ ```
156
+
157
+ Браузер **не** скачивает `ticket-list.js` только потому, что увидел
158
+ `<ticket-list>`. Файл должен быть где-то импортирован. После import он вызывает
159
+ `customElements.define(...)`, и тег становится известен текущему document.
160
+
161
+ Не стоит bulk-import всех компонентов в `main.ts` "just in case". Для маленьких
162
+ demo это работает, но прячет ownership и ломает lazy route loading. Лучше:
163
+
164
+ - global app shell/providers импортировать в `main.ts`;
165
+ - components, которыми владеет одна page, импортировать в этой page;
166
+ - shared components feature-а импортировать в feature entry page;
167
+ - truly global leaf components импортировать в `main.ts` только если они реально
168
+ используются везде.
169
+
110
170
  ## Showcase Lesson
111
171
 
112
172
  `examples/showcase` uses this split deliberately:
@@ -34,8 +34,8 @@ const form = useForm({
34
34
  });
35
35
  ```
36
36
 
37
- Mado спирається на Constraint Validation API браузера, а не на окрему validation
38
- екосистему.
37
+ Mado використовує schema-based validation, близьку до HTML constraints, а не
38
+ окрему validation ecosystem.
39
39
 
40
40
  ## Auth
41
41
 
@@ -1,40 +1,107 @@
1
1
  # Shadow DOM vs Light DOM
2
2
 
3
- Mado використовує Shadow DOM за замовчуванням, але це не означає, що його треба
4
- вмикати всюди.
3
+ Mado використовує Shadow DOM за замовчуванням. Це хороший default для
4
+ самодостатніх widgets, але не для кожного компонента в app.
5
5
 
6
- ## Shadow DOM добре підходить для
6
+ ## Практичне правило
7
+
8
+ У Mado layout — це теж component. Якщо файл описує видиму reusable частину
9
+ UI-дерева — app shell, sidebar, modal, table, page section — за замовчуванням
10
+ робіть Web Component через `component()`.
7
11
 
8
- - leaf-компонентів;
9
- - isolated widgets;
10
- - badges, cards, modals, buttons;
11
- - компонентів, які мають не ламатися від зовнішнього CSS.
12
+ Звичайні функції залишайте для маленьких inline helpers:
12
13
 
13
14
  ```ts
14
- component("x-badge", () => () => html`<span><slot></slot></span>`, {
15
- styles: css`:host { display: inline-block; }`,
16
- });
15
+ const money = (value: number) => html`<span>${formatMoney(value)}</span>`;
17
16
  ```
18
17
 
19
- ## Light DOM краще для
18
+ Не варто робити app shell функцією в public examples. Це працює, але ховає
19
+ browser model замість того, щоб її пояснювати.
20
+
21
+ Використовуйте **Shadow DOM** для leaf widgets:
22
+
23
+ - buttons, badges, cards, metrics;
24
+ - modals, toasts, small visual components;
25
+ - embedded widgets, які не мають випадково успадковувати app CSS;
26
+ - components, чиї styles належать самому component.
27
+
28
+ Використовуйте **Light DOM** (`{ shadow: false }`) для app structure, якій
29
+ потрібні global CSS utilities:
30
+
31
+ - route/page components;
32
+ - admin screens з dense table/form layouts;
33
+ - data-heavy screens з tables and forms;
34
+ - місця, де children мають залишатися normal document DOM.
35
+
36
+ Використовуйте **Shadow DOM** для slot-based layouts:
20
37
 
21
- - app shell;
22
- - admin layouts;
23
- - route-level pages;
24
- - компонентів, які мають користуватися глобальними utility classes;
25
- - великих CRUD surfaces, де CSS має бути простим і передбачуваним.
38
+ - app shells, які render `<slot>`;
39
+ - sidebar/content wrappers;
40
+ - reusable layout frames, які володіють своїм grid/header/sidebar CSS.
41
+
42
+ `<slot>` це feature Shadow DOM. У component з `shadow: false` тег `<slot>` є
43
+ звичайним DOM element і не переносить children у це місце layout.
44
+
45
+ ## Як працює import
26
46
 
27
47
  ```ts
28
- component("x-app-layout", () => () => html`<main><slot></slot></main>`, {
29
- shadow: false,
30
- styles: css`x-app-layout main { display: grid; }`,
31
- });
48
+ import "./components/app-layout.js";
49
+
50
+ render(html`<x-app-layout>${router.view}</x-app-layout>`, app);
32
51
  ```
33
52
 
34
- ## Практичне правило
53
+ Import реєструє custom element через `customElements.define()`. Template створює
54
+ `<x-app-layout>` element. Далі browser сам з'єднує tag з component class. Це не
55
+ React-style component value, який передається як function.
35
56
 
36
- Якщо компонент самостійний reusable visual, бери Shadow DOM. Якщо це частина
37
- app layout або page composition, часто краще Light DOM.
57
+ ## Routing and links
38
58
 
39
- Посилання з `data-link` працюють і в Shadow DOM, бо router використовує
59
+ `data-link` працює всередині Shadow DOM, бо router використовує
40
60
  `event.composedPath()`.
61
+
62
+ ```ts
63
+ component("x-card-link", () => () => html`
64
+ <a href="/app/accounts" data-link>Accounts</a>
65
+ `);
66
+ ```
67
+
68
+ Link може бути в Shadow DOM; navigation все одно залишається SPA.
69
+
70
+ ## Де імпортувати компоненти
71
+
72
+ Custom elements стають global після registration, але registration все одно є
73
+ явним JavaScript import.
74
+
75
+ ```ts
76
+ // main.ts: global app frame
77
+ import "./components/app-shell.js";
78
+
79
+ // pages/tickets.ts: component, яким володіє ця page
80
+ import "../components/ticket-list.js";
81
+ ```
82
+
83
+ Browser **не** завантажує `ticket-list.js` лише тому, що побачив
84
+ `<ticket-list>`. File має бути imported десь першим. Після import він викликає
85
+ `customElements.define(...)`, і tag стає відомим у поточному document.
86
+
87
+ Не робіть bulk-import усіх components у `main.ts` "just in case". Це працює в
88
+ tiny demos, але ховає ownership і ламає lazy route loading. Краще:
89
+
90
+ - global app shell/providers імпортувати в `main.ts`;
91
+ - components, якими володіє одна page, імпортувати в цій page;
92
+ - shared feature components імпортувати у feature entry page;
93
+ - truly global leaf components імпортувати в `main.ts` лише якщо вони реально
94
+ використовуються всюди.
95
+
96
+ ## Showcase lesson
97
+
98
+ `examples/showcase` використовує цей split навмисно:
99
+
100
+ - `x-app` і CRM route pages — Light DOM;
101
+ - `x-app-layout` — Shadow DOM, бо він володіє slot-based sidebar/content shell;
102
+ - table/form/page utilities живуть у `styles/global.ts`;
103
+ - leaf components на кшталт `x-stat-card`, `x-status-badge`, `x-modal`,
104
+ `x-toast-stack` залишають Shadow DOM.
105
+
106
+ Якщо page раптом виглядає unstyled, перевірте, чи не використовує вона global
107
+ classes всередині Shadow DOM component. Зазвичай проблема саме в цьому.
package/llms.txt CHANGED
@@ -11,6 +11,7 @@ Mado is a narrowly-focused frontend framework that deliberately avoids React pat
11
11
  - **All templates are tagged template `html\`...\``** (not JSX). Allowed bindings inside: `${value}`, `attr=${v}`, `@evt=${fn}`, `.prop=${v}`, `?attr=${flag}`.
12
12
  - **Reactivity via signals.** `signal()`, `computed()`, `effect()`. A signal is a getter function (`count()`), not an object field (`count.value`).
13
13
  - **Components are Web Components.** Registered via `component('x-name', setupFn, options)`. Names must include a hyphen (`x-foo`, `my-button`).
14
+ - **Component files register tags as side effects.** The browser does not auto-import files by tag name. If `<x-card>` works, some imported module already ran `customElements.define("x-card", ...)`.
14
15
  - **Cleanup via `ctx.onDispose(fn)`** in setup, not via return from effect.
15
16
 
16
17
  ## Critical template rules
@@ -94,6 +95,20 @@ export default page({
94
95
  });
95
96
  ```
96
97
 
98
+ ## Component registration imports
99
+
100
+ ```ts
101
+ // main.ts: global shell/provider components only
102
+ import "./components/app-shell.js";
103
+
104
+ // pages/tickets.ts: feature/page-owned components
105
+ import "../components/ticket-list.js";
106
+ ```
107
+
108
+ Do not bulk-import every component in `main.ts` "just in case". Import the
109
+ registration near the route/page that owns the tag. This preserves lazy loading
110
+ and makes the code readable to humans and LLMs.
111
+
97
112
  ## Canonical CRUD pattern (for backend developers)
98
113
 
99
114
  ```ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madojs/mado",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Mado — a small native-web SPA framework with Web Components, signals, tagged-template html, router, resources, and forms. TypeScript-only build, zero runtime dependencies.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -2,7 +2,7 @@
2
2
  //
3
3
  // Usage:
4
4
  // node scripts/bundle.mjs # → out/<hash>.js + chunks + out/index.html
5
- // ENTRY=examples/main.ts node scripts/bundle.mjs
5
+ // ENTRY=src/index.ts HTML=examples/index.html node scripts/bundle.mjs
6
6
  //
7
7
  // What it does:
8
8
  // 1. Bundles entry with code splitting (each dynamic import → a chunk).
@@ -26,9 +26,9 @@ import { gzipSync, brotliCompressSync, constants as zlibConst } from "node:zlib"
26
26
  import { join, basename } from "node:path";
27
27
  import { existsSync } from "node:fs";
28
28
 
29
- const ENTRY = process.env.ENTRY ?? "examples/main.ts";
29
+ const ENTRY = process.env.ENTRY ?? "examples/showcase/main.ts";
30
30
  const OUT_DIR = process.env.OUT_DIR ?? "out";
31
- const HTML = process.env.HTML ?? "examples/index.html";
31
+ const HTML = process.env.HTML ?? "examples/showcase/index.html";
32
32
 
33
33
  await mkdir(OUT_DIR, { recursive: true });
34
34
 
package/scripts/cli.mjs CHANGED
@@ -40,7 +40,7 @@ switch (command) {
40
40
  await runNodeScript("scripts/showcase-regression.mjs", args.slice(1));
41
41
  } else {
42
42
  const files = await listTestFiles();
43
- await run(process.execPath, ["--test", "--test-timeout=10000", ...files, ...args]);
43
+ await run(process.execPath, ["--test", "--test-timeout=20000", ...files, ...args]);
44
44
  }
45
45
  break;
46
46
  case "serve":
@@ -120,6 +120,7 @@ async function runInit(rawArgs) {
120
120
  await mkdir(target, { recursive: true });
121
121
  await cp(source, target, { recursive: true, force: true });
122
122
  await copyCanonicalAgentFiles(target);
123
+ await ensureStarterGitignore(target);
123
124
 
124
125
  const packageName = packageNameFromDir(target);
125
126
  if (!isValidPackageName(packageName)) {
@@ -204,6 +205,12 @@ async function copyCanonicalAgentFiles(target) {
204
205
  }
205
206
  }
206
207
 
208
+ async function ensureStarterGitignore(target) {
209
+ const file = join(target, ".gitignore");
210
+ if (existsSync(file)) return;
211
+ await writeFile(file, "node_modules\ndist\nout\n.DS_Store\n*.log\n");
212
+ }
213
+
207
214
  async function runNodeBin(bin, args) {
208
215
  await run(process.execPath, [resolveBin(bin), ...args]);
209
216
  }
package/server/serve.mjs CHANGED
@@ -6,7 +6,8 @@
6
6
  // node server/serve.mjs basic # mount examples/basic/ at /
7
7
  // EXAMPLE=showcase node server/serve.mjs # mount examples/showcase/ at /
8
8
  //
9
- // Without EXAMPLE, / serves examples/index.html (example index).
9
+ // Without EXAMPLE, / serves examples/index.html when running inside the Mado
10
+ // repository, or ./index.html when running inside a generated app.
10
11
  // With EXAMPLE, all extensionless and /index.html requests fall back to
11
12
  // examples/<EXAMPLE>/index.html so the client router works from root, just
12
13
  // like a production SPA deploy.
@@ -26,6 +27,9 @@ const EXAMPLE_DIR = EXAMPLE
26
27
  ? resolve(join(ROOT, "examples", EXAMPLE))
27
28
  : "";
28
29
  const EXAMPLE_INDEX = EXAMPLE ? join(EXAMPLE_DIR, "index.html") : "";
30
+ const EXAMPLES_INDEX = join(ROOT, "examples", "index.html");
31
+ const APP_INDEX = join(ROOT, "index.html");
32
+ const DEFAULT_INDEX = existsSync(EXAMPLES_INDEX) ? EXAMPLES_INDEX : APP_INDEX;
29
33
 
30
34
  if (EXAMPLE) {
31
35
  if (!existsSync(EXAMPLE_INDEX)) {
@@ -133,9 +137,7 @@ const server = createServer(async (req, res) => {
133
137
 
134
138
  // A mounted example owns root and SPA fallback. Otherwise serve the
135
139
  // examples index page.
136
- const fallbackIndex = EXAMPLE
137
- ? EXAMPLE_INDEX
138
- : join(ROOT, "examples", "index.html");
140
+ const fallbackIndex = EXAMPLE ? EXAMPLE_INDEX : DEFAULT_INDEX;
139
141
 
140
142
  if (pathname === "/") {
141
143
  // Resolved through fallback below.
@@ -260,6 +262,10 @@ async function buildPreloadHints() {
260
262
  }
261
263
  }
262
264
  }
265
+ } else if (!existsSync(EXAMPLES_INDEX)) {
266
+ if (existsSync(join(ROOT, "dist/main.js"))) {
267
+ hrefs.push("/dist/main.js");
268
+ }
263
269
  }
264
270
  cachedPreloadHints = hrefs
265
271
  .map((h) => ` <link rel="modulepreload" href="${h}">`)
@@ -274,16 +280,22 @@ server.on("error", (err) => {
274
280
  });
275
281
 
276
282
  server.listen(PORT, () => {
277
- const distReady = existsSync(join(ROOT, "dist/src/index.js"));
283
+ const distReady = existsSync(join(ROOT, "dist/src/index.js"))
284
+ || existsSync(join(ROOT, "dist/main.js"));
285
+ const mount = EXAMPLE
286
+ ? `examples/${EXAMPLE}/ -> /`
287
+ : existsSync(EXAMPLES_INDEX)
288
+ ? "examples/index.html landing"
289
+ : "index.html app";
278
290
  console.log("");
279
291
  console.log("Mado dev server");
280
292
  console.log(` url: http://localhost:${PORT}/`);
281
293
  console.log(` root: ${ROOT}`);
282
- console.log(` example: ${EXAMPLE ? `examples/${EXAMPLE}/ -> /` : "examples/index.html landing"}`);
294
+ console.log(` mount: ${mount}`);
283
295
  console.log(` hmr: ${HMR ? "on" : "off"}`);
284
296
  console.log(` preload: ${PRELOAD}`);
285
297
  console.log(` dist: ${distReady ? "ready" : "missing (run mado build)"}`);
286
- if (!EXAMPLE) {
298
+ if (!EXAMPLE && existsSync(EXAMPLES_INDEX)) {
287
299
  console.log(" try: mado serve basic");
288
300
  console.log(" mado serve showcase");
289
301
  console.log(" mado serve tickets");
@@ -4,18 +4,30 @@ Generated with the Mado CRUD starter.
4
4
 
5
5
  ```bash
6
6
  npm install
7
- npm run build
8
- npm run serve
7
+ npm run dev
9
8
  ```
10
9
 
11
10
  Open http://localhost:5173.
12
11
 
12
+ Use `npm run build` for a production TypeScript build and `npm run serve` to
13
+ serve an already-built app.
14
+
13
15
  This starter demonstrates:
14
16
 
15
17
  - lazy routes;
16
18
  - Web Components through `component()`;
19
+ - an app shell component with Shadow DOM and `<slot>`;
17
20
  - query params through `queryParam()`;
18
21
  - async data through `resource()`;
19
22
  - mutations with invalidation;
20
23
  - forms through `useForm()`;
21
24
  - keyed tables through `each()`.
25
+
26
+ `x-app-shell` uses Shadow DOM because it owns the page frame and projects route
27
+ pages through `<slot>`. Page/table/form components can still use global styles:
28
+ slotted route pages remain normal document DOM.
29
+
30
+ Component imports are explicit registration side effects. `main.ts` imports the
31
+ global app shell. Feature pages import the components they render, for example
32
+ `src/pages/tickets.ts` imports `../components/ticket-list.js` before rendering
33
+ `<ticket-list>`.
@@ -6,7 +6,8 @@
6
6
  "scripts": {
7
7
  "build": "tsc -p tsconfig.json",
8
8
  "typecheck": "tsc -p tsconfig.json --noEmit",
9
- "serve": "mado serve"
9
+ "serve": "mado serve",
10
+ "dev": "mado dev"
10
11
  },
11
12
  "dependencies": {
12
13
  "@madojs/mado": "__MADOJS_VERSION__"
@@ -1,7 +1,7 @@
1
1
  import { component, css, html } from "@madojs/mado";
2
2
 
3
3
  component(
4
- "app-shell",
4
+ "x-app-shell",
5
5
  () => () => html`
6
6
  <header>
7
7
  <a class="brand" href="/" data-link>__APP_NAME__</a>
@@ -11,17 +11,18 @@ component(
11
11
  <a href="/tickets/new" data-link>New ticket</a>
12
12
  </nav>
13
13
  </header>
14
- <slot></slot>
14
+ <main>
15
+ <slot></slot>
16
+ </main>
15
17
  `,
16
18
  {
17
- shadow: false,
18
19
  styles: css`
19
- app-shell {
20
+ :host {
20
21
  min-height: 100vh;
21
22
  display: block;
22
23
  }
23
24
 
24
- app-shell header {
25
+ header {
25
26
  display: flex;
26
27
  align-items: center;
27
28
  justify-content: space-between;
@@ -31,18 +32,22 @@ component(
31
32
  padding: 0.9rem 1.25rem;
32
33
  }
33
34
 
34
- app-shell nav {
35
+ main {
36
+ display: block;
37
+ }
38
+
39
+ nav {
35
40
  display: flex;
36
41
  gap: 0.75rem;
37
42
  flex-wrap: wrap;
38
43
  }
39
44
 
40
- app-shell a {
45
+ a {
41
46
  color: #334155;
42
47
  text-decoration: none;
43
48
  }
44
49
 
45
- app-shell .brand {
50
+ .brand {
46
51
  color: #111827;
47
52
  font-weight: 800;
48
53
  }
@@ -1,12 +1,9 @@
1
1
  import { html, render } from "@madojs/mado";
2
2
  import "./styles/global.js";
3
3
  import "./components/app-shell.js";
4
- import "./components/ticket-list.js";
5
- import "./components/ticket-form.js";
6
- import "./components/ticket-detail.js";
7
4
  import router from "./routes.js";
8
5
 
9
6
  const app = document.getElementById("app");
10
7
  if (!app) throw new Error("#app not found");
11
8
 
12
- render(html`<app-shell>${router.view}</app-shell>`, app);
9
+ render(html`<x-app-shell>${router.view}</x-app-shell>`, app);
@@ -1,4 +1,5 @@
1
1
  import { html, page } from "@madojs/mado";
2
+ import "../components/ticket-detail.js";
2
3
 
3
4
  export default page<{ id: string }>({
4
5
  title: ({ id }) => `Ticket ${id}`,
@@ -1,4 +1,5 @@
1
1
  import { html, page } from "@madojs/mado";
2
+ import "../components/ticket-form.js";
2
3
 
3
4
  export default page({
4
5
  title: "New ticket",
@@ -1,4 +1,5 @@
1
1
  import { html, page } from "@madojs/mado";
2
+ import "../components/ticket-list.js";
2
3
 
3
4
  export default page({
4
5
  title: "Tickets",
@@ -4,12 +4,14 @@ Generated with Mado __MADO_VERSION__.
4
4
 
5
5
  ```bash
6
6
  npm install
7
- npm run build
8
- npm run serve
7
+ npm run dev
9
8
  ```
10
9
 
11
10
  Open http://localhost:5173.
12
11
 
12
+ Use `npm run build` for a production TypeScript build and `npm run serve` to
13
+ serve an already-built app.
14
+
13
15
  ## Files
14
16
 
15
17
  - `src/main.ts` mounts the router into `#app`.
@@ -6,7 +6,8 @@
6
6
  "scripts": {
7
7
  "build": "tsc -p tsconfig.json",
8
8
  "typecheck": "tsc -p tsconfig.json --noEmit",
9
- "serve": "mado serve"
9
+ "serve": "mado serve",
10
+ "dev": "mado dev"
10
11
  },
11
12
  "dependencies": {
12
13
  "@madojs/mado": "__MADOJS_VERSION__"
@@ -1,7 +1,7 @@
1
1
  import { component, css, html, signal } from "@madojs/mado";
2
2
 
3
3
  component(
4
- "app-counter",
4
+ "x-app-counter",
5
5
  () => {
6
6
  const count = signal(0);
7
7
  return () => html`