@nativele/ts-dom 0.4.0 → 0.5.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.
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: nativele-<tool>
3
+ description: "What the Skill lets an agent do, and when to use it (the dependencies or files that call for it)."
4
+ metadata:
5
+ tier: experimental
6
+ nativele: ">=0.5.0 <0.6.0"
7
+ verified: "YYYY-MM-DD"
8
+ tools: "<tool>@<exact version> <other tool>@<exact version>"
9
+ author: "<your name or GitHub handle>"
10
+ detects: "<package.json dependency names, space-separated>"
11
+ ---
12
+
13
+ # Nativele + <tool>
14
+
15
+ ## When to use
16
+
17
+ - The situation, and the signals (dependencies, files) that call for this Skill.
18
+ - When not to use it.
19
+
20
+ ## Versions
21
+
22
+ | Tool | Verified version |
23
+ |---|---|
24
+ | @nativele/ts-dom | x.y.z |
25
+ | <tool> | x.y.z |
26
+
27
+ Verified on YYYY-MM-DD. With a different major version of <tool>, treat every step as unverified.
28
+
29
+ ## Set-up
30
+
31
+ 1. Numbered steps with the exact commands and file contents.
32
+
33
+ ## Example
34
+
35
+ The complete, minimal working example: every file it needs.
36
+
37
+ ## Known limitations
38
+
39
+ 1. What does not work or needs care, and what to do instead.
40
+
41
+ ## Verify
42
+
43
+ ```sh
44
+ npx ts-dom check # expect: ts-dom check: no issues
45
+ npx tsc --noEmit # expect: no errors
46
+ ```
47
+
48
+ What to check in the browser or with a test.
49
+
50
+ ## Troubleshooting
51
+
52
+ | Symptom | Cause | Fix |
53
+ |---|---|---|
54
+
55
+ ## See also
56
+
57
+ - Links to the guides in https://github.com/Pixmi/nativele/tree/main/docs for the facts used.
@@ -0,0 +1,92 @@
1
+ ---
2
+ name: nativele-core
3
+ description: "Build and change web components with Nativele (@nativele/ts-dom): the write-check-fix loop, templates, state, lists and data flow. Use whenever a project depends on @nativele/ts-dom and you add or change an .html template or a component."
4
+ metadata:
5
+ tier: official
6
+ nativele: ">=0.5.0 <0.6.0"
7
+ verified: "2026-11-05"
8
+ tools: "typescript@7.0.2"
9
+ author: "Nativele maintainers"
10
+ detects: "@nativele/ts-dom"
11
+ ---
12
+
13
+ # Nativele core
14
+
15
+ ## When to use
16
+
17
+ - The project depends on `@nativele/ts-dom` and you write or change a component: an `x.html` template with its `x.ts` class.
18
+ - Not for using Nativele components from a framework (React, Vue, ...): see the integration Skills (`npx ts-dom skills`), which build on this one.
19
+
20
+ A component is two files. The HTML holds structure and names (`{{ name }}`, `data-ref`, `data-if`, `data-each`, `data-on-*`), never expressions; the TypeScript class holds state and behavior. `ts-dom gen` turns each `x.html` into a typed `x.html.ts`, and `tsc` checks that the two files agree.
21
+
22
+ ## Versions
23
+
24
+ | Tool | Verified version |
25
+ |---|---|
26
+ | @nativele/ts-dom | 0.5.0 |
27
+ | typescript | 7.0.2 |
28
+
29
+ Verified on 2026-11-05 by the library's own CI, which type-checks, lints and runs every example in the repository in real Chrome.
30
+
31
+ ## Set-up
32
+
33
+ 1. `npm i @nativele/ts-dom` and `npm i -D typescript`.
34
+ 2. `tsconfig.json` with `"strict": true`, `"lib"` including `"DOM"`, and `"moduleResolution"` `"NodeNext"` or `"bundler"`; its `include` must cover the folders with templates.
35
+ 3. With Vite, add `tsDom()` from `@nativele/ts-dom/vite` to `plugins` and set `"type": "module"` in `package.json`. Without a bundler, `ts-dom gen` plus `tsc` is enough.
36
+ 4. Run `npx ts-dom doctor` once; fix every error and warning it reports. Its `info` lines name the other Skills that fit the project.
37
+
38
+ ## The loop
39
+
40
+ 1. Write the `.html` and the `.ts` of a component together. Import the template as `./x.html.js`.
41
+ 2. Run `npx ts-dom check` (add `--json` to read the result as data). It regenerates the generated files, runs `tsc`, points template contract errors at the template line and column, and runs the lint rules. Use `--no-write` where the working tree must not change.
42
+ 3. Fix every issue by its `Fix:` hint. Never cast a type error away, and never edit `x.html.ts` or `ts-dom-tags.ts`: they are generated.
43
+ 4. `npx ts-dom rules --explain=<id>` explains any rule id from a diagnostic, with a wrong and a right example.
44
+ 5. Repeat until `ts-dom check` prints `no issues`.
45
+
46
+ ## Rules
47
+
48
+ - No expressions in HTML: `{{ name }}` is a single identifier. Compute in `derive` (or in TypeScript) and fill a named hole.
49
+ - Change state only with `this.set()`. Rendering is synchronous; `this.batch()` combines updates.
50
+ - Once a component has `state`, `props` or `derive`, every `{{ hole }}` and `data-if` must name one of them. Show store data by copying it into state in the `bind` callback.
51
+ - `store.set` replaces the whole value; a component's `this.set` merges. Spread the rest yourself: `store.set((s) => ({ ...s, status: "loading" }))`.
52
+ - Lists, attributes and focus go in `render(view, changed)`. `changed` holds the state, prop and derived names that changed; annotate it with the names you check (`changed: ReadonlySet<"items" | "visible">`), never `ReadonlySet<string>`.
53
+ - Props go down (the parent sets them), events go up (`emit`, handled with `data-on-<event>` on the child tag). `emit` does not bubble; data needed several levels away goes in a shared `createStore` that the components `bind`.
54
+ - State, props and the view are deeply read-only: replace objects instead of changing them (`{ ...todo, done: true }`).
55
+ - A `data-each` list cannot be nested: the inner list belongs to its own item component.
56
+ - For a list of components, write `data-key` and `data-prop` on the `<template data-each>`, give the item component `role="listitem"` inside `<ul>` or `<ol>`, assign `this.lists.x.items = ...` in `render`, and type item handlers as `hit: EventHit<Item, "id">`.
57
+ - Do not name a handler or prop like a DOM or Nativele member (`remove`, `click`, `title`, `render`, ...); `ts-dom check` reports it.
58
+
59
+ ## Known limitations
60
+
61
+ - Holes are text only; attributes are set in TypeScript through `data-ref`.
62
+ - A derived value cannot read another derived value; share the calculation as a plain function.
63
+ - `data-if` removes the element from the page (its state is kept in memory); to hide an element while it keeps focus or media playback, set `this.refs.x.hidden` in `render`.
64
+ - Nativele has no router, data fetching, form validation library, styling system or global state manager by design; use the project's tools for those, and see the other Skills for how they connect.
65
+
66
+ ## Verify
67
+
68
+ ```sh
69
+ npx ts-dom check --strict # expect: ts-dom check: no issues
70
+ npx ts-dom doctor # expect: ts-dom doctor: the set-up looks right (info lines are suggestions)
71
+ npx tsc --noEmit # expect: no errors
72
+ ```
73
+
74
+ Then exercise the changed component in a real browser (the project's own test runner, or the browser).
75
+
76
+ ## Troubleshooting
77
+
78
+ | Symptom | Cause | Fix |
79
+ |---|---|---|
80
+ | `Cannot find module './x.html.js'` | the generated file is missing | run `npx ts-dom check` (or `ts-dom gen`) |
81
+ | `missing-binding` at a template line | a hole or `data-if` name that state, props and derive do not have | add the field, or rename the hole |
82
+ | A list stays empty | `render` never assigns it, or guards it with a name that is not in `changed` | assign `this.lists.x.items` in `render`, guarded by the name of what it shows |
83
+ | A child component does not update | an object prop was changed in place | pass a new object |
84
+ | No types in a parent's `refs.child` | the child class is not exported, or is defined outside the scanned folders | `export class`, and keep it in a folder `ts-dom gen` scans |
85
+
86
+ ## See also
87
+
88
+ - Templates: https://github.com/Pixmi/nativele/blob/main/docs/templates.md
89
+ - Components and data flow: https://github.com/Pixmi/nativele/blob/main/docs/components.md
90
+ - Lists: https://github.com/Pixmi/nativele/blob/main/docs/lists.md
91
+ - Recipes (loading states, dialogs, forms, store data): https://github.com/Pixmi/nativele/blob/main/docs/recipes.md
92
+ - Setup, checks and lint: https://github.com/Pixmi/nativele/blob/main/docs/tooling.md
@@ -0,0 +1,197 @@
1
+ ---
2
+ name: nativele-react
3
+ description: "Use Nativele web components inside a React 19 app: passing props, receiving events, children and slots, lifecycle, and TSX types. Use when a project depends on react and @nativele/ts-dom."
4
+ metadata:
5
+ tier: official
6
+ nativele: ">=0.5.0 <0.6.0"
7
+ verified: "2026-11-05"
8
+ tools: "react@19.3.1 react-dom@19.3.1 @types/react@19.3.0 @types/react-dom@19.3.0 vite@8.3.5 @vitejs/plugin-react@6.2.1 typescript@7.0.2"
9
+ author: "Nativele maintainers"
10
+ detects: "react"
11
+ ---
12
+
13
+ # Nativele + React
14
+
15
+ ## When to use
16
+
17
+ - The project renders with React 19 and wants to use components written with Nativele (`@nativele/ts-dom`), for example a design-system layer or widgets shared with non-React pages.
18
+ - Signals: `react` and `@nativele/ts-dom` in `package.json`, `.tsx` files that render tags with a hyphen (`<info-card>`).
19
+ - Not for React 18 or earlier: React 18 sets every prop on a custom element as an attribute and does not attach `on...` handlers to custom events. The steps below are verified only with React 19.
20
+
21
+ Nativele components are native custom elements. React renders them like any other tag; nothing wraps them.
22
+
23
+ ## Versions
24
+
25
+ | Tool | Verified version |
26
+ |---|---|
27
+ | @nativele/ts-dom | 0.5.0 |
28
+ | react, react-dom | 19.3.1 |
29
+ | @types/react, @types/react-dom | 19.3.0 |
30
+ | vite | 8.3.5 |
31
+ | @vitejs/plugin-react | 6.2.1 |
32
+ | typescript | 7.0.2 |
33
+
34
+ Verified on 2026-11-05 in Google Chrome 148 with the example project in the Nativele repository (`skills/nativele-react/example`), which CI runs again when the Skill or the library changes and once a month. With a different major version of React, treat every step as unverified.
35
+
36
+ ## Set-up
37
+
38
+ 1. Keep the Nativele components in their own folder, for example `src/nv/`, each as `x.html` plus `x.ts` (see the core Skill or the guides).
39
+ 2. Install: `npm i @nativele/ts-dom`.
40
+ 3. Add the Nativele Vite plugin next to the React plugin, pointing at that folder:
41
+
42
+ ```ts
43
+ // vite.config.ts
44
+ import { defineConfig } from "vite";
45
+ import react from "@vitejs/plugin-react";
46
+ import { tsDom } from "@nativele/ts-dom/vite";
47
+
48
+ export default defineConfig({ plugins: [tsDom({ dirs: ["src/nv"] }), react()] });
49
+ ```
50
+
51
+ 4. Import every Nativele component module in the entry file, before React renders anything:
52
+
53
+ ```tsx
54
+ // src/main.tsx
55
+ import "./nv/info-card.js";
56
+ import "./nv/shadow-card.js";
57
+ // then createRoot(...).render(...)
58
+ ```
59
+
60
+ React 19 sets a value as a property only when the element is already defined; before that it writes an attribute, and an object prop becomes the text `"[object Object]"`.
61
+
62
+ 5. Add the TSX declaration below as `src/nativele-react.d.ts`. It reads the tag map that `ts-dom gen` writes (`ts-dom-tags.ts`), so every exported component gets typed props and events, and new components need no edit:
63
+
64
+ ```ts
65
+ // src/nativele-react.d.ts
66
+ import type { DetailedHTMLProps, HTMLAttributes } from "react";
67
+
68
+ type NativeleTag = Extract<keyof HTMLElementTagNameMap, `${string}-${string}`>;
69
+ type NativeleProps<E> = E extends { readonly props: infer P } ? { [K in keyof P & keyof E]?: E[K] } : unknown;
70
+ type NativeleEvents<E> = E extends { readonly "~emits"?: infer M }
71
+ ? { [K in keyof NonNullable<M> & string as `on${K}`]?: (event: CustomEvent<NonNullable<M>[K]>) => void }
72
+ : unknown;
73
+ type NativeleElementProps<E extends HTMLElement> = Omit<DetailedHTMLProps<HTMLAttributes<E>, E>, keyof NativeleProps<E>> &
74
+ NativeleProps<E> &
75
+ NativeleEvents<E>;
76
+ type NativeleIntrinsics = { [K in NativeleTag]: NativeleElementProps<HTMLElementTagNameMap[K]> };
77
+
78
+ declare module "react" {
79
+ namespace JSX {
80
+ interface IntrinsicElements extends NativeleIntrinsics {}
81
+ }
82
+ }
83
+ ```
84
+
85
+ The component class must be exported and passed to `define()` in a folder that `ts-dom gen` scans, so it appears in `ts-dom-tags.ts`.
86
+
87
+ ## Example
88
+
89
+ A Nativele component with props and an event:
90
+
91
+ ```html
92
+ <!-- src/nv/info-card.html -->
93
+ <span>{{ label }}</span>
94
+ <b>{{ count }}</b>
95
+ <i data-ref="itemName"></i>
96
+ <button type="button" data-on-click="pick">pick</button>
97
+ ```
98
+
99
+ ```ts
100
+ // src/nv/info-card.ts
101
+ import { Component, define, event } from "@nativele/ts-dom";
102
+ import tpl from "./info-card.html.js";
103
+
104
+ export interface Item {
105
+ id: number;
106
+ name: string;
107
+ }
108
+
109
+ export class InfoCard extends Component(tpl, {
110
+ props: { label: "", count: 0, item: null as Item | null },
111
+ emits: { "item-select": event<{ id: number }>() },
112
+ }) {
113
+ pick(): void {
114
+ const item = this.props.item;
115
+ if (item) this.emit("item-select", { id: item.id });
116
+ }
117
+ override render(view: { item: Item | null }): void {
118
+ this.refs.itemName.textContent = view.item ? view.item.name : "-";
119
+ }
120
+ }
121
+ define("info-card", InfoCard);
122
+ ```
123
+
124
+ Used from React:
125
+
126
+ ```tsx
127
+ <info-card label={label} count={count} item={item} onitem-select={(e) => select(e.detail.id)} />
128
+ ```
129
+
130
+ - `label`, `count` and `item` arrive as properties; the object is the same object React passed. Changing them from React state re-renders the Nativele component.
131
+ - The event handler prop is `on` followed by the exact event name: `onitem-select`. `e.detail` is typed from the component's `emits`.
132
+
133
+ A component with slots that receives React children: give it `shadow: true`, then pass children directly.
134
+
135
+ ```ts
136
+ // src/nv/shadow-card.ts
137
+ export class ShadowCard extends Component(tpl, { shadow: true }) {}
138
+ define("shadow-card", ShadowCard);
139
+ ```
140
+
141
+ ```tsx
142
+ <shadow-card>
143
+ {showTitle && <span slot="title">{title}</span>}
144
+ {rows.map((r) => <p key={r.id}>{r.text}</p>)}
145
+ </shadow-card>
146
+ ```
147
+
148
+ ## Known limitations
149
+
150
+ 1. **Define before render.** Import every Nativele module before React first renders its tag (step 4). Otherwise props become attributes and object props are lost.
151
+ 2. **Event prop names.** Only `on` plus the exact event name works (`onitem-select`); `onItemSelect` and `onItem-select` never fire. Nativele events do not bubble by default, so put the handler on the element that emits. A ref with `addEventListener` in `useEffect` also works (remove it in the cleanup).
152
+ 3. **Light DOM slots and React children do not mix.** In the default Light DOM mode, Nativele moves the host's children into its slot positions. When React later inserts, removes or reorders those children it fails (`insertBefore`/`removeChild` errors that unmount the root) or leaves nodes outside the slot. Changing text in place and appending at the end happen to work, but do not rely on that. Use one of:
153
+ - `shadow: true` on the component (children can then change freely), or
154
+ - one stable React element per slot, and let React change only what is inside it:
155
+
156
+ ```tsx
157
+ <light-card>
158
+ <div slot="title">{showTitle && <span>{title}</span>}</div>
159
+ <div>{rows.map((r) => <p key={r.id}>{r.text}</p>)}</div>
160
+ </light-card>
161
+ ```
162
+
163
+ A stable but empty wrapper still fills its slot, so the slot's fallback content does not show.
164
+ 4. **New objects for changed props.** A Nativele component compares props by reference; pass a new object when the data changes (React state updates usually do).
165
+ 5. **TSX types need the declaration** in step 5; without it, every Nativele tag is `TS2339: Property 'info-card' does not exist on type 'JSX.IntrinsicElements'`. Check it with a `.tsx` that has a wrong prop under `// @ts-expect-error`, because `skipLibCheck` can hide mistakes inside the declaration itself.
166
+ 6. Not verified: server-side rendering, form controls inside Nativele components submitting with a React form, and React versions other than 19.3.
167
+
168
+ React StrictMode is fine: it runs effects twice but does not insert the custom element twice, so `connected()` runs once, and `bind` subscriptions are released when React unmounts the element.
169
+
170
+ ## Verify
171
+
172
+ Run in the project:
173
+
174
+ ```sh
175
+ npx ts-dom check src/nv # expect: ts-dom check: no issues
176
+ npx ts-dom doctor src/nv # expect: ts-dom doctor: the set-up looks right
177
+ npx tsc --noEmit # expect: no errors (keep one @ts-expect-error case per component kind)
178
+ npx vite build # expect: built
179
+ ```
180
+
181
+ In the browser, render a component with an object prop and an event handler, and check that the element has no `item` attribute (`el.getAttributeNames()`), that `el.item` is the object, and that the handler fires once per click. The example project's `verify.mjs` does these checks with Playwright, plus children that React reorders, adds and removes inside a `shadow: true` component and inside stable wrappers in a Light DOM component.
182
+
183
+ ## Troubleshooting
184
+
185
+ | Symptom | Cause | Fix |
186
+ |---|---|---|
187
+ | An object prop shows `[object Object]` or is missing | the component was rendered before `define()` ran | import the component module in the entry file before rendering |
188
+ | The event handler never runs | the prop name is camelCased (`onItemSelect`) | use `on` plus the exact event name (`onitem-select`) |
189
+ | `insertBefore`/`removeChild` errors, the React root disappears | React children changed inside a Light DOM component with slots | use `shadow: true`, or stable wrapper elements per slot |
190
+ | `TS2339: Property 'x-y' does not exist on type 'JSX.IntrinsicElements'` | the declaration file is missing, or the component class is not exported | add `src/nativele-react.d.ts`, export the class, run `ts-dom gen` |
191
+ | `ts-dom check` reports `unused-ref` | a `data-ref` the class never reads | use it in the class or remove the attribute |
192
+
193
+ ## See also
194
+
195
+ - Components, props and events: https://github.com/Pixmi/nativele/blob/main/docs/components.md
196
+ - Slots, Light DOM and Shadow DOM: https://github.com/Pixmi/nativele/blob/main/docs/components.md#light-default-and-shadow
197
+ - Setup and checks: https://github.com/Pixmi/nativele/blob/main/docs/tooling.md