balises 0.7.2 → 0.8.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.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  <img alt="balises" src="./assets/logo.svg" width="280">
5
5
  </picture>
6
6
 
7
- ### A minimal reactive HTML templating library for building websites and web components. ~2.8KB gzipped.
7
+ ### A minimal reactive HTML templating library for building websites and web components. ~3.2KB gzipped.
8
8
 
9
9
  Balises gives you reactive signals and HTML templates without the framework overhead. Works great with custom elements, vanilla JavaScript projects, or anywhere you need dynamic UIs but don't want to pull in React.
10
10
 
@@ -268,19 +268,12 @@ When rendering lists that change frequently, use `each()` for keyed reconciliati
268
268
 
269
269
  **Note:** The `each()` function is opt-in via the `balises/each` import to keep the base bundle small. Use `html.with(eachPlugin)` to enable keyed list support.
270
270
 
271
- **Two forms:**
272
-
273
- 1. **Two-arg form** (object reference as key): Render receives raw item. DOM reused only when same object reference appears.
274
- 2. **Three-arg form** (explicit key function): Render receives `ReadonlySignal<T>`. DOM reused when keys match, even with new object references.
275
-
276
271
  ```ts
277
272
  import { html as baseHtml, signal } from "balises";
278
273
  import eachPlugin, { each } from "balises/each";
279
274
 
280
275
  const html = baseHtml.with(eachPlugin);
281
276
 
282
- // Three-arg form: explicit key, receives ReadonlySignal
283
- // DOM is preserved when keys match (ideal for API data)
284
277
  const users = signal([
285
278
  { id: 1, name: "Alice" },
286
279
  { id: 2, name: "Bob" },
@@ -302,25 +295,15 @@ users.value = [
302
295
  { id: 2, name: "Bobby" },
303
296
  { id: 3, name: "Carol" }, // New key - new DOM created
304
297
  ];
305
-
306
- // Two-arg form: object reference as key, receives raw item
307
- const items = signal([{ name: "Item 1" }, { name: "Item 2" }]);
308
-
309
- html`
310
- <ul>
311
- ${each(items, (item) => html`<li>${item.name}</li>`)}
312
- </ul>
313
- `.render();
314
298
  ```
315
299
 
316
- Signatures:
300
+ Signature:
317
301
 
318
302
  ```ts
319
- each(list, keyFn, renderFn); // Three-arg: keyFn extracts key, renderFn receives ReadonlySignal<T>
320
- each(list, renderFn); // Two-arg: object reference as key, renderFn receives raw T
303
+ each(list, keyFn, renderFn); // keyFn extracts key, renderFn receives ReadonlySignal<T>
321
304
  ```
322
305
 
323
- **Important:** When using the three-arg form, access item properties through `itemSignal.value` and wrap in `() => ...` for reactive updates.
306
+ **Important:** Access item properties through `itemSignal.value` and wrap in `() => ...` for reactive updates.
324
307
 
325
308
  ## Reactivity API
326
309
 
@@ -523,6 +506,34 @@ count.value = 1; // logs "count changed to 1"
523
506
  unsubscribe(); // Stop listening
524
507
  ```
525
508
 
509
+ ### `.is(value)`
510
+
511
+ Checks equality with O(1) update performance - ideal for selection patterns.
512
+
513
+ When you have a signal representing a selected item (like `selectedId`), using `.is()` inside computed/templates creates optimized subscriptions. Only computeds checking the **previous** or **new** value are notified on change, not all of them.
514
+
515
+ ```ts
516
+ import { html, signal } from "balises";
517
+
518
+ const selected = signal<number | null>(null);
519
+
520
+ // In a list of 1000 rows, only 2 rows update when selection changes
521
+ // (the previously selected and newly selected)
522
+ function Row({ id, label }) {
523
+ return html`
524
+ <tr class=${() => (selected.is(id) ? "danger" : "")}>
525
+ <td>${id}</td>
526
+ <td>${label}</td>
527
+ </tr>
528
+ `;
529
+ }
530
+
531
+ selected.value = 5; // Only row 5 gets "danger" class
532
+ selected.value = 10; // Only rows 5 and 10 update
533
+ ```
534
+
535
+ Works on both `Signal` and `Computed`. Uses `Object.is()` for equality checks.
536
+
526
537
  ### `.dispose()`
527
538
 
528
539
  Stops a computed from tracking dependencies and frees memory.
@@ -537,7 +548,7 @@ doubled.dispose(); // Stops tracking, frees memory
537
548
  You can import just what you need to keep bundle size down:
538
549
 
539
550
  ```ts
540
- // Full library (~2.8KB gzipped)
551
+ // Full library (~3.2KB gzipped)
541
552
  import { html, signal, computed, effect } from "balises";
542
553
 
543
554
  // Signals only (no HTML templating - use in any JS project)
@@ -689,23 +700,23 @@ Performance comparison of Balises against other popular reactive libraries. Benc
689
700
  ┌───────┬───────────────────┬───────┬───────────────┬──────────────────┐
690
701
  │ Rank │ Library │ Score │ Avg Time (μs) │ vs Fastest │
691
702
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
692
- │ #1 🏆 │ preact@1.12.1 │ 0.000 │ 64.17 │ 1.00x (baseline) │
703
+ │ #1 🏆 │ preact@1.12.1 │ 0.000 │ 64.93 │ 1.00x (baseline) │
693
704
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
694
- │ #2 │ balises@0.7.0 │ 0.03385.48 │ 1.33x
705
+ │ #2 │ balises@0.8.0 │ 0.052100.43 │ 1.55x
695
706
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
696
- │ #3 │ vue@3.5.26 │ 0.09594.27 │ 1.47x
707
+ │ #3 │ vue@3.5.26 │ 0.09396.74 │ 1.49x
697
708
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
698
- │ #4 │ maverick@6.0.0 │ 0.146124.891.95x
709
+ │ #4 │ maverick@6.0.0 │ 0.149130.582.01x
699
710
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
700
- │ #5 │ usignal@0.10.0 │ 0.175133.48 │ 2.08x
711
+ │ #5 │ usignal@0.10.0 │ 0.188148.47 │ 2.29x
701
712
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
702
- │ #6 │ angular@19.2.17 │ 0.214169.86 │ 2.65x
713
+ │ #6 │ angular@19.2.17 │ 0.211165.34 │ 2.55x
703
714
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
704
- │ #7 │ solid@1.9.10 │ 0.343260.01 │ 4.05x
715
+ │ #7 │ solid@1.9.10 │ 0.373320.23 │ 4.93x
705
716
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
706
- │ #8 │ mobx@6.15.0 │ 0.854910.41 │ 14.19x
717
+ │ #8 │ mobx@6.15.0 │ 0.836971.67 │ 14.97x
707
718
  ├───────┼───────────────────┼───────┼───────────────┼──────────────────┤
708
- │ #9 │ hyperactiv@0.11.3 │ 1.000 │ 1051.1216.38x
719
+ │ #9 │ hyperactiv@0.11.3 │ 1.000 │ 1144.9117.63x
709
720
  └───────┴───────────────────┴───────┴───────────────┴──────────────────┘
710
721
  ```
711
722
 
@@ -715,21 +726,21 @@ Performance comparison of Balises against other popular reactive libraries. Benc
715
726
  ┌───────────────────┬───────────────┬─────────────┬────────────────┬────────────────────┬─────────────┬──────────────┬──────────┐
716
727
  │ Library │ S1: 1: Layers │ S2: 2: Wide │ S3: 3: Diamond │ S4: 4: Conditional │ S5: 5: List │ S6: 6: Batch │ Avg Rank │
717
728
  ├───────────────────┼───────────────┼─────────────┼────────────────┼────────────────────┼─────────────┼──────────────┼──────────┤
718
- │ preact@1.12.1 │ #1 🏆 │ #1 🏆 │ #2 │ #1 🏆 │ #1 🏆 │ #2 │ 1.3
729
+ │ preact@1.12.1 │ #1 🏆 │ #1 🏆 │ #2 │ #1 🏆 │ #1 🏆 │ #1 🏆 │ 1.2
719
730
  ├───────────────────┼───────────────┼─────────────┼────────────────┼────────────────────┼─────────────┼──────────────┼──────────┤
720
- │ balises@0.7.0 │ #3 │ #2 │ #1 🏆 │ #2 │ #2 │ #1 🏆 1.8
731
+ │ balises@0.8.0 │ #3 │ #2 │ #1 🏆 │ #2 │ #2 │ #2 2.0
721
732
  ├───────────────────┼───────────────┼─────────────┼────────────────┼────────────────────┼─────────────┼──────────────┼──────────┤
722
- │ vue@3.5.26 │ #2 │ #3 │ #5 │ #3 │ #3 │ #5 │ 3.5
733
+ │ vue@3.5.26 │ #2 │ #3 │ #5 │ #3 │ #3 │ #4 │ 3.3
723
734
  ├───────────────────┼───────────────┼─────────────┼────────────────┼────────────────────┼─────────────┼──────────────┼──────────┤
724
- │ maverick@6.0.0 │ #5 │ #5 │ #4 │ #4 │ #4 │ #4 │ 4.3 │
735
+ │ maverick@6.0.0 │ #4 │ #5 │ #4 │ #4 │ #4 │ #5 │ 4.3 │
725
736
  ├───────────────────┼───────────────┼─────────────┼────────────────┼────────────────────┼─────────────┼──────────────┼──────────┤
726
- │ usignal@0.10.0 │ #4 │ #4 │ #3 │ #5 │ #8 │ #6 │ 5.0
737
+ │ usignal@0.10.0 │ #5 │ #4 │ #3 │ #5 │ #8 │ #6 │ 5.2
727
738
  ├───────────────────┼───────────────┼─────────────┼────────────────┼────────────────────┼─────────────┼──────────────┼──────────┤
728
- │ angular@19.2.17 │ #6 │ #6 │ #6 │ #6 │ #5 │ #3 │ 5.3
739
+ │ angular@19.2.17 │ #6 │ #6 │ #6 │ #6 │ #6 │ #3 │ 5.5
729
740
  ├───────────────────┼───────────────┼─────────────┼────────────────┼────────────────────┼─────────────┼──────────────┼──────────┤
730
741
  │ solid@1.9.10 │ #7 │ #8 │ #7 │ #7 │ #7 │ #7 │ 7.2 │
731
742
  ├───────────────────┼───────────────┼─────────────┼────────────────┼────────────────────┼─────────────┼──────────────┼──────────┤
732
- │ mobx@6.15.0 │ #9 │ #7 │ #8 │ #8 │ #6 │ #8 │ 7.7
743
+ │ mobx@6.15.0 │ #9 │ #7 │ #8 │ #8 │ #5 │ #8 │ 7.5
733
744
  ├───────────────────┼───────────────┼─────────────┼────────────────┼────────────────────┼─────────────┼──────────────┼──────────┤
734
745
  │ hyperactiv@0.11.3 │ #8 │ #9 │ #9 │ #9 │ #9 │ #9 │ 8.8 │
735
746
  └───────────────────┴───────────────┴─────────────┴────────────────┴────────────────────┴─────────────┴──────────────┴──────────┘
@@ -750,7 +761,7 @@ Performance comparison of Balises against other popular reactive libraries. Benc
750
761
  - These are synthetic benchmarks measuring pure reactivity - real apps should consider the whole picture (ecosystem, docs, community, etc.)
751
762
  - Lower rank = better performance
752
763
 
753
- _Last updated: 2026-01-05_
764
+ _Last updated: 2026-01-10_
754
765
 
755
766
  <!-- BENCHMARK_RESULTS_END -->
756
767