custom-element

Copyright (c) 2026 Sasha Firsov https://github.com/sashafirsov

Declarative Custom Element (DCE) is a part of pure Declarative Web Application stack. A proof of concept as a part of WCCG in Declarative custom elements and Declarative Web Application discussion. NO-JS The functionality of DCE and its data access does not require programming using JavaScript.

It allows to define custom HTML tag with template filled from slots, attributes and data slice as of now from local-storage and location. UI is re-rendered on each data slice change triggered by initialization or DOM event.

git GitHub | Live demo: custom-element | Try in Sandbox | tests project | Chrome devtools pugin

NPM version coverage Published on webcomponents.org

HTTP companion retirement

The standalone http-request.js module and HttpRequestElement export are retired. Remove that script/import and migrate HTTP templates to cem-elements with type="text/cem-ml". Declare {http-request @slice=response @url="…"} in the template; request lifecycle and metadata remain under datadom.slices.response. Its .data is a native CEM document during rendering. Query it through CEM-QL nodes or a separate xpath-functions library, replacing JavaScript paths such as .data.items. JavaScript snapshots contain metadata and data: null.

The HTTP examples and version-picker examples install the shared runtime and ship the required fixtures and function library. The reusable version picker uses package, initialversion, showdate, value and the label slot; migrate legacy package-name to package, current-version to initialversion, and show-date to showdate. The old npm-version-to-url fragment is replaced by the documented URL synchronization composition in the version-picker examples. The shared runtime is included in the package. See the loader contract for ownership, error/reload behavior and the later AST streaming phase.

What is DCE?

DCE provides the next level of abstraction in HTML - native composition. With native implementation which is streaming parser, streaming transformation, multithreading. native assumes the C/Rust compiled code. There is no place for JavaScript except of polyfill and ability to extend DCE, which otherwise has to be native.

The composition assumes the fully functional template and ability to call the template with parameters( custom tag + attributes) .

As the next to HTML abstraction layer - composition, it provides:

After composition the layer of functional component provides

While DCE is no-JS concept, DCE provides the basic declarative constructs to build most of simple apps. Assuming the extending via custom elements and JS. The evolution goal is to adopt most demanded APIs/construct natively into DCE stack over time.

DCE is compatible with closed/open/named root. Enabling as site-scoped styling and registry as encapsulated anonymous scopes in shadow root.

This project is a POC( Proof of Concept ) targeting to become a base for native DCE implementation polyfill.

use

install

use via CDN

NPM, yarn

Enable IDE support

IDE.md

Live demo 🔗

Interactivity via data slice triggered by events

More on slice concept in slice and events demo page

Templating power

comes from XSLT and XPath. Which is natively implemented in all current browsers, globally tested and well documented.

generates HTML

responsive hex grid demo responsive hex-grid demo , look into sources for samples of CSS encapsulation and external template use.

Implementation notes

Life cycle

custom-element declaration

NOTE: attempt to register custom element with already registered tag name would fail due to w3c standard limitations. The scoped custom element registry is still a proposal.

omitting tag leads to template instantiation

Whether template is inline or given by src attribute, the custom-element would be instantiated inline if no tag attribute is given.

custom element instance

constructor creates XML with

DOM content is replaced with results of instance XML transformation by declaration XSLT.

tag attribute

allows to define the Custom Element tag registered by window.customElements.define().

If omitted, the tag is auto-generated and the content is rendered inline.

See demo for tag attribute use.

src attribute

allows to refer either external template or template within external library by #id hash in URL.

The compatibility adapter follows the CEM-ML resource lifecycle through the cem-element external resource loading contract, covering src="#id", src="url", src="url#id", module-map policy, security context, and expected content-type context passed to CEM-ML.

See demo with various samples.

types of template

#id Local reference

allows to refer the template withing same document

url

allows to use the external document as template

url#id

allows to refer the template withing external document

template syntax

Scoped CSS live demo

styles encapsulation

DCE can have the own styles which would be scoped to the instances. In order to prevent the style leaking, it has to be defined withing template tag:

override style for instance

In same way as in DCE itself:

Attributes

To be served by IDE and to track the attributes changes, they have to be declared via attribute:

The curly braces {} in attributes implemented as attribute value template

The names in curly braces are matching the instance attributes. I.e. in XML node /my-component/attributes/.

To access payload XPath could start with /*/payload/. I.e. {/*/payload//label} refers to all label tags in payload.

Slots

<slot name="xxx"> is replaced by payload top elements with slot attribute matching the name, i.e. slot xxx is matching <i slot="xxx">...</i> in payload.

loops, variables

Loop implemented via for-each. See for-each table demo for examples including nested loops and XHTML namespace usage.

Variables in XSLT

XPath

is available in {} in attributes, in for-each, if, value-of, and other XSL tags.

XPath is a selector language to navigate over custom element instance data, attributes, and payload.

XSLT 1.0 — engine retired, legacy templates supported via conversion (0.1.0)

How it works now (0.1.0): the in-browser native XSLT 1.0 transform engine is retired — <custom-element> no longer runs a browser XSLT processor. Instead, legacy HTML+XSLT templates are transpiled to CEM-ML and rendered on the CEM-ML / CEM-QL substrate (CemElementRuntime), the same engine hand-authored type="cem-ml; version=0.0" templates use. A legacy sample and its migrated CEM-ML twin therefore render identically, on one engine.

Supported legacy constructs (auto-detected; the template DOM is parsed with HTML + XSLT namespaces and converted): <xsl:value-of> / {…} interpolation, <xsl:for-each> (incl. the exsl:node-set($var)/* inline-variable idiom, unrolled), <xsl:if> / <xsl:choose> / when / otherwise (and the bare for-each/if/choose spellings), <xsl:variable>, <slot>, declaration <attribute> / <slice>, attribute value templates, and the XPath function subset the legacy demos use (contains, not, translate, substring(-before/-after), string-length, position(), count, concat, normalize-space, hasBoolAttribute, comparisons). Bare {name} / {$name} / {//path} interpolation and ?? coalescing are supported.

Not converted (Tier 3, deferred): XSLT outside the bounded compatibility profile, including EXSLT func:function, <msxsl:script> (embedded JScript — non-transpilable), and dynamic construction names outside the scalar AVT subset. These emit a conversion diagnostic. Author such logic in CEM-ML/CEM-QL directly.

The historical XSLT 1.0 reference below documents the original authoring surface; most of it is reproduced by the converter. The original POC used the native XSLT 1.0 processor — that engine dependency is what 0.1.0 retires, not the authoring dialect.

<template lang="custom-element-v0"> remains a deprecated browser compatibility selector through the next-major migration window. Untyped legacy templates on <custom-element> are normalized to that selector; both forms lower to canonical CEM-ML and render through the same cem_ql substrate. New authoring should use type="text/cem-ml". The selector may be removed only after retained demos, material fixtures, and downstream generator workflows have canonical CEM-ML replacements and the FF-5 usage inventory reaches its removal gate.

troubleshooting

HTML parser is not compatible with templates

On many tags like table, or link a the attempt to use XSLT operations could lead to DOM order mismatch to given in template. In such cases the xhtml: prefix in front of troubled tag would solve the parsing.

See demo source for detailed sample.

Chrome devtools plugin

@epa-wg/custom-element plugin gives the view into

template debugging

xml and xslt can be saved to file via for "copy string contents" into clipboard.

The XSLT debugger from your favorite IDE can set the breakpoints withing those files and run transformation under debugger.

{} does not give a value