This is the full developer documentation for ElementsKit
# Introduction
> Universal reactive primitives — signals, utilities, JSX, custom elements.
ElementsKit is a toolkit of reactive primitives — signals, JSX, custom elements, and browser-API helpers. Import one at a time, compose them, or use any of them inside a UI framework (React, etc.).
* **Compose, don’t configure.** Small focused APIs — `signal`, `computed`, `on`, `fromEvent`, `async`. Combine primitives instead of maintaining an overloaded interface.
* **Close to the platform.** JSX compiles to `document.createElement`. `promise` extends `Promise`. Custom elements *are* `HTMLElement`. Thin or absent abstraction layers — no virtual DOM, no proxies, no build steps.
* **Predictable and explicit — no magic.** `signal/compose` are reactive; nothing else is. No heuristic dependency tracking, no hidden subscriptions.
* **Designed for the AI age.** Code is cheap; maintenance still isn’t. Primitives compose into higher-level blocks. Swap one block at a time instead of maintaining long lines of code.
* **Bundler-friendly.** Every primitive is its own subpath — `elements-kit/signals`, `elements-kit/utilities/media-query`, `elements-kit/integrations/react`. Import only what you need.
Signals & Stores
Fine-grained reactive state. `signal`, `computed`, `effect`, and `@reactive` class fields — plain classes, no proxies. Shared across custom elements, React components, and plain scripts, all in sync.
Utilities
Browser-API signals — `online`, `windowSize`, `createMediaQuery`, `on`, `fromEvent`, observers, and more. Reactive wrappers, pay-per-import.
Async
`async`, `promise`, `retry`, `createLocalStorage`. Compose queries, mutations, persistence, and revalidation — no query library required.
Elements
JSX compiles to real `document.createElement` calls. Reactive props become live DOM bindings — no diffing, no reconciliation, no runtime overhead.
Custom Elements
Native `HTMLElement` subclasses enhanced with signals, JSX, and decorators. Usable in any HTML context — React, Vue, or plain HTML — no adapters needed.
Framework Integration
Bridge any signal into a UI framework via `useSignal` / `useScope`. React today, Svelte and Vue planned.
## Install
* pnpm
```sh
pnpm add elements-kit
```
* npm
```sh
npm install elements-kit
```
* yarn
```sh
yarn add elements-kit
```
* bun
```sh
bun add elements-kit
```
Full setup — TypeScript JSX config, CDN, Deno — in [Installation](/getting-started/installation).
* Counter
```tsx
import {
function signal(): Updater & Computed (+1 overload)
Creates a mutable reactive signal.
Read: call with no arguments → returns the current value and
subscribes the active tracking context.
Write: call with a value → updates the signal and schedules
downstream effects if the value changed.
@example
const count = signal(0);
count(); // → 0 (read)
count(1); // write – effects depending on count will re-run
count(); // → 1
signal,
function computed(getter: (previousValue?: T) => T): () => T
Creates a lazily-evaluated computed value.
The getter is only called when the computed value is read and one of
its dependencies has changed since the last evaluation. If nothing has
changed the cached value is returned without re-running getter.
Computed values are read-only; they cannot be set directly.
@param ― getter - Pure function deriving a value from other reactive sources.
Receives the previous value as an optional optimisation hint.
@example
const a = signal(1);
const b = signal(2);
const sum = computed(() => a() + b());
sum(); // → 3
a(10);
sum(); // → 12 (re-evaluated lazily)
computed } from "elements-kit/signals";
import {
function render(target: Element | DocumentFragment, setup: () => Node | null | undefined): () => void
Mount a node into target with a scoped lifetime.
setup runs inside a detached effectScope. The returned node is appended
to target. Calling the returned unmount removes the node from the DOM,
disposes its Symbol.dispose hook (JSX-created elements carry one), and
tears down every effect / onCleanup registered inside setup.
@example
import { render } from "elements-kit/render";
const unmount = render(document.getElementById("app")!, () => );
// later
unmount();
render } from "elements-kit/render";
import type {
type ReactiveProps
= { readonly [K in keyof P]: Computed
; } & {
readonly [RAW_PROPS]?: P;
}
ReactiveProps } from "elements-kit/jsx-runtime";
function
function Counter(props: ReactiveProps<{
initial?: number;
}>): JSX$1.Element
Counter(
props: ReactiveProps<{
initial?: number;
}>
props:
type ReactiveProps
= { readonly [K in keyof P]: Computed
; } & {
readonly [RAW_PROPS]?: P;
}
ReactiveProps<{
initial?: number | undefined
initial?: number }>) {
const
const count: Updater & Computed
count =
signal(initialValue: number): Updater & Computed (+1 overload)
Creates a mutable reactive signal.
Read: call with no arguments → returns the current value and
subscribes the active tracking context.
Write: call with a value → updates the signal and schedules
downstream effects if the value changed.
@example
const count = signal(0);
count(); // → 0 (read)
count(1); // write – effects depending on count will re-run
count(); // → 1
signal(
props: ReactiveProps<{
initial?: number;
}>
props.
initial?: Computed | undefined
initial() ?? 0);
const
const doubled: () => number
doubled =
computed(getter: (previousValue?: number | undefined) => number): () => number
Creates a lazily-evaluated computed value.
The getter is only called when the computed value is read and one of
its dependencies has changed since the last evaluation. If nothing has
changed the cached value is returned without re-running getter.
Computed values are read-only; they cannot be set directly.
@param ― getter - Pure function deriving a value from other reactive sources.
Receives the previous value as an optional optimisation hint.
@example
const a = signal(1);
const b = signal(2);
const sum = computed(() => a() + b());
sum(); // → 3
a(10);
sum(); // → 12 (re-evaluated lazily)
computed(() =>
const count: () => number (+1 overload)
count() * 2);
return (
<
section: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
section>
<
p: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLParagraphElement
p><
strong: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
strong>{
const count: Updater & Computed
count}
strong: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
strong> × 2 = <
strong: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
strong>{
const doubled: () => number
doubled}
strong: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
strong>
p: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLParagraphElement
p>
<
button: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement
button
JSX.CustomEventHandlersNamespaced["on:click"]?: JSX.EventHandlerWithOptionsUnion> | undefined
on:
JSX.CustomEventHandlersNamespaced["on:click"]?: JSX.EventHandlerWithOptionsUnion> | undefined
click={() =>
const count: (value: number) => void (+1 overload)
count(
const count: () => number (+1 overload)
count() + 1)}>+1
button: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement
button>{" "}
<
button: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement
button
JSX.CustomEventHandlersNamespaced["on:click"]?: JSX.EventHandlerWithOptionsUnion> | undefined
on:
JSX.CustomEventHandlersNamespaced["on:click"]?: JSX.EventHandlerWithOptionsUnion> | undefined
click={() =>
const count: (value: number) => void (+1 overload)
count(
const count: () => number (+1 overload)
count() - 1)}>−1
button: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement
button>
section: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
section>
);
}
function render(target: Element | DocumentFragment, setup: () => Node | null | undefined): () => void
Mount a node into target with a scoped lifetime.
setup runs inside a detached effectScope. The returned node is appended
to target. Calling the returned unmount removes the node from the DOM,
disposes its Symbol.dispose hook (JSX-created elements carry one), and
tears down every effect / onCleanup registered inside setup.
@example
import { render } from "elements-kit/render";
const unmount = render(document.getElementById("app")!, () => );
// later
unmount();
render(
var document: Document
window.document returns a reference to the document contained in the window.
MDN Reference
document.
Document.getElementById(elementId: string): HTMLElement | null
The getElementById() method of the Document interface returns an Element object representing the element whose id property matches the specified string. Since element IDs are required to be unique if specified, they're a useful way to get access to a specific element quickly.
getElementById("app")!, () => <
function Counter(props: ReactiveProps<{
initial?: number;
}>): JSX$1.Element
Counter
initial?: MaybeReactive | undefined
initial={0} />);
```
* Utility
```tsx
import {
function createMediaQuery(query: string, defaultState?: boolean): Computed
Creates a signal that tracks a CSS media query.
@param ― query The media query string (e.g. '(max-width: 600px)')
@param ― defaultState The default value (for SSR/hydration)
@returns ― Computed that is true if the query matches
createMediaQuery } from "elements-kit/utilities/media-query";
import {
function render(target: Element | DocumentFragment, setup: () => Node | null | undefined): () => void
Mount a node into target with a scoped lifetime.
setup runs inside a detached effectScope. The returned node is appended
to target. Calling the returned unmount removes the node from the DOM,
disposes its Symbol.dispose hook (JSX-created elements carry one), and
tears down every effect / onCleanup registered inside setup.
@example
import { render } from "elements-kit/render";
const unmount = render(document.getElementById("app")!, () => );
// later
unmount();
render } from "elements-kit/render";
const
const isDark: Computed
isDark =
function createMediaQuery(query: string, defaultState?: boolean): Computed
Creates a signal that tracks a CSS media query.
@param ― query The media query string (e.g. '(max-width: 600px)')
@param ― defaultState The default value (for SSR/hydration)
@returns ― Computed that is true if the query matches
createMediaQuery("(prefers-color-scheme: dark)");
const
const isMobile: Computed
isMobile =
function createMediaQuery(query: string, defaultState?: boolean): Computed
Creates a signal that tracks a CSS media query.
@param ― query The media query string (e.g. '(max-width: 600px)')
@param ― defaultState The default value (for SSR/hydration)
@returns ― Computed that is true if the query matches
createMediaQuery("(max-width: 640px)");
function render(target: Element | DocumentFragment, setup: () => Node | null | undefined): () => void
Mount a node into target with a scoped lifetime.
setup runs inside a detached effectScope. The returned node is appended
to target. Calling the returned unmount removes the node from the DOM,
disposes its Symbol.dispose hook (JSX-created elements carry one), and
tears down every effect / onCleanup registered inside setup.
@example
import { render } from "elements-kit/render";
const unmount = render(document.getElementById("app")!, () => );
// later
unmount();
render(
var document: Document
window.document returns a reference to the document contained in the window.
MDN Reference
document.
Document.getElementById(elementId: string): HTMLElement | null
The getElementById() method of the Document interface returns an Element object representing the element whose id property matches the specified string. Since element IDs are required to be unique if specified, they're a useful way to get access to a specific element quickly.
getElementById("app")!, () => (
<
ul: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLUListElement
ul>
<
li: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLLIElement
li>Dark mode: <
strong: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
strong>{
const isDark: Computed
isDark}
strong: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
strong>
li: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLLIElement
li>
<
li: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLLIElement
li>Mobile: <
strong: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
strong>{
const isMobile: Computed
isMobile}
strong: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
strong>
li: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLLIElement
li>
ul: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLUListElement
ul>
));
```
* Async
```tsx
import {
function signal(): Updater & Computed (+1 overload)
Creates a mutable reactive signal.
Read: call with no arguments → returns the current value and
subscribes the active tracking context.
Write: call with a value → updates the signal and schedules
downstream effects if the value changed.
@example
const count = signal(0);
count(); // → 0 (read)
count(1); // write – effects depending on count will re-run
count(); // → 1
signal } from "elements-kit/signals";
import {
function async(fn: MaybeReactive<(input: TInput) => Promise>): Async & ((...args: any[]) => TOutput | undefined)
Create an
Async
that is also callable as a signal: invoking it
(with no args) reads the current result, so it drops into any reactive
context that expects a zero-arg getter.
@example
import { async } from "elements-kit/utilities/async";
const load = async((id: string) => fetch(`/u/${id}`).then(r => r.json()));
load.run("alice");
// Read as a signal — subscribes to result changes
effect(() => console.log(load()));
await load; // Await the current run — works like a normal promise
async } from "elements-kit/utilities/async";
import {
function render(target: Element | DocumentFragment, setup: () => Node | null | undefined): () => void
Mount a node into target with a scoped lifetime.
setup runs inside a detached effectScope. The returned node is appended
to target. Calling the returned unmount removes the node from the DOM,
disposes its Symbol.dispose hook (JSX-created elements carry one), and
tears down every effect / onCleanup registered inside setup.
@example
import { render } from "elements-kit/render";
const unmount = render(document.getElementById("app")!, () => );
// later
unmount();
render } from "elements-kit/render";
const
const query: Updater & Computed
query =
signal(initialValue: string): Updater & Computed (+1 overload)
Creates a mutable reactive signal.
Read: call with no arguments → returns the current value and
subscribes the active tracking context.
Write: call with a value → updates the signal and schedules
downstream effects if the value changed.
@example
const count = signal(0);
count(); // → 0 (read)
count(1); // write – effects depending on count will re-run
count(); // → 1
signal("hello");
const
const search: Async & ((...args: any[]) => any)
search =
async(fn: MaybeReactive<(input: any) => Promise>): Async & ((...args: any[]) => any)
Create an
Async
that is also callable as a signal: invoking it
(with no args) reads the current result, so it drops into any reactive
context that expects a zero-arg getter.
@example
import { async } from "elements-kit/utilities/async";
const load = async((id: string) => fetch(`/u/${id}`).then(r => r.json()));
load.run("alice");
// Read as a signal — subscribes to result changes
effect(() => console.log(load()));
await load; // Await the current run — works like a normal promise
async(async () => {
const
const res: Response
res = await
function fetch(input: RequestInfo | URL, init?: RequestInit): Promise
MDN Reference
fetch(`/api/search?q=${
const query: () => string (+1 overload)
query()}`);
return
const res: Response
res.
Body.json(): Promise
MDN Reference
json();
}).
Async.start(...args: [] | [input: any]): Async & ((...args: any[]) => any)
Starts a new reactive async operation, stopping any currently active one.
start();
function render(target: Element | DocumentFragment, setup: () => Node | null | undefined): () => void
Mount a node into target with a scoped lifetime.
setup runs inside a detached effectScope. The returned node is appended
to target. Calling the returned unmount removes the node from the DOM,
disposes its Symbol.dispose hook (JSX-created elements carry one), and
tears down every effect / onCleanup registered inside setup.
@example
import { render } from "elements-kit/render";
const unmount = render(document.getElementById("app")!, () => );
// later
unmount();
render(
var document: Document
window.document returns a reference to the document contained in the window.
MDN Reference
document.
Document.getElementById(elementId: string): HTMLElement | null
The getElementById() method of the Document interface returns an Element object representing the element whose id property matches the specified string. Since element IDs are required to be unique if specified, they're a useful way to get access to a specific element quickly.
getElementById("app")!, () => (
<
section: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
section>
<
input: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement
input
JSX.InputHTMLAttributes.value?: JSX.FunctionMaybe
value={
const query: Updater & Computed
query}
JSX.CustomEventHandlersNamespaced["on:input"]?: JSX.EventHandlerWithOptionsUnion> | undefined
on:
JSX.CustomEventHandlersNamespaced["on:input"]?: JSX.EventHandlerWithOptionsUnion> | undefined
input={(
e: InputEvent & {
currentTarget: HTMLInputElement;
target: HTMLInputElement;
}
e) =>
const query: (value: string) => void (+1 overload)
query((
e: InputEvent & {
currentTarget: HTMLInputElement;
target: HTMLInputElement;
}
e.
target: EventTarget & HTMLInputElement
The read-only target property of the Event interface is a reference to the object onto which the event was dispatched. It is different from Event.currentTarget when the event handler is called during the bubbling or capturing phase of the event.
MDN Reference
target as
interface HTMLInputElement
The HTMLInputElement interface provides special properties and methods for manipulating the options, layout, and presentation of elements.
MDN Reference
HTMLInputElement).
HTMLInputElement.value: string
The value property of the HTMLInputElement interface represents the current value of the element as a string.
MDN Reference
value)}
/>
<
p: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLParagraphElement
p>state: <
strong: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
strong>{() =>
const search: Async & ((...args: any[]) => any)
search.
Async.state: "pending" | "fulfilled" | "rejected"
state}
strong: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
strong>
p: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLParagraphElement
p>
<
pre: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLPreElement
pre>{() =>
var JSON: JSON
An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.
JSON.
JSON.stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string (+1 overload)
Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
@param ― value A JavaScript value, usually an object or array, to be converted.
@param ― replacer An array of strings and numbers that acts as an approved list for selecting the object properties that will be stringified.
@param ― space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
@throws ― {TypeError} If a circular reference or a BigInt value is found.
stringify(
const search: Async & ((...args: any[]) => any)
search.
Async.value: any
value, null, 2)}
pre: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLPreElement
pre>
section: WithJsxNamespaces>
@url ― https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section
@url ― https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
section>
));
```
* Custom element
```tsx
import {
function reactive(source?: (self: This) => Signal): (_target: unknown, context: ClassFieldDecoratorContext) => (this: This, initialValue: Value) => Value
A decorator that makes a class field reactive by automatically wrapping its value in a signal.
The field behaves like a normal property (get/set) but reactivity is tracked under the hood.
Any reads will subscribe to the signal and any writes will trigger updates.
@example
class Counter {
\@reactive() count: number = 0;
}
const counter = new Counter();
counter.count++; // Triggers reactivity
console.log(counter.count); // Subscribes to changes
@remarks ―
Equivalent to manually creating a private signal and getter/setter:
class Counter {
#count = signal(0);
get count() { return this.#count(); }
set count(value) { this.#count(value); }
}
reactive,
function computed(getter: (previousValue?: T) => T): () => T
Creates a lazily-evaluated computed value.
The getter is only called when the computed value is read and one of
its dependencies has changed since the last evaluation. If nothing has
changed the cached value is returned without re-running getter.
Computed values are read-only; they cannot be set directly.
@param ― getter - Pure function deriving a value from other reactive sources.
Receives the previous value as an optional optimisation hint.
@example
const a = signal(1);
const b = signal(2);
const sum = computed(() => a() + b());
sum(); // → 3
a(10);
sum(); // → 12 (re-evaluated lazily)
computed } from "elements-kit/signals";
import {
function attributes HTMLElement>(target: AttributeTarget, context: ClassDecoratorContext): AttributeDecorated
A class decorator that automatically wires up observedAttributes and attributeChangedCallback
from a static [ATTRIBUTES] map.
The this type inside attribute handlers is automatically inferred from the decorated class.
@example
\@attributes
class MyElement extends HTMLElement {
static [ATTRIBUTES] = {
count(this: MyElement, value: string | null) {
this.count = Number(value);
},
};
}
attributes,
const ATTRIBUTES: typeof attr
export ATTRIBUTES
Static-field key used by the @attributes decorator (and
dispatchAttrChange
/
observedAttributes
) to locate the
attribute handler map on a custom-element class.
@example
class MyElement extends HTMLElement {
static [ATTRIBUTES]: Attributes = {
name(value) { this.name = value ?? ""; },
};
}
ATTRIBUTES as
const attr: typeof attr
Static-field key used by the @attributes decorator (and
dispatchAttrChange
/
observedAttributes
) to locate the
attribute handler map on a custom-element class.
@example
class MyElement extends HTMLElement {
static [ATTRIBUTES]: Attributes = {
name(value) { this.name = value ?? ""; },
};
}
attr } from "elements-kit/attributes";
import {
function render(target: Element | DocumentFragment, setup: () => Node | null | undefined): () => void
Mount a node into target with a scoped lifetime.
setup runs inside a detached effectScope. The returned node is appended
to target. Calling the returned unmount removes the node from the DOM,
disposes its Symbol.dispose hook (JSX-created elements carry one), and
tears down every effect / onCleanup registered inside setup.
@example
import { render } from "elements-kit/render";
const unmount = render(document.getElementById("app")!, () => );
// later
unmount();
render } from "elements-kit/render";
@
function attributes HTMLElement>(target: AttributeTarget, context: ClassDecoratorContext): AttributeDecorated
A class decorator that automatically wires up observedAttributes and attributeChangedCallback
from a static [ATTRIBUTES] map.
The this type inside attribute handlers is automatically inferred from the decorated class.
@example
\@attributes
class MyElement extends HTMLElement {
static [ATTRIBUTES] = {
count(this: MyElement, value: string | null) {
this.count = Number(value);
},
};
}
attributes
class
class CounterElement
CounterElement extends
var HTMLElement: {
new (): HTMLElement;
prototype: HTMLElement;
}
The HTMLElement interface represents any HTML element. Some elements directly implement this interface, while others implement it via an interface that inherits it.
MDN Reference
HTMLElement {
static [
const attr: typeof attr
Static-field key used by the @attributes decorator (and
dispatchAttrChange
/
observedAttributes
) to locate the
attribute handler map on a custom-element class.
@example
class MyElement extends HTMLElement {
static [ATTRIBUTES]: Attributes = {
name(value) { this.name = value ?? ""; },
};
}
attr] = {
function initial(this: CounterElement, v: string | null): void
initial(
this: CounterElement
this:
class CounterElement
CounterElement,
v: string | null
v: string | null) {
this.
CounterElement.count: number
count =
var Number: NumberConstructor
(value?: any) => number
An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.
Number(
v: string | null
v ?? 0);
},
};
@
reactive