v0.21

frontend components

introduction

dframework ships a complete library of zero dependency, high performance custom web components compiled into the frontend bundle. components are standard custom elements with no virtual dom, no compiler requirements, and no client build step. the framework compiles and bundles component files in public/js/components/ automatically on server boot.

built in components

d-checkbox

form associated checkbox switch with synchronized label and native form submission integration.

1<d-checkbox text="enable notifications" checked name="notifications" value="1"></d-checkbox>

attributes and properties

attribute property type default form description
checked checked boolean false yes checked state reflected in form submission
text text string "checkbox" no label text displayed beside the toggle
name name string "" no input field name used during form submission
value value string "on" no form submission payload sent when checked

programmatic api

1const checkbox = select('d-checkbox');
2
3// get or set checked state
4checkbox.checked = true;
5
6// update label text
7checkbox.text = 'allow telemetry';
8
9// update form payload name and value
10checkbox.name = 'telemetry';
11checkbox.value = 'accepted';

events

event detail bubbles description
change boolean (checked state) yes dispatched whenever the user toggles the checkbox

behavior


d-color-picker

high precision color picker featuring 2d saturation and brightness controls, hue slider, and alpha transparency controls.

1<d-color-picker value="#3b82f6" name="theme_color" label="accent color"></d-color-picker>

attributes and properties

attribute property type default form description
value value string "#d3ac5f" yes current hex color value (supports 3, 4, 6, 8 digit hex)
name name string "" no input name attribute for form submission
id id string "" no element identifier
label label string "color picker" no header label displayed above the picker
opened opened boolean false no whether the popup picker panel is initially open

programmatic api

1const picker = select('d-color-picker');
2
3// toggle popup panel visibility
4picker.toggle();
5
6// programmatically set hex color
7picker.setHex('#ef4444');
8
9// read color state
10console.log(picker.hex); // current hex string
11console.log(picker.h, picker.s, picker.b, picker.a); // hsb and alpha values

events

event detail bubbles description
change hex string yes dispatched when color changes via drag, input, or setHex
input hex string yes dispatched continuously during dragging

behavior


d-combobox

searchable, extensible select combobox with automatic floating placement, keyboard navigation, and dynamic option injection.

1<d-combobox
2 placeholder="select department"
3 allow-search
4 allow-input
5 name="department">
6 <option value="eng" selected>engineering</option>
7 <option value="des">product design</option>
8 <option value="ops">operations</option>
9</d-combobox>

attributes and properties

attribute property type default form description
placeholder placeholder string "" no placeholder text displayed when no option is selected
value value string null yes currently selected option value
allow-search allowSearch boolean false no enables real time search filtering input
allow-input allowInput boolean false no allows adding custom options via input and plus button
horizontal isHorizontal boolean false no switches option layout from vertical list to horizontal chips
empty-text emptyText string "no results" no custom empty state message displayed when no options match
name name string "" no form field name
id id string "" no component id
options options array [] no array of {value, text, content, html} objects (set html: true for rich content)

programmatic api

1const combo = select('d-combobox');
2
3// add option dynamically
4combo.addOption('mktg', 'marketing');
5
6// remove option by value
7combo.removeOption('ops');
8
9// clear all options
10combo.clearOptions();
11
12// set selected value programmatically
13combo.value = 'eng';
14
15// close all open comboboxes across document
16dCombobox.closeAll();

events

event detail bubbles description
change selected value string yes dispatched when an option is selected or added
open none yes dispatched when the dropdown panel opens
close none yes dispatched when the dropdown panel closes

behavior


d-context-menu

contextual right click menu with automatic boundary clamping and outside click suppression.

1<div class="card p-2">
2 right click inside this card
3 <d-context-menu>
4 <span>copy link</span>
5 <span>duplicate item</span>
6 <span>delete</span>
7 </d-context-menu>
8</div>

attributes and properties

attribute property type default form description
opened opened boolean false no whether the context menu is open
parent parent string "" no selector for target parent element (defaults to parent element)

programmatic api

1const menu = select('d-context-menu');
2
3// open context menu programmatically (optionally passing pointer event for cursor positioning)
4menu.open();
5menu.open(pointerEvent);
6
7// close context menu
8menu.close();
9
10// toggle context menu
11menu.toggle();
12
13// listen to item selection (e.detail is the clicked element)
14listen(menu, 'click', (e) => {
15 const target = e.target;
16 const action = target.closest('[data-action]')?.getAttribute('data-action');
17 if (action) handleAction(action);
18});

events

event detail bubbles description
click clicked element yes emitted through component emitter when an item is selected
open none yes dispatched when context menu opens
close none yes dispatched when context menu closes

behavior


d-drawer

offcanvas navigation drawer with animated entrance transitions and wave styling.

1<d-drawer direction="left" id="main-drawer">
2 <nav class="flex-column g-1 p-2">
3 <a href="/dashboard">dashboard</a>
4 <a href="/settings">settings</a>
5 </nav>
6</d-drawer>

attributes and properties

attribute property type default form description
opened opened boolean false no whether the drawer is open
direction direction string "left" no entrance edge ("left", "right", "top", "bottom")

programmatic api

1const drawer = select('d-drawer');
2
3// open drawer (returns promise that resolves after animation completes)
4await drawer.open();
5
6// close drawer (returns promise)
7await drawer.close();
8
9// toggle drawer
10await drawer.toggle();
11
12// inspect animation and open state
13console.log(drawer.isOpened, drawer.isAnimating);

events

event detail bubbles description
open none yes dispatched when the opening animation completes
close none yes dispatched when the closing animation completes

behavior


d-dropdown

collapsible accordion dropdown container, perfect for faqs.

1<d-dropdown header="account security">
2 <div class="p-1 flex-column g-1">
3 <d-checkbox text="two factor authentication"></d-checkbox>
4 <a href="/sessions" class="btn btn-sm">manage active sessions</a>
5 </div>
6</d-dropdown>

attributes and properties

attribute property type default form description
header header string "dropdown" no title text displayed on the clickable header bar
opened opened boolean false no whether the dropdown is expanded

programmatic api

1const dropdown = select('d-dropdown');
2
3// open dropdown
4dropdown.open();
5
6// close dropdown
7dropdown.close();
8
9// toggle dropdown
10dropdown.toggle();
11
12// inspect open state
13console.log(dropdown.isOpened);

events

event detail bubbles description
open none yes dispatched when the dropdown panel opens
close none yes dispatched when the dropdown panel closes

behavior


d-file-input

drag and drop file upload container with interactive item list, formatted byte size calculations, and compact bar modes.

1<d-file-input accept=".pdf,.zip,.csv" name="documents" multiple compact></d-file-input>

attributes and properties

attribute property type default form description
accept accept string "*/*" no allowed mime types or file extensions
icon icon string "dstrn-folder-line" no icon class displayed in dropzone
placeholder placeholder string "drag & drop file" no primary dropzone title
subtitle subtitle string "or click to browse" no secondary helper subtitle
compact compact boolean true no renders as a slim horizontal bar when true
multiple multiple boolean false no allows selecting and dropping multiple files
name name string "" no form payload field name
id id string "" no component id
value value object/array null yes File, File[], or url string payload

programmatic api

1const fileInput = select('d-file-input');
2
3// reset dropzone, clearing value, file list, and native input
4fileInput.reset();
5
6// inspect selected files
7console.log(fileInput.value); // File instance or File[] array

events

event detail bubbles description
change File, File[], or null yes dispatched when files are selected, dropped, or deleted

behavior


d-form

ajax form container with automatic csrf token injection, client side validation, inline field error rendering, and response routing.

1<d-form action="/register" method="POST" rules='{"email": "required|email"}' class="flex-column g-1">
2 <input type="email" name="email" placeholder="email address">
3 <input type="password" name="password" placeholder="password">
4 <button type="submit" class="btn">register</button>
5</d-form>

attributes and properties

attribute property type default form description
action action string "" no submission destination url
method method string "POST" no http request method (GET, POST, PUT, DELETE)
rules rules string "" no json string of client side validation rules
target target string "" no css selector of element to swap when server returns html
navigate navigate boolean false no updates browser history and uses spa router when html is returned
force-reload forceReload string "" no reload condition ("", "true", "all", "success", "error")
callback callback string "" no name of global window function executed after successful response

programmatic api

1const form = select('d-form');
2
3// submit form programmatically
4form.submit();
5
6// retrieve form data
7const formData = form.serialize();
8
9// display validation errors manually
10form.showErrors({ email: ['invalid address'] });
11
12// clear active errors
13form.clearErrors();
14
15// disable or enable form controls
16form.setDisabled(true);

events

event detail bubbles description
d-submit none yes cancelable, dispatched before submission starts
d-success object or string yes dispatched when request succeeds
d-error object yes dispatched when request fails or validation errors occur
d-response object or string yes dispatched on every completed response

behavior

response lifecycle

controller return status / type default behavior (no force-reload) behavior with force-reload
return json({ data }) 200 (application/json) parses payload, dispatches d-success and d-response, executes callback if defined, leaves form on page without reload reloads page if set to force-reload, all, or success (ignored on error)
return json({ redirect }) 200 (application/json) navigates via dSPA.navigate(redirect) without reload (executes full page redirect if force: true or X-Redirect-Force is set) navigates via spa router (or hard redirect if force: true)
return json().withErrors(errors) 422 (application/json) marks inputs with .error, injects .error-message below fields (or .error-banner at top), dispatches d-error and d-response reloads page if set to force-reload, all, or error (retains inline errors on success)
validate() failure 422 (application/json) renders field validation errors inline, dispatches d-error and d-response, keeps form state intact reloads page if set to force-reload, all, or error (retains inline errors on success)
return abort(status, message) 4xx or 5xx (application/json) displays .error-banner at top of form, dispatches d-error and d-response, reenables form inputs reloads page if set to force-reload, all, or error (displays banner on success)
return render(view, data) 200 (text/html) swaps returned markup into target selector (default body), runs scripts, dispatches d-success (updates url if navigate is set) reloads page if set to force-reload, all, or success (proceeds with swap on error)
unexpected server error 500 (text/html or application/json) renders full 500 error page via document.write for html (displays error banner for json) displays 500 error page for html (reloads page for json if set to all or error)
unexpected 404 error 404 (text/html or application/json) swaps target with 404 error template for html (displays error banner for json) reloads page if set to all or error (preserves view on success)

d-hamburger

responsive mobile navigation hamburger trigger and full screen navigation overlay with programmable breakpoints and multi level sliding subcategory panels.

1<nav class="nav">
2 <div class="container flex-row align-center justify-between py-1">
3 <a d-link href="/" class="flex-row align-center">
4 <img src="/img/logo.png" alt="logo">
5 </a>
6
7 <div id="nav-links" class="flex-row align-center g-2 md:d-flex d-none">
8 <a d-link href="/features" class="nav-link">features</a>
9 <div d-submenu="framework">
10 <a d-link href="/architecture">architecture</a>
11 <a d-link href="/benchmarks">benchmarks</a>
12 </div>
13 </div>
14
15 <d-hamburger target="#nav-links" breakpoint="md">
16 <div slot="header">
17 <img src="/img/logo.png" class="logo-mark">
18 </div>
19 <div slot="footer">
20 <d-text-input placeholder="search docs"></d-text-input>
21 </div>
22 </d-hamburger>
23 </div>
24</nav>

attributes and properties

attribute property type default form description
opened opened boolean false no whether the overlay is open
breakpoint breakpoint string "md" no breakpoint token (sm, md, lg, xl, xxl, ultra) or media query
target target string "" no selector of desktop container to clone links from
animation animation string "slide-down" no overlay transition (slide-down, fade, slide-right, slide-left)
direction direction string "top" no entrance direction
size size string "1.5em" no hamburger button size in em units
color color string "currentColor" no button stroke color
activecolor activeColor string "var(--accent)" no button color when open
autohidetarget autoHideTarget boolean true no automatically hides desktop target container below breakpoint
title title string "" no fallback header title when slot="header" is omitted

slots

programmatic api

1const hamburger = select('d-hamburger');
2
3// open overlay (returns promise)
4await hamburger.open();
5
6// close overlay (returns promise)
7await hamburger.close();
8
9// toggle overlay
10await hamburger.toggle();
11
12// navigate into a specific subcategory panel programmatically
13hamburger.navigateTo(subPanelElement, 'framework');
14
15// navigate back to previous parent panel
16hamburger.navigateBack();
17
18// close all open hamburger instances across the page
19dHamburger.closeAll();

events

event detail bubbles description
open none yes dispatched when overlay opens
close none yes dispatched when overlay closes

behavior


d-hold-button

press and hold button that prevents accidental triggers of destructive operations by requiring a continuous pointer press.

1<d-hold-button delay="1500" class="btn btn-red" type="submit">
2 hold to delete database
3</d-hold-button>

attributes and properties

attribute property type default form description
delay delay number 800 no hold duration in milliseconds required before triggering action
type type string "button" no button type ("button", "submit")
disabled disabled boolean false no whether the button is disabled

programmatic api

1const holdBtn = select('d-hold-button');
2
3// adjust required hold duration in ms
4holdBtn.delay = 2000;
5
6// toggle disabled state
7holdBtn.disabled = true;

events

event detail bubbles description
click native click event yes dispatched only after holding for the full delay duration

behavior


d-icon-button

icon button wrapper supporting custom icons, sizes, and dynamic theme active colors.

1<d-icon-button icon="dstrn-heart-line" size="2em" color="var(--content-l)" activecolor="var(--red)"></d-icon-button>

attributes and properties

attribute property type default form description
icon icon string "" no css icon class (e.g. dstrn-heart-line, dstrn-search)
size size string "4.2em" no font size of the icon container
color color string "var(--content-l)" no default icon color
activecolor activeColor string "var(--accent)" no active icon color
id id string "" no component id

d-image-input

drag and drop image dropzone with instant client preview and action overlay.

1<d-image-input accept="image/png,image/jpeg" name="avatar" fit="cover" placeholder="drop profile photo"></d-image-input>

attributes and properties

attribute property type default form description
accept accept string "image/png, image/jpeg, image/gif" no allowed image mime types
icon icon string "dstrn-pic-line" no placeholder icon class
placeholder placeholder string "drag & drop image" no dropzone title text
subtitle subtitle string "or click to browse" no dropzone subtitle text
replace-text replaceText string "replace" no text for overlay replace button
delete-text deleteText string "delete" no text for overlay delete button
no-replace noReplace boolean false no hides the replace button when true
no-delete noDelete boolean false no hides the delete button when true
fit fit string "contain" no object-fit CSS property (contain, cover, fill)
name name string "" no form payload field name
id id string "" no component id
value value object/string null yes File object or image URL string

programmatic api

1const imgInput = select('d-image-input');
2
3// reset dropzone and clear image preview
4imgInput.reset();
5
6// get or set preview src directly
7imgInput.preview = '/img/avatar.png';
8
9// read selected File instance
10console.log(imgInput.value);

events

event detail bubbles description
change File or null yes dispatched when an image is selected, dropped, or deleted

behavior


d-loader

high performance canvas arc spinner with automatic viewport intersection pause.

1<d-loader style="width: 2em; height: 2em;"></d-loader>

attributes and properties

attribute property type default form description
color color string "" no stroke color (defaults to --accent or computed text color)

programmatic api

1const loader = select('d-loader');
2
3// destroy loader and disconnect observers
4loader.destroy();

behavior


client side navigation link component and directive that intercepts clicks, fetches content asynchronously, and performs dom swaps without full page reloads.

1<!-- custom element -->
2<d-link href="/dashboard" class="nav-link">dashboard</d-link>
3
4<!-- anchor directive alias -->
5<a d-link href="/settings" target="#settings-panel" mode="inner">settings</a>

attributes and properties

attribute property type default form description
href href string "" no target destination url
target target string "" no comma separated css selectors of elements to swap in current page
src src string "" no comma separated css selectors of elements to extract from fetched page
mode mode string "inner" no content swap mode ("inner", "outer", "append", "prepend")
preserve-scroll preserveScroll boolean false no preserves window scroll position across navigation
d-full-reload dFullReload boolean false no bypasses client side router and triggers standard browser reload

programmatic api

1// navigate programmatically using dSPA
2await dSPA.navigate('/dashboard', {
3 targets: ['#content'],
4 mode: 'inner',
5 preserveScroll: true
6});

behavior


d-modal

modal dialog container with page scroll locking and backdrop dismiss protection.

1<d-modal id="confirm-modal" persistent>
2 <div class="flex-column g-1">
3 <h3>confirm deletion</h3>
4 <p class="text-content-l">this action cannot be undone.</p>
5 <div class="flex-row justify-end g-1 mt-1">
6 <button class="btn btn-sm" onclick="this.closest('d-modal').close()">cancel</button>
7 <button class="btn btn-sm btn-red">delete</button>
8 </div>
9 </div>
10</d-modal>

attributes and properties

attribute property type default form description
opened opened boolean false no whether the modal is currently visible
persistent persistent boolean false no whether the element remains in the dom when closed via backdrop click

programmatic api

1const modal = select('d-modal');
2
3// open modal (locks body scroll)
4modal.open();
5
6// close modal (runs exit animation and unlocks body scroll)
7modal.close();
8
9// close, await exit transition, and remove element from dom
10await modal.destroy();

events

event detail bubbles description
open none yes dispatched when modal opens
close none yes dispatched when modal closes

behavior and styling boundaries


d-morph

physics based morphing animation engine (dMotion) that transitions between named elements anywhere in the document with continuous geometry, border-radius, box-shadow, and form state interpolation.

1<d-morph name="summary-card" d-action="click" d-to="detail-card" d-duration="350" d-visible>
2 <div class="card p-2">
3 <h4>project summary</h4>
4 <p>click to expand details</p>
5 </div>
6</d-morph>
7
8<d-morph name="detail-card" d-action="click" d-to="summary-card" d-duration="350">
9 <div class="card p-4">
10 <h4>detailed project view</h4>
11 <p>full metadata and extended controls</p>
12 </div>
13</d-morph>

attributes and properties

attribute property type default description
name name string "" unique morph identifier registered in window.dMotion
d-visible none boolean false marks this morph element as initially active and visible
d-action none string "" trigger actions: "click", "long-press", "click-out"
d-to none string "" target morph name to transition to
d-duration none number computed transition duration in ms (computed from distance if omitted)

programmatic api (window.dMotion)

1// get a morph element instance by name
2const morph = dMotion.get('summary-card');
3
4// get the immediately previous morph element
5const prev = dMotion.getPrevious();
6
7// programmatically trigger a transition between two named elements
8await dMotion.transition('summary-card', 'detail-card', optionalTriggerNode);
9
10// register transition lifecycle hooks ('before' or 'after')
11dMotion.listen('summary-card', 'detail-card', () => {
12 console.log('transition starting');
13}, 'before');
14
15// unregister hook
16dMotion.unlisten('summary-card', 'detail-card', callback, 'before');
17
18// component methods on <d-morph>
19morph.show();
20morph.hide();

behavior


d-notification

toast notification component with linear progress bar countdown and automated exit lifecycle.

1<d-notification timer="4000" opened>
2 <p>project settings saved successfully</p>
3</d-notification>

attributes and properties

attribute property type default form description
opened opened boolean false no whether the toast notification is displayed
timer timer number 0 no auto dismissal duration in milliseconds (0 disables timer)

programmatic api

1const toast = select('d-notification');
2
3// show toast and start progress bar countdown
4toast.show();
5
6// hide toast
7toast.hide();
8
9// hide, wait for animation to finish, and remove from dom
10await toast.destroy();

events

event detail bubbles description
open none yes dispatched when notification opens
close none yes dispatched when notification closes

behavior


d-skeleton

content loading placeholder shapes with customizable dimensions, line counts, and card presets.

1<d-skeleton type="text" lines="3" gap="0.5em"></d-skeleton>
2<d-skeleton type="circle" size="3em"></d-skeleton>
3<d-skeleton type="rect" width="100%" height="150px" radius="0.5em"></d-skeleton>
4<d-skeleton type="card"></d-skeleton>

attributes and properties

attribute property type default description
type type string "text" preset shape ("text", "circle", "rect", "card")
lines lines number 1 line count for "text" type (last line renders at 60% width)
width width string null CSS width
height height string null CSS height
size size string null shorthand for equal width and height on circle and rect
gap gap string "0.6em" vertical gap between lines in text mode
radius radius string null custom border-radius override

d-slider

range slider with pointer dragging, keyboard navigation, audio feedback, and automatic indicator text binding.

1<d-slider id="volume" name="volume" min="0" max="1" step="0.01" value="0.75"></d-slider>
2<span data-slider-volume class="text-content-l">75%</span>

attributes and properties

attribute property type default form description
value value number 0.5 yes current numeric slider value
min min number 0 no minimum allowable value
max max number 1 no maximum allowable value
step step number 0.01 no step increment for dragging and arrow keys
name name string "" no form payload field name
id id string "" no component id (used for data-slider-{id} text binding)
disabled disabled boolean false no disables slider interactions
thumbcolor thumbColor string "var(--accent)" no thumb color variable
fillcolor fillColor string "var(--accent-d)" no active track fill color variable
trackcolor trackColor string "var(--border-muted)" no inactive track background color variable
sfx none string embedded no audio sound effect URL or base64 audio payload

programmatic api

1const slider = select('d-slider');
2
3// read current value
4const current = slider.getValue();
5
6// set new value (updates visual fill, thumb position, and data-slider indicator)
7slider.setValue(0.85);

events

event detail bubbles description
input numeric value yes dispatched during continuous dragging and arrow key movement
change numeric value yes dispatched on pointer up or keyboard commit

behavior


d-text-input

enhanced input field with icon prefixes, keyboard shortcut binding, number stepper buttons, and form integration.

1<d-text-input
2 placeholder="search records"
3 icon="dstrn-search-line"
4 shortcut="cmd+k"
5 name="query">
6</d-text-input>

attributes and properties

attribute property type default form description
placeholder placeholder string "" no placeholder text
icon icon string "" no icon class name
type type string "text" no input type ("text", "password", "search", "number")
autocomplete autocomplete boolean false no browser autocomplete flag
value value string "" yes current input string value
name name string "" no form field name
id id string "" no component id
shortcut shortcut string "" no global keyboard shortcut (e.g. "cmd+k", "ctrl+f")
disabled disabled boolean false no disables input
readonly readonly boolean false no sets input to read only mode
min min string "" no minimum number value
max max string "" no maximum number value
step step string "1" no step value for number controls
endpoint endpoint string "" no live input debounced fetch endpoint
callback callback function null no callback function invoked during live input updates

programmatic api

1const input = select('d-text-input');
2
3// update value
4input.value = 'search term';
5
6// read or modify properties
7input.placeholder = 'type command';
8input.disabled = false;

events

event detail bubbles description
input string value yes dispatched on keystroke input
change string value yes dispatched on input modification

behavior


d-toggle

segmented toggle control with magnetic hover indicators and animated transitions.

1<d-toggle name="billing_period" value="0">
2 <div default>monthly billing</div>
3 <div>annual billing (save 20%)</div>
4</d-toggle>

attributes and properties

attribute property type default form description
value value number 0 yes zero indexed integer of currently selected option
name name string "" no form field name
id id string "" no component id
color color string "var(--container-l)" no container background color variable
activecolor activeColor string "var(--accent)" no active option highlight color
textcolor textColor string "var(--content-l)" no inactive option text color
textactivecolor textActiveColor string "" no active option text color
bgcolor bgColor string "var(--container-l)" no base background color
hovercolor hoverColor string "rgba(255, 255, 255, 0.05)" no hover background color

programmatic api

1const toggle = select('d-toggle');
2
3// get currently active button element
4console.log(toggle.selected);
5
6// activate option by index (number), string index, or button element
7toggle.selected = 1;
8
9// read or set numeric value
10toggle.value = 0;

events

event detail bubbles description
change selected index number yes dispatched when active selection changes

behavior


overriding built in components

every built in component behavior can be customized, extended, or completely overwritten by application code. to override a built in component, place a file with the exact same component filename under public/js/components/ (such as public/js/components/dCheckbox.js).

when compiling the frontend bundle, the compiler automatically detects the user file and replaces the built in component implementation in place.

1// public/js/components/dCheckbox.js
2class dCheckbox extends dComponent {
3 static form = true;
4 static get tag() { return 'd-checkbox'; }
5
6 // custom template or methods
7}
8
9dComponent.define(dCheckbox);

immutable base class

all custom elements and overrides extend dComponent. unlike UI components, dComponent is the immutable framework base class providing reactivity, reflection, and lifecycle management. the compiler always preserves the core dComponent implementation at the root of the bundle so all components inherit from it consistently.


dComponent (base class)

dComponent is the authoring base class for all custom web components in dframework. it provides property reflection, reactive proxy state, form integration, surgical dom rendering, and automatic memory cleanup.

1class dCounter extends dComponent {
2 static tag = 'd-counter';
3 static form = true; // opt into native form submission integration
4
5 static props = {
6 value: { type: 'number', default: 0, form: true },
7 step: { type: 'number', default: 1 },
8 disabled: { type: 'boolean', default: false },
9 label: { type: 'string', default: 'count' }
10 };
11
12 template() {
13 // called once on mount to establish initial innerHTML
14 return `
15 <span class="label">${this.label}</span>
16 <button class="dec" type="button">−</button>
17 <span class="value">${this.value}</span>
18 <button class="inc" type="button">+</button>
19 `;
20 }
21
22 mount() {
23 // called once after template initialization
24 this.effect(() => {
25 // reading this.state or props registers auto dependencies
26 document.title = `count: ${this.state.value ?? this.value}`;
27 return () => { document.title = 'app'; }; // optional cleanup
28 });
29 }
30
31 render() {
32 // called automatically on prop or state changes
33 // use for surgical dom updates only (never mutate this.state here)
34 this.refs('.value')[0].textContent = this.state.value ?? this.value;
35 this.refs('button.dec')[0].disabled = this.disabled;
36 this.refs('button.inc')[0].disabled = this.disabled;
37
38 // listeners attached inside render() are cleared and rebound automatically
39 this.listen(this.refs('button.dec')[0], 'click', () => this.decrement());
40 this.listen(this.refs('button.inc')[0], 'click', () => this.increment());
41 }
42
43 onPropChanged(name, oldVal, newVal) {
44 // called on attribute or property modifications
45 }
46
47 increment() {
48 this.setState(s => { s.value = (s.value ?? this.value) + this.step; });
49 }
50
51 decrement() {
52 this.setState(s => { s.value = (s.value ?? this.value) - this.step; });
53 }
54}
55
56dComponent.define(dCounter);

component definition and registration

register components using dComponent.define(Class):

1dComponent.define(dCounter);

lifecycle methods

method timing description
template() before mount returns initial HTML string to populate inner dom
mount() on first connection initializes component logic and registers effects
connected() on every connection called whenever element is attached to document
render() on state/prop change performs surgical dom node updates
onPropChanged(name, old, new) on prop mutation reacts to specific property changes
onscreen() on viewport entry called when component enters viewport
offscreen() on viewport exit called when component leaves viewport
destroy() on disconnection teardown hook called when element is removed from dom
NOTE

`mount()` initialization is batched via `requestAnimationFrame` upon initial dom connection. this guarantees that all child nodes parsed in the light dom are present and available before `mount()` executes. for template components, template markup is preparsed on definition and stamped via cached `cloneNode(true)`. subsequent property and state updates are batched into a static 3 phase microtask flush.

fragment caching and batch mounting

for maximum throughput and xss safety when generating dynamic content, setting inner markup, or mounting lists of components, dComponent provides template caching utilities and safe dom replacement:

1// parse html into a cached template fragment (avoids repeated innerHTML string parsing)
2const frag = dComponent.fragment('<div class="list-item"><span>title</span></div>');
3
4// append multiple nodes, fragments, or html strings in a single dom operation
5dComponent.appendMany(container, frag1, frag2, '<div class="extra">item</div>');
6
7// safely set or replace element children using cached template fragments or nodes
8this.setChildren('<div class="card"><span class="title"></span></div>');

rendering strategy and surgical updates

dComponent avoids virtual dom overhead. dom nodes are created once in template() or mount(), while dynamic updates are applied surgically inside render().

IMPORTANT

`this.state` must never be modified inside `render()`. state mutations inside `render()` are blocked to prevent infinite update loops.

batched microtask update queue

property modifications and state mutations are automatically coalesced across all active components into a static 3 phase microtask flush:

  1. phase 1 (attribute sync): synchronizes observed html attributes for changed properties.
  2. phase 2 (rendering): clears transient render listeners and executes render() once per updated component.
  3. phase 3 (effects & form sync): reevaluates reactive effects, invalidates refs caches, synchronizes form internals, and dispatches an aggregated debugbar event.
1// multiple synchronous state mutations trigger exactly one render cycle in the next microtask
2this.state.title = 'new title';
3this.state.count = 42;

reactive state

1// update state and trigger microtask batched render()
2this.setState({ active: true });
3
4// functional updater
5this.setState(s => { s.count++; s.updatedAt = Date.now(); });
6
7// update state without triggering render() (still triggers effect loops)
8this.setState({ timer: Date.now() }, false);

effects and dependency tracking

1// auto tracking effect (tracks any accessed this.state or prop)
2this.effect(() => {
3 console.log('value updated:', this.state.value);
4 return () => console.log('cleaning up effect');
5});
6
7// explicit dependency array effect
8this.effect(() => {
9 fetchData(this.query);
10}, () => [this.query]);

automatic cleanup apis

these methods automatically clean up listeners, timers, and animation frame requests when the component is removed from the dom:

1// event listener returning unlistener function
2const off = this.listen(element, 'click', (e) => this.handleClick(e));
3off(); // manual cleanup if needed
4
5// listen to multiple elements returning composite unlistener function
6const offAll = this.listenAll(elements, 'click', (e) => this.handleClick(e));
7offAll(); // detaches all listeners
8
9// unlisten manually if needed
10this.unlisten(element, 'click', handler);
11
12// auto cleared timers
13this.setTimeout(() => this.tick(), 1000);
14this.setInterval(() => this.refresh(), 5000);
15
16// auto cleared animation frame
17this.requestAnimationFrame((time) => this.draw(time));

dom caching and refs

this.refs(selector) caches queried element arrays between renders to eliminate repeated dom query overhead:

1const [inputEl] = this.refs('input.search');

inner content capture

access original light dom nodes passed into the custom element before template() execution:

1this.originalChildren // array of cloned child nodes
2this.originalHTML // original innerHTML string
3this.cloneChildren() // returns fresh clones of original child nodes

form integration

when static form = true, dComponent integrates with parent <form> elements and submits properties marked with form: true:

1static form = true;
2static props = {
3 value: { type: 'string', default: '', form: true }
4};
5
6// access parent form element
7console.log(this.form);

supports single values, file instances (File), file arrays (File[]), and multiple form properties automatically.

internal event emitter

1// listen to component events returning unlistener function
2const off = this.on('custom-event', (data) => console.log(data));
3off(); // detach listener
4
5// remove listener by callback reference
6this.off('custom-event', handler);
7
8// emit event to internal listeners and dispatch bubbling composed customevent
9this.emit('custom-event', { id: 123 });

websocket integration

1// emit event through global websocket connection
2this.wire('chat:send', { message: 'hello' });