nuclo 0.2.17 → 0.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,14 @@
1
1
  # nuclo
2
2
 
3
- **A lightweight, imperative DOM framework with explicit updates.**
3
+ **A lightweight, type-safe JS/TS DOM framework with plain mutable state and explicit updates.**
4
4
 
5
- Build interactive UIs with plain functions, mutable JavaScript objects, and explicit `update()` calls. Nuclo is imperative: nothing re-renders until you call `update()`. No virtual DOM, no proxies, and no hidden state tracking.
5
+ Create elements with plain functions, and pass a function anywhere a value depends on state — that function is registered so Nuclo can evaluate it again later. Change regular JavaScript values directly, then call `update()` when you want Nuclo to synchronize the DOM. No virtual DOM, no proxies, no signals, and no automatic state tracking.
6
+
7
+ Nuclo's workflow has three parts:
8
+
9
+ 1. **Create the UI with builder functions** — text, attributes, conditions, and lists can be passed a function whenever they depend on state; this registers the function as a state-dependent value.
10
+ 2. **Mutate regular JavaScript state** — no signals, no proxies, no wrapped values. `count++` is just `count++`. Mutating state does not, by itself, touch the DOM.
11
+ 3. **Call `update()`** — this explicitly starts synchronization: Nuclo reevaluates the state-dependent values registered in step 1 and applies changed results to the real DOM.
6
12
 
7
13
  ```ts
8
14
  import 'nuclo';
@@ -22,12 +28,13 @@ render(counter, document.body);
22
28
 
23
29
  ## Why nuclo?
24
30
 
25
- - **Explicit and Predictable** – You control when updates happen with a simple `update()` call
26
- - **Direct DOM Manipulation** – Work directly with the DOM, no virtual layer in between
27
- - **Tiny Footprint** – ~10 KB gzipped, zero dependencies
31
+ - **State-Dependent Values** – Pass a function for any text, attribute, condition, or list that depends on state; Nuclo can evaluate it again later
32
+ - **Explicit and Predictable** – Reevaluation doesn't happen on mutation — you trigger it yourself with a simple `update()` call
33
+ - **Real DOM, No Virtual Layer** – Nuclo creates and updates real DOM nodes directly; there's no virtual DOM to diff
34
+ - **Tiny Footprint** – ~13 KB gzipped, zero dependencies
28
35
  - **Global Tag Builders** – Natural API with global functions for all HTML and SVG elements
29
36
  - **TypeScript-First** – Full type definitions for all 175 HTML and SVG builders
30
- - **Targeted DOM Updates** – `update()` re-runs dynamic bindings and only touches DOM where values changed
37
+ - **Targeted DOM Updates** – `update()` re-runs registered state-dependent values and only touches DOM where values changed
31
38
  - **Atomic Styling** – Built-in `css()` / `createCss()` with TypeScript autocomplete, theming, and SSR CSS collection
32
39
  - **Server-Side Rendering** – `renderToString()` + `hydrate()` with a lightweight DOM polyfill
33
40
 
@@ -303,6 +310,16 @@ render(app, document.body);
303
310
 
304
311
  ## Core Concepts
305
312
 
313
+ **The mental model:**
314
+
315
+ 1. Create the UI with builder functions — `h1(...)`, `when()`, `list()`.
316
+ 2. Use functions for values that depend on state — `h1(() => \`Count: ${count}\`)`. This registers a state-dependent value.
317
+ 3. Mutate regular JavaScript state — `count++`.
318
+ 4. Call `update()`.
319
+ 5. Nuclo reevaluates the registered state-dependent values and applies the changes to the existing DOM.
320
+
321
+ State mutation and the `update()` call are plain JavaScript: you write `count++` and call `update()` yourself. Nuclo does not watch your variables — nothing reevaluates until `update()` runs. The function from step 2 stays registered so it can be evaluated again, but only `update()` starts that evaluation.
322
+
306
323
  ### 1. **Explicit Updates**
307
324
 
308
325
  nuclo doesn't auto-detect changes. You call `update()` when ready:
@@ -344,7 +361,7 @@ user.name = 'Alice'; // triggers update
344
361
 
345
362
  ### 2. **Dynamic Functions**
346
363
 
347
- Zero-arg functions become dynamic bindings that re-run when you call `update()`:
364
+ Zero-arg functions become state-dependent values: Nuclo keeps them registered and can evaluate them again, but only when you call `update()` — mutating the state they read does nothing on its own:
348
365
 
349
366
  ```ts
350
367
  let count = 0;
@@ -719,18 +736,18 @@ These markers identify conditional and list boundaries in the DOM.
719
736
  ### Gotchas
720
737
 
721
738
  **Parameter count decides what a function modifier means.**
722
- A modifier with **zero declared parameters** is treated as reactive text (or a reactive `cn()` className) and is called with no arguments. A modifier with **one or more declared parameters** is a node-modifier function receiving `(element, index)`. Default and rest parameters don't count as declared parameters (`fn.length` is 0), so these are all treated as *reactive text*, not node modifiers:
739
+ A modifier with **zero declared parameters** is treated as dynamic text (or a dynamic `cn()` className) and is called with no arguments. A modifier with **one or more declared parameters** is a node-modifier function receiving `(element, index)`. Default and rest parameters don't count as declared parameters (`fn.length` is 0), so these are all treated as *dynamic text*, not node modifiers:
723
740
 
724
741
  ```ts
725
- div((el = fallback) => el.id); // reactive text — el is undefined!
726
- div((...args) => args[0]); // reactive text — args is empty!
742
+ div((el = fallback) => el.id); // dynamic text — el is undefined!
743
+ div((...args) => args[0]); // dynamic text — args is empty!
727
744
  div((el) => el.id = "x"); // node modifier — el is the element ✓
728
745
  ```
729
746
 
730
747
  If a modifier needs the element, declare it as a plain required parameter.
731
748
 
732
- **Detached nodes lose reactivity.**
733
- Reactive registrations (text, attributes, `list()`, `when()`) are pruned for any node that is disconnected from the DOM when `update()` runs. Re-attaching the node later does **not** restore them. Keep-alive and portal-style patterns should re-render content after re-attaching instead of moving live subtrees between updates.
749
+ **Detached nodes stop updating.**
750
+ Registered state-dependent values (text, attributes, `list()`, `when()`) are pruned for any node that is disconnected from the DOM when `update()` runs. Re-attaching the node later does **not** restore them. Keep-alive and portal-style patterns should rebuild content after re-attaching instead of moving live subtrees between updates.
734
751
 
735
752
  ---
736
753
 
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "nuclo",
3
3
  "private": false,
4
- "version": "0.2.17",
5
- "description": "Lightweight reactive DOM library with global tag builders, SSR, hydration, and atomic CSS — no virtual DOM, no compiler.",
4
+ "version": "0.2.19",
5
+ "description": "Lightweight, type-safe DOM library with plain mutable state and explicit updates — global tag builders, SSR, hydration, and atomic CSS, no virtual DOM, no compiler.",
6
6
  "license": "MIT",
7
7
  "author": "Danilo Celestino de Castro (https://github.com/dan2dev)",
8
8
  "keywords": [
9
9
  "dom",
10
- "reactive",
11
10
  "ui",
12
11
  "ssr",
13
12
  "hydration",
@@ -26,7 +26,8 @@ declare global {
26
26
 
27
27
  /**
28
28
  * Hydrates an existing server-rendered DOM tree by walking it in parallel with
29
- * the component tree, reusing existing nodes and re-attaching reactivity.
29
+ * the component tree, reusing existing nodes and re-registering their
30
+ * state-dependent values for future `update()` calls.
30
31
  *
31
32
  * @param nodeModFn The NodeModFn to hydrate (same component used for SSR)
32
33
  * @param parent The parent element containing the SSR HTML (defaults to document.body)
@@ -5,7 +5,7 @@ declare global {
5
5
  // SVG attribute types that accept string values for all SVG properties
6
6
  // This is needed because SVG DOM properties like 'width' are SVGAnimatedLength,
7
7
  // but we set them as strings using setAttribute()
8
- // Supports reactive values (functions) for dynamic attributes
8
+ // Supports state-dependent values (functions) for dynamic attributes
9
9
  export type SVGAttributes = {
10
10
  // Common SVG attributes
11
11
  width?: SVGAttributeValue<string | number>;
@@ -70,7 +70,7 @@ declare global {
70
70
  "clip-path"?: SVGAttributeValue<string>;
71
71
  mask?: SVGAttributeValue<string>;
72
72
 
73
- // Allow any other string attributes (including reactive functions)
73
+ // Allow any other string attributes (including state-dependent functions)
74
74
  // Using a more restrictive type to maintain type safety
75
75
  } & {
76
76
  [K in string]?: SVGAttributeValue;