Browser experiments & working notes
Renderer examples / Preact
A custom base.
Preact rendering.
An HTMLElement base class that uses Preact to render into its own shadow root.
for="host"#controlWaiting for Preact to render…
Browser details
- Current path
- Checking browser support…
- Native API surface
- Checking…
- Active adapters
- Waiting for setup…
Automatic checks for the native property. Force fallback loads the selected adapters. Browser alone leaves them out.
Labels & rendering
A new control. The same reference.
The outside label references the component. Preact renders the checkbox inside its shadow root.
Try: Click the label, replace the checkbox, then click the label again. Each replacement starts unchecked.
Popover targeting
An outside button. A rendered panel.
Preact creates a native popover inside another component. The outside buttons reference its host.
Try: Open the popover, then dismiss it with its close button or Escape.
Open an internal popover
How the examples are connected
Each component sets its shadow root’s referenceTarget to the internal target ID. The fallback loads before the component definitions, so it can capture the roots when the renderer creates them and observe later DOM changes.
The page waits for the renderer’s first update before enabling its buttons. Replacing the checkbox changes the component’s revision attribute; the renderer creates a new input with the same ID.
<label for="renderer-checkbox">Send me release notes</label>
<rt-preact-checkbox id="renderer-checkbox" revision="0"></rt-preact-checkbox>
<button type="button" popovertarget="renderer-popover">Open popover</button>
<rt-preact-popover id="renderer-popover"></rt-preact-popover>
The selected setup uses labels({ activation: "focus-and-click", naming: true }) and popoverTargets(). The page’s event listeners only display state; they do not forward label or popover actions.
Component source
preact-element.js
import { render } from "preact";
// The custom element owns its lifecycle and shadow root; Preact owns rendering.
export class PreactElement extends HTMLElement {
static referenceTarget = null;
constructor() {
super();
this.renderRoot = this.attachShadow({ mode: "open" });
this.renderRoot.referenceTarget = this.constructor.referenceTarget;
this.updateComplete = Promise.resolve(false);
this.updatePending = false;
}
connectedCallback() {
this.requestUpdate();
}
disconnectedCallback() {
render(null, this.renderRoot);
}
attributeChangedCallback(name, previousValue, value) {
if (previousValue !== value) this.requestUpdate();
}
requestUpdate() {
if (this.updatePending) return this.updateComplete;
this.updatePending = true;
this.updateComplete = Promise.resolve().then(() => {
this.updatePending = false;
// A queued update may outlive a removal. Reconnection schedules a new one.
if (!this.isConnected) return false;
render(this.render(), this.renderRoot);
return true;
});
return this.updateComplete;
}
render() {
return null;
}
}
components.js
import { Fragment, h } from "preact";
import { PreactElement } from "./preact-element.js";
import componentStyles from "../shared/components.css";
import { withRendererTimeout } from "../shared/renderer-readiness.js";
class PreactCheckbox extends PreactElement {
static referenceTarget = "control";
static observedAttributes = ["revision"];
get revision() {
return Number(this.getAttribute("revision")) || 0;
}
set revision(value) {
this.setAttribute("revision", String(value));
}
render() {
return h(Fragment, null,
h("style", null, componentStyles),
h("div", { class: "component-preview" },
h("input", { key: this.revision, id: "control", type: "checkbox", "data-revision": this.revision }),
h("span", { "aria-hidden": "true" }, "Native checkbox"),
),
h("p", { class: "hint" }, `Render revision ${this.revision}`),
);
}
}
class PreactPopover extends PreactElement {
static referenceTarget = "panel";
render() {
return h(Fragment, null,
h("style", null, componentStyles),
h("div", { id: "panel", popover: "auto", role: "dialog", "aria-labelledby": "panel-title" },
h("p", { class: "eyebrow" }, "Preact · shadow DOM"),
h("h2", { id: "panel-title" }, "Rendered with Preact"),
h("p", null, "This native popover lives inside a custom element rendered by Preact."),
h("button", { type: "button", popovertarget: "panel", popovertargetaction: "hide", autofocus: true }, "Close popover"),
),
);
}
}
customElements.define("rt-preact-checkbox", PreactCheckbox);
customElements.define("rt-preact-popover", PreactPopover);
export async function whenReady() {
await withRendererTimeout(Promise.all([
document.getElementById("renderer-checkbox").updateComplete,
document.getElementById("renderer-popover").updateComplete,
]), "Preact");
}
Functional JavaScript sizes
Functional page JavaScript
22.121 KB 8.810 KB gzipAlways loaded.
Fallback additional
23.929 KB 8.430 KB gzipCore + selected adapters.
Fallback route total
46.050 KB 17.240 KB gzipAlways-loaded and fallback files; shared files counted once.
Native-surface route total
23.110 KB 9.348 KB gzipIncludes 0.989 KB (0.538 KB gzip) of behavioral probes requested only after the property surface is present.
Minified / gzip. 1 KB = 1,000 bytes. Demo-only syntax highlighting is excluded from the functional totals and reported separately below.
Whole-page delivery context
HTML document
19.660 KB 1 requestGenerated markup, including these measurements.
Initial supporting assets
18.520 KB raw 17.897 KB local transfer · 3 requestsStyles, icon, and the highlighting scheduler; functional JS is separate above.
Initial page with fallback
84.230 KB raw 54.797 KB local transfer · 9 requestsDocument + functional fallback path + initial supporting assets, with unique files counted once.
Deferred highlighting
16.700 KB raw 7.831 KB local transfer · 8 requestsEngine, theme, and this page’s grammar closure, requested after a source disclosure opens or during idle time.
“Local transfer” matches the included development server: gzip JavaScript sidecars, but uncompressed HTML, CSS, and SVG. GitHub Pages, browser caching, HTTP headers, and production compression can change transferred bytes and request scheduling. Module preloads fetch functional modules early without evaluating the app and do not add duplicate transfers.
Inspect the 6 generated JavaScript files
Selected adapters: labels, popover-targets.
Gzip totals sum each compressed file. The fallback and native-probe routes are mutually exclusive. Separate CSS files, HTML, JSON, source maps, HTTP headers, and Microlighter assets are excluded. Component styles embedded in JavaScript are included. Transfer sizes depend on compression and caching. Microlighter MIT license.
| File | Delivery | Minified | Gzip |
|---|---|---|---|
preact/main.js |
Page | 3.257 KB | 1.352 KB |
shared/chunks/app-HVHZEK6Y.js |
Page | 18.722 KB | 7.320 KB |
shared/chunks/chunk-CLCOZ6O7.js |
Fallback / native probe shared | 0.849 KB | 0.422 KB |
shared/chunks/chunk-S2QLMWTP.js |
Page | 0.142 KB | 0.138 KB |
shared/chunks/detect-X6QVIAY4.js |
Native-surface probe | 0.140 KB | 0.116 KB |
shared/chunks/reference-target.setup-F3BB382F.js |
Fallback additional | 23.080 KB | 8.008 KB |
Renderer integration & limitations
Preact and Reference Target
PreactElement creates the shadow root, batches attribute updates, and unmounts Preact on disconnection. Its subclasses return virtual DOM with h(); a keyed input is replaced when revision changes. Reconnecting schedules a fresh render.
The library and its component code are included in Page JavaScript above. Each renderer is built as an independent page with only the labels and popover-targets adapters in its optional fallback bundle.
These examples use open, client-rendered roots. They do not test server rendering or hydration. The label fallback does not recreate native label.control or input.labels relationships. Readouts describe DOM state; computed names and assistive technology need separate validation.