ZEBRA
a class-based view framework
Build web UIs with signals and imperative DOM construction. Views are
plain classes; you compose element wrappers (Div,
Span, ...), wire reactivity with signals and effects, and
ship the same code to client or server.
Counter example
import { View, Div, Span, Button, signal } from '@matthewp/zebra';
class Counter extends View {
count = signal(0);
render() {
return new Div().addClass('counter').append(
new Span().setText(this.count),
new Button().setText('+').on('click', () => this.count(this.count() + 1)),
);
}
}
new Counter().appendTo(document.querySelector('#app')); Why Zebra
- No template DSL. Views are plain classes.
render()builds DOM with element wrappers and returns the root. - Signals, not virtual DOM. Reactivity flows through alien-signals; effects update only the nodes that depend on a changed signal.
- Same code, server or client.
view.toString()produces HTML strings without touching a DOM;view.appendTo(el)builds and attaches real DOM;view.hydrate(el)adopts SSR'd HTML and wires it up. - Typed DOM access. Reach for
getValue(),setChecked(),isFocused(),measure()—view.elis rarely needed.
Concepts
- Element wraps a tag (
Div,Span,Button, ...). Build a tree with.append(), style with.addClass()/.setStyle(), attach handlers with.on(). - View is a component class. Override
render()to return anElement. Mount withview.appendTo(el), hydrate SSR'd HTML withview.hydrate(el), or embed inside another view viaparent.append(view). - signal(v) holds reactive state.
count()reads,count(v)writes. - effect(fn) re-runs
fnwhenever any signal it read changes — used to update the DOM from state. - List handles dynamic lists with keyed reconciliation; takes a signal of items, a key fn, and a factory.
- Fragment groups multiple siblings without a wrapper tag.
- Model is a base class for state owned outside views —
gives
loading/errorsignals, an asyncrun()helper, andtoJSON()/fromJSON()for SSR round-trip. - Document / Window attach global event listeners scoped to the View that creates them; listeners are removed when the View leaves the DOM.