kerfjs 4.4.0 → 4.4.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/CHANGELOG.md CHANGED
@@ -6,6 +6,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [4.4.1] - 2026-08-26
10
+
11
+
12
+
13
+ - Overhauled the README landing page: new one-line tagline, npm/size/license/TypeScript-types badges, a hoisted "Quick start" (install + `tsconfig`) right under the hook, and a jump nav (Quick start · Why kerf · Quick tour · Docs). The counter example now shows `delegate()` wiring the click handler.
14
+ - Reworked the homepage hero to lead with the value proposition ("Reactive UI that touches only the bytes that changed"), with "Introducing Kerf" demoted to an eyebrow, a right-sized logo, tighter vertical spacing, and "Get started" as the single filled primary action ("View examples" now a secondary/outline button).
15
+ - Redesigned the complete-apps index as a card grid — each app appears once with an animated preview, a one-line summary, and "Run live" / "Source" buttons.
16
+ - Every complete-app page now shows "Run live" and "View source" as buttons above the demo instead of small inline text links.
17
+ - Rewrote the site's docs pages as hand-authored consumer content (cleaner headings, no internal section numbers) rather than verbatim copies of the internal design docs.
18
+ - Renamed the migration guides' "Side-by-side code" heading to "Section by section" (the sections stack code blocks rather than showing true columns), across all framework pages.
19
+
20
+
21
+ - The getting-started and Markdown-editor demo animations now open on a non-blank frame — real code and a rendered heading are visible immediately instead of an empty pane.
22
+ - Added a horizontal-scroll shadow affordance to wide tables so off-screen columns are discoverable on narrow viewports.
23
+ - Shortened the longest demo alt text on the router and virtual-list pages for more concise screen-reader output.
24
+ - Moved framework version labels out of the performance table's framework column onto a methodology line below, keeping the column a clean label.
25
+
9
26
  ## [4.4.0] - 2026-08-23
10
27
 
11
28
 
package/README.md CHANGED
@@ -4,61 +4,72 @@
4
4
 
5
5
  <h1 align="center">Kerf</h1>
6
6
 
7
- <p align="center"><em>The smallest cut.</em></p>
7
+ <p align="center"><em>A tiny reactive UI framework. The smallest cut.</em></p>
8
8
 
9
9
  <p align="center">
10
10
  <a href="https://brianwestphal.github.io/kerf/"><strong>brianwestphal.github.io/kerf</strong></a> — docs · examples · live demo
11
11
  </p>
12
12
 
13
+ <p align="center">
14
+ <a href="https://www.npmjs.com/package/kerfjs"><img src="https://img.shields.io/npm/v/kerfjs.svg" alt="npm version" /></a>
15
+ <img src="https://img.shields.io/badge/min%2Bgzip-~12%20KB-brightgreen.svg" alt="~12 KB minified and gzipped" />
16
+ <img src="https://img.shields.io/npm/l/kerfjs.svg" alt="MIT license" />
17
+ <img src="https://img.shields.io/badge/types-included-3178c6.svg" alt="TypeScript types included" />
18
+ </p>
19
+
13
20
  ---
14
21
 
15
- > Introducing Kerf.
16
- > The smallest cut.
17
- >
18
22
  > ~12 KB. No virtual DOM. No compiler. No magic.
19
23
  > Reactive UI that touches only the bytes that changed.
20
24
 
21
25
  ```ts
22
- import { signal, mount } from 'kerfjs';
26
+ import { signal, mount, delegate } from 'kerfjs';
23
27
 
24
28
  const count = signal(0);
29
+ const app = document.getElementById('app')!;
25
30
 
26
- mount(document.getElementById('app')!, () => (
31
+ mount(app, () => (
27
32
  <div>
28
33
  <button data-action="inc">+</button>
29
34
  <span>{count.value}</span>
30
35
  </div>
31
36
  ));
37
+
38
+ delegate(app, 'click', '[data-action="inc"]', () => count.value++);
32
39
  ```
33
40
 
34
41
  That's it. Your JSX renders to HTML strings, kerf's native diff applies the minimum DOM mutations to make the live tree match, and signals re-run the render only when something they read actually changed.
35
42
 
36
43
  Here's the whole development loop — write a component, run the dev server, click around, edit, watch the browser pick it up:
37
44
 
38
- [![Animated coding session: a counter component is typed line by line into an editor, npm run dev starts in a terminal and the localhost link is clicked, the running app is clicked in a browser, then back in the editor a computed class is added — selecting "btn" and typing a bound {cls} hole — and the browser shows the button change color at the fifth click](https://brianwestphal.github.io/kerf/demos/getting-started.svg)](https://brianwestphal.github.io/kerf/getting-started/)
39
-
40
- ## Why Kerf
45
+ [![Animated demo: coding a kerf counter in an editor, running the dev server, then hot-reloading a class change in the browser](https://brianwestphal.github.io/kerf/demos/getting-started.svg)](https://brianwestphal.github.io/kerf/getting-started/)
46
+ **[Quick start](#quick-start) · [Why kerf](#why-kerf) · [Quick tour](#quick-tour) · [Docs & examples](https://brianwestphal.github.io/kerf/)**
41
47
 
42
- 1. **Small bundle.** ~12 KB minified + gzipped including `@preact/signals-core` (~13 KB with `arraySignal`). One runtime dependency. No virtual DOM, no scheduler, no concurrent-mode machinery. On the official [krausest js-framework-benchmark](https://krausest.github.io/js-framework-benchmark/current.html) — where kerf is a listed entry, measured on the same reference machine as every competitor ([local mirror](./bench/results.md)) — kerf is in the same cluster as Vue, vanjs, and Lit on most operations; Solid's compiler leads the update-path benchmarks (notably `partial update`), which kerf doesn't try to match by design — no compiler.
48
+ ## Quick start
43
49
 
44
- 2. **No virtual DOM, no compiler.** JSX → HTML strings → native diff. DevTools shows the real DOM because it *is* the DOM.
45
-
46
- 3. **Values bind, structure re-renders.** Hand a signal *itself* into a JSX hole — `class={selectedId}` or `{status}` — and kerf binds that one node directly: when the signal changes, only that attribute or text node updates, with no render re-run and no list reconcile. A selection flip on a 10,000-row table touches exactly one class. Taken to its logical end: a mount whose render reads no `.value` at all runs **exactly once, forever** — every subsequent update flows through the per-hole bindings. Read `.value` in the render only when the *structure* depends on it (conditionals, list shape).
50
+ ```bash
51
+ npm install kerfjs
52
+ ```
47
53
 
48
- 4. **Focus, selection, listeners survive re-renders — even mid-list.** The reconciler morphs instead of rebuilding, so caret position, selection range, IME composition, and delegated listeners survive every re-render. Keyed lists get the same treatment: same-identity rows are updated *in place* rather than recreated, so a row reorder or a single-cell edit no longer blows away focus, scroll, or an in-flight animation the way node replacement does.
54
+ ```jsonc
55
+ // tsconfig.json — point JSX at kerf
56
+ { "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "kerfjs" } }
57
+ ```
49
58
 
50
- 5. **Safe by default.** Text and attribute values are HTML-escaped automatically, URL attributes are scheme-screened (`javascript:` / script-carrying `data:` dropped), inline `on*` handlers are rejected outright, and the same screening covers the fine-grained bound path — so untrusted data stays inert even when kerf is dropped into someone else's page. The URL screen fails loudly at your desk (throws in development) and degrades safely in the field (warns and drops in production). `raw()` is the explicit, auditable opt-out.
59
+ Write plain `.tsx` and build with your existing esbuild / Vite / tsup no extra plugin. New here? Read the [5-minute orientation](https://github.com/brianwestphal/kerf/blob/main/docs/orientation.md), or open a [complete example](https://brianwestphal.github.io/kerf/examples/complete/).
60
+ ## Why Kerf
51
61
 
52
- 6. **JSX typed against HTML, not against React.** Tags and attributes are checked at compile time `<diiv>` and `<input typo />` don't build. The attribute types are derived from the HTML standard rather than another framework's property table, and that distinction has teeth: `draggable` and `spellcheck` are *enumerated* attributes that take the strings `"true"` / `"false"`, so kerf rejects `draggable={true}` rather than quietly emitting markup that means the opposite. Custom elements and web components slot in with one declaration merge.
62
+ 1. **~12 KB, one dependency.** ~12 KB minified + gzipped including `@preact/signals-core` (~13 KB with `arraySignal`). No virtual DOM, no scheduler, no concurrent-mode machinery. On the official [krausest benchmark](https://krausest.github.io/js-framework-benchmark/current.html) kerf sits in the same cluster as Vue, Lit, and vanjs; Solid's compiler leads the update-path benchmarks, which kerf doesn't try to match by design no compiler.
53
63
 
54
- 7. **Small public API.** ~18 exports from the main barrel (plus `arraySignal`, the `html` tagged template, and the companion-utility subpaths below each opt-in, none in the core). No hooks, no lifecycle, no per-instance state. Components are plain functions that return JSX.
64
+ 2. **No virtual DOM, no compiler.** JSX HTML strings native diff. DevTools shows the real DOM because it *is* the DOM.
55
65
 
56
- 8. **Batteries on their own subpaths.** Nine optional, tree-shakeable subpaths cover the patterns every real app otherwise hand-rolls **`kerfjs/list`** (a keyed list with per-row fine-grained mounts and fixed / app-declared / measured-height viewport **virtualization**, plus a `content-visibility` mode that keeps every row find-in-page-able), **`kerfjs/router`** (a "postcard **router**": route matching, `navigate`, auto `<a>` link interception, and a keyed outlet — the *core* stays router-free, this is opt-in), **`kerfjs/overlay`** (modals, `confirm` / `prompt` / `form` / `choice`, anchored popovers + tooltips, toasts — with opt-in native **top-layer** backing that stacks above any `z-index`), **`kerfjs/async`** (`resource` async-state with a built-in stale-response guard + SWR cache), **`kerfjs/scope`** (dispose-scopes that tie teardown to a DOM node's lifetime), plus `timing`, `remount`, `attach`, and `actions`. None of them grows the ~12 KB core until you import it.
66
+ 3. **Values bind, structure re-renders.** Hand a signal *itself* into a JSX hole `class={selectedId}` and kerf binds that one node: on change, only that attribute updates, with no render re-run and no list reconcile. A selection flip on a 10,000-row table touches exactly one class. ([more →](#fine-grained-updates-bind-a-signal-into-a-hole))
57
67
 
58
- 9. **Plain TS, plain JSX, plain ESM.** Drops into anything using esbuild / Vite / tsup. No plugin chain. And with the `html` tagged template (`import { html } from 'kerfjs/html'` identical runtime semantics to JSX), a CDN / importmap project needs no build step at all.
68
+ 4. **Focus, selection, and listeners survive re-renders even mid-list.** The reconciler morphs instead of rebuilding, so caret position, IME composition, scroll, and delegated listeners survive every update; keyed rows are patched in place rather than recreated.
59
69
 
60
- 10. **Grown-up tooling around a tiny core.** An [ESLint plugin](https://brianwestphal.github.io/kerf/docs/eslint-plugin/) that enforces the hard rules at edit time, an opt-in family of `KERF_DEV_WARN_*` runtime warnings that catch the classic mistakes in development (with zero production cost), a `create-kerf-component` scaffold for publishable component packages, drop-in AI-assistant configs, and side-by-side migration guides for a dozen-plus frameworks — none of which grows the core runtime past ~12 KB.
70
+ 5. **Safe by default.** Text and attributes are HTML-escaped automatically, URL attributes are scheme-screened (`javascript:` dropped), and inline `on*` handlers are rejected outright so untrusted data stays inert. `raw()` is the explicit, auditable opt-out.
61
71
 
72
+ **Plus, nothing you don't ask for:** JSX typed against the HTML standard (not React's props) · a ~18-export API with no hooks, lifecycle, or per-instance state · **nine** tree-shakeable companion subpaths (`router`, `list`, `overlay`, `async`, …) that stay out of the core until imported · an [ESLint plugin](https://brianwestphal.github.io/kerf/docs/eslint-plugin/) + opt-in dev warnings + a `create-kerf-component` scaffold · plain TS/JSX/ESM that drops into esbuild / Vite / tsup — or **no** build at all via the `html` tagged template.
62
73
  ## When to use Kerf
63
74
 
64
75
  - **Hybrid desktop apps (Tauri / Electron)** — small bundle, predictable diff, debuggable runtime; ideal for the embedded webview.
@@ -247,21 +258,9 @@ mount(app, () => <div><nav>{/* <a href> links, auto-intercepted */}</nav>{router
247
258
 
248
259
  Each subpath adds nothing to the main barrel until it's imported. See [`docs/8-api-reference.md`](./docs/8-api-reference.md) for the full list (`list`, `router`, `overlay`, `scope`, `async`, `timing`, `remount`, `attach`, `actions`).
249
260
 
250
- ## Install
251
-
252
- ```bash
253
- npm install kerfjs
254
- ```
261
+ ## Optional tooling
255
262
 
256
- ```jsonc
257
- // tsconfig.json
258
- {
259
- "compilerOptions": {
260
- "jsx": "react-jsx",
261
- "jsxImportSource": "kerfjs"
262
- }
263
- }
264
- ```
263
+ Install and JSX setup are in [Quick start](#quick-start) above. These companion packages are opt-in.
265
264
 
266
265
  ### Optional: `eslint-plugin-kerfjs`
267
266
 
package/ai/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "kerfjsVersion": "4.4.0",
2
+ "kerfjsVersion": "4.4.1",
3
3
  "files": [
4
4
  {
5
5
  "name": "skill",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kerfjs",
3
- "version": "4.4.0",
3
+ "version": "4.4.1",
4
4
  "description": "Tiny reactive UI framework — fine-grained signals + DOM morphing + JSX. Apply the smallest possible cut to update your DOM.",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -184,8 +184,6 @@
184
184
  "gitgist": {
185
185
  "exclude": [
186
186
  "site/public/demos/*.svg",
187
- "site/src/content/docs/docs/*",
188
- "site/src/content/docs/api.md",
189
187
  "ai/*",
190
188
  "site/public/llms.txt",
191
189
  "bench/results.json",