@microdom/mode
v2.0.0
Published
**Mode JS** is a low-level interaction engine for the web — the grammar of the **Microdom** ecosystem. One function, `µ`, reads and writes the DOM through a small, composable command vocabulary.
Downloads
371
Maintainers
Readme
Mode JS
Mode JS is a low-level interaction engine for the web — the grammar of the
Microdom ecosystem. One function, µ, reads and writes the DOM through a
small, composable command vocabulary.
µ("#price", { text: 42 }) // writes once
µ("#price", { text: priceAtom }) // keeps writingThat second line is the whole idea. Pass a plain value and µ writes it. Pass a reactive source and µ stays subscribed — the DOM tracks the source until it is re-bound or the element is gone.
The atom protocol
A reactive source is any object with two methods:
get() → the current value
sub(fn, runNow = true) → subscribe; returns an unsubscribe functionThat's the entire contract. Anything that speaks it — a Mode Atom, a Mode Data
cell, your own object — can be handed to any helper (text, html, value,
css, classes, attr) and µ keeps that helper in sync. Re-binding the same
helper on an element (through any element of a bound set) releases the previous
subscription first: no leaks, no double writers.
import { atom, computed } from "@microdom/mode/atom"
const price = atom(9.99)
µ("#price", { text: computed(() => `$${price.get()}`, [price]) })
price.set(12.50) // #price updates on its own
µ("#qty", { value: qtyAtom }) // two-way for form controlsThe family
| Module | Import | One line |
|------------|---------------------------|----------|
| mode.js | @microdom/mode | The grammar: select, read, write, traverse, bind. |
| Mode Atom | @microdom/mode/atom | The unit of state: atom, computed, effect. Zero DOM. |
| Mode List | @microdom/mode/list | Keyed list rendering with lifecycle — nodes keep their identity. |
| Mode Data | @microdom/mode/data | Columnar store for high-frequency feeds; cells speak the atom protocol. |
| Mode Move | @microdom/mode/move | Animation and movement, chained through the same µ(...) call. |
Every module is independent and zero-dependency.
Install
npm install @microdom/modeimport µ from "@microdom/mode"
// or: import { µ, html } from "@microdom/mode"Browser — native ES module (CDN, pinned):
<script type="module">
import µ from "https://cdn.jsdelivr.net/npm/@microdom/[email protected]/dist/mode.esm.min.js"
µ("body", { /* ... */ })
</script>Browser — classic <script> (global µ):
<script src="https://cdn.jsdelivr.net/npm/@microdom/[email protected]/dist/mode.min.js"></script>
<script>
const html = String.raw // optional: editor highlighting for µ templates
µ("body", { html: html`<p>hello</p>` })
</script>html is String.raw — a naming convenience so editors colorize your template
strings. µ takes plain strings; the tag does not process escapes, so write real
newlines.
Extensions — the import is the installation
Mode List and Mode Move extend the core. Importing them installs them:
import µ from "@microdom/mode"
import "@microdom/mode/move" // registers the animation vocabulary
import "@microdom/mode/list" // registers the list vocabulary
µ("#panel", { slideUp: { t: 300 } })
µ("#books", { list: { source: booksAtom, key: b => b.id, mount } })Import the core before any extension — extensions register against it at import time.
Registration is composable: each extension wraps the previous µ._ext
instead of replacing it, so several can coexist in one dispatch. It is also
guarded — loaded without the core present, an extension warns and no-ops
rather than throwing. For script tags, load mode.min.js first, then the
extension's .min.js.
Reading values
On a dispatch key, null or the string 'get' triggers read mode:
µ("#price", { text: "get" }) // read textContent
µ("#name", { value: null }) // read a control's value
µ("#el", { attr: { id: null } }) // read one attributeThe sentinel applies only at the dispatch-key level, never inside nested option
objects — so µ(form, { attr: { method: "get" } }) still sets method="get".
Finite numbers and bigints passed to text/html/value are coerced to
strings; NaN and ±Infinity are ignored.
Philosophy
Most of the web is content. It shouldn't cost application infrastructure.
Not a smaller version of the app stack — a different model for the pages that never needed one.
Read more in docs/philosophy.md,
docs/mental-models.md and
docs/why-not-x.md.
License
MIT
