LUR-E is an experimental UI library focused on efficient memory management, advanced reactivity, and compatibility with modern web standards. It provides a low-level API for DOM manipulation, enhanced CSS integration, and supports web components out of the box.
- Efficient Memory Management
- Advanced Cache & Reaction System
- Low-Level DOM Manipulation
- Full CSS Compatibility
- Web Components Support
- Experimental Typed OM
- Attribute Mutation Observer
- Reactive Input Handling
The core API provides a concise and powerful way to work with the DOM:
E(Element|Selector, { attributes, dataset, style, ... }, children[] | mapped)- Create a DOM element with specified properties and children.
M(Array|Set, generateCb)- Map arrays or sets to DOM elements.
H(DOMCode)- Create static DOM HTML from code.
T(String|StringRef)- Create a TextNode object.
This is a consolidated, human-friendly overview of the public API exported from src/index.ts. For the full, generated reference, see the markdown files under ./docs/.
import {
// Core
bindBeh, bindCtrl, bindHandler, bindWith, bindForms,
$observeInput, $observeAttribute,
// Refs
makeRef, attrRef, valueRef, valueAsNumberRef, localStorageRef,
sizeRef, checkedRef, scrollRef, visibleRef, matchMediaRef, hashTargetRef, orientRef, makeWeakRef,
// Node
E, M, Q, createElement, H,
// Extensions (selected)
bindDraggable, grabForDrag, agWrapEvent,
} from "fest/lure";// Create an element
const el = E("div", {
attributes: { id: "app" },
classList: new Set(["box"]),
style: { padding: "8px" },
}, [
"Hello",
]);
document.body.append(el as Node);bindBeh(element, store, behavior): Invokebehavioron store changes.bindCtrl(element, ctrlCb): Wire common input/change/click listeners.bindHandler(element, value, prop, handler, set?, withObserver?): Generic bridge for refs β DOM.bindWith(el, prop, value, handler, set?, withObserver?): Apply once and affected.bindForms(fields?, wrapper?, state?): Two-way bind inputs within a container to a reactivestate.$observeInput(element, ref?, prop = "value"): Sync input property to ref.$observeAttribute(el, ref?, prop): Sync attribute to ref.
Create reactive references, often linked to DOM state:
makeRef(host?, type?, link?, ...args)attrRef(host, name),valueRef(host, name),valueAsNumberRef(host, name)localStorageRef(key),sizeRef(host, prop?),checkedRef(host),scrollRef(host, prop?),visibleRef(host)matchMediaRef(query),hashTargetRef(),orientRef(host)makeWeakRef(initial?, behavior?)
const out = E("button", {
attributes: { title: "Click me" },
properties: { disabled: false },
on: { click: (e) => console.log("clicked", e) },
}, ["OK"]);// Use with JSX if configured (jsxFactory: createElement)
const v = createElement("div", { className: "c" }, ["hello"]);const box = Q("#app");
box.attr.id = "app2"; // example of reactive wrapper operationsM(observable, mapper) maps a reactive array/set into DOM. Returns a reactive fragment-like node.
import { observe } from "fest/object";
const rxItems = iterated(["A", "B", "C"]);
const list = H`<ul>${M(rxItems, (x) => H`<li>${x}</li>`)}</ul>`;
// later
rxItems.push("D"); // DOM updatesH supports both raw HTML strings and tagged template strings.
- Raw string starting/ending with
</>β parsed intoNode/DocumentFragment. - Plain string β
Textnode. - Function β invoked and processed recursively.
- Tagged template β interpolates values into content/attributes/events/props.
Attribute/prop/event/ref prefixes inside tagged templates:
attr:*β HTML attributeprop:*β DOM propertyon:*or@*β event listenerreforref:*β assigns element to ref(s)
Examples:
// Raw string β Node
const el = H("<div class=box>hello</div>");
// Tagged template β content interpolation
const name = "World";
const title = H`<h1 class="title">Hello, ${name}!</h1>`;
// Dynamic tag: supports tag#id.class1.class2
const tag = "button.primary";
const btn = H`<${tag}>Click</${tag}>`;
// Attributes/props/events/refs
const ref = { value: null as HTMLElement | null };
const click = (e: Event) => console.log("clicked", e);
const button = H`<button attr:title=${"Click"} prop:disabled=${false} on:click=${click} ref=${ref}>OK</button>`;Static vs Reactive lists in H content:
// Static (non-reactive) mapping in template content
const items = ["A", "B", "C"];
const listStatic = H`<ul>${items.map(x => H`<li>${x}</li>`)}</ul>`;
// Reactive list: use M(...)
import { observe } from "fest/object";
const rxItems = iterated(["A", "B", "C"]);
const listReactive = H`<ul>${M(rxItems, (x) => H`<li>${x}</li>`)}</ul>`;Multiple top-level nodes produce a DocumentFragment:
const frag = H`<div>one</div><div>two</div>`; // DocumentFragmentPointer helpers and drag handling:
import { bindDraggable, grabForDrag } from "fest/lure";
const target = H`<div class="draggable" />` as HTMLElement;
bindDraggable(target, () => console.log("drag end"));Flexible handling of refs and DOM elements:
- Referenced content is also a DOM element (
Textnode) - HTML DOM elements also can be placed as content of other DOM elements
ref(...)are reactive and will be updated when the referenced content changes
import { ref } from "fest/object";
import { H } from "fest/lure";
// referenced content is also a DOM element (`Text` node)
const txt = ref("Hello");
const hookOf = (el: HTMLElement) => { console.log(el); };
const span = H`<span ref=${hookOf}>${txt}</span>`; // span is a DOM element (`HTMLSpanElement`)
const button = H`<button>${span}</button>`;
// Regular DOM elements
const regular = document.createElement("span");
regular.textContent = "Hello";
const another = H`<button>${regular}</button>`;- Full markdown API reference is generated into
./docs/by:
npm run docs:md- HTML documentation can be generated by:
npm run docs- Investigate advanced MutationObserver and IntersectionObserver features for DOM tree changes.
- Explore integration with Web Animations API.
- Research and implement animation-specific features, including scroll-driven animations and animation worklets.
- Consider adding support for ResizeObserver.
This project is licensed under the MIT License.
Contributions, issues, and feature requests are welcome! Feel free to check issues page.
Made with β€οΈ by fest-live
- Originally, project was named as BLU.E.
- However, I won't have 'B' as first letter.
- Also, LUR.E also should been named as BLUR.
- However, I would to save 'E' letter at end.
- And also, I don't want 'B' as first letter.
- So, I decided to use LUR.E naming.
- However, that naming still controversial.
