UNPKG

@zeix/ui-element

Version:

UIElement - minimal reactive framework based on Web Components

56 lines (48 loc) â€ĸ 61.4 kB
<!DOCTYPE html><html class="default" lang="en" data-base="."><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>@zeix/ui-element</title><meta name="description" content="Documentation for @zeix/ui-element"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="assets/style.css"/><link rel="stylesheet" href="assets/highlight.css"/><script defer src="assets/main.js"></script><script async src="assets/icons.js" id="tsd-icons-script"></script><script async src="assets/search.js" id="tsd-search-script"></script><script async src="assets/navigation.js" id="tsd-nav-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><div class="table-cell" id="tsd-search"><div class="field"><label for="tsd-search-field" class="tsd-widget tsd-toolbar-icon search no-caption"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-search"></use></svg></label><input type="text" id="tsd-search-field" aria-label="Search"/></div><div class="field"><div id="tsd-toolbar-links"></div></div><ul class="results"><li class="state loading">Preparing search index...</li><li class="state failure">The search index is not available</li></ul><a href="index.html" class="title">@zeix/ui-element</a></div><div class="table-cell" id="tsd-widgets"><a href="#" class="tsd-widget tsd-toolbar-icon menu no-caption" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-menu"></use></svg></a></div></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><h1>@zeix/ui-element</h1></div><div class="tsd-panel tsd-typography"><a id="uielement" class="tsd-anchor"></a><h1 class="tsd-anchor-link">UIElement<a href="#uielement" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h1><p>Version 0.12.2</p> <p><strong>UIElement</strong> - the HTML-first microframework bringing reactivity to Web Components.</p> <p>UIElement is a set of functions to build reusable, loosely coupled Web Components with reactive properties. It provides structure through components and simplifies state management and DOM synchronization using declarative signals and effects, leading to more organized and maintainable code without a steep learning curve.</p> <p>Unlike SPA frameworks (React, Vue, Svelte, etc.) UIElement takes a HTML-first approach, progressively enhancing sever-rendered HTML rather than recreating (rendering) it using JavaScript. UIElement achieves the same result as SPA frameworks with SSR, but with a simpler, more efficient approach. It works with a backend written in any language or with any static site generator.</p> <a id="key-features" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Key Features<a href="#key-features" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><ul> <li>🧱 <strong>HTML Web Components</strong>: Build on standard HTML and enhance it with encapsulated, reusable Web Components. No virtual DOM – UIElement works directly with the real DOM.</li> <li>đŸšĻ <strong>Reactive Properties</strong>: Define reactive properties for fine-grained, efficient state management (signals). Changes automatically propagate only to the parts of the DOM that need updating, avoiding unnecessary re-renders.</li> <li>🧩 <strong>Function Composition</strong>: Declare component behavior by composing small, reusable functions (attribute parsers and effects). This promotes cleaner code compared to spaghetti code problems that commonly occur when writing low-level imperative code.</li> <li>đŸ› ī¸ <strong>Customizable</strong>: UIElement is designed to be easily customizable and extensible. You can create your own custom attribute parsers and effects to suit your specific needs.</li> <li>🌐 <strong>Context Support</strong>: Share global states across components without prop drilling or tightly coupling logic.</li> <li>đŸĒļ <strong>Tiny footprint</strong>: Minimal core (~4kB gzipped) with tree-shaking support, adding only the necessary JavaScript to enhance your HTML.</li> <li>đŸ›Ąī¸ <strong>Type Safety</strong>: Get early warnings when types don't match, improving code quality and reducing bugs.</li> </ul> <p>UIElement uses <a href="https://github.com/zeixcom/cause-effect">Cause &amp; Effect</a> internally for state management with signals and for scheduled DOM updates. But you could easily rewrite the <code>component()</code> function to use a signals library of your choice or to produce something else than Web Components.</p> <a id="installation" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Installation<a href="#installation" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><pre><code class="bash"><span class="hl-0"># with npm</span><br/><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">install</span><span class="hl-2"> </span><span class="hl-3">@zeix/ui-element</span><br/><br/><span class="hl-0"># or with bun</span><br/><span class="hl-1">bun</span><span class="hl-2"> </span><span class="hl-3">add</span><span class="hl-2"> </span><span class="hl-3">@zeix/ui-element</span> </code><button type="button">Copy</button></pre> <a id="documentation" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Documentation<a href="#documentation" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>The full documentation is still work in progress. The following chapters are already reasonably complete:</p> <ul> <li><a href="https://zeixcom.github.io/ui-element/index.html">Introduction</a></li> <li><a href="https://zeixcom.github.io/ui-element/getting-started.html">Getting Started</a></li> <li><a href="https://zeixcom.github.io/ui-element/building-components.html">Building Components</a></li> <li><a href="https://zeixcom.github.io/ui-element/styling-components.html">Styling Components</a></li> <li><a href="https://zeixcom.github.io/ui-element/data-flow.html">Data Flow</a></li> <li><a href="https://zeixcom.github.io/ui-element/about-community.html">About &amp; Community</a></li> </ul> <a id="basic-usage" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Basic Usage<a href="#basic-usage" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><a id="show-appreciation" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Show Appreciation<a href="#show-appreciation" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Server-rendered markup:</p> <pre><code class="html"><span class="hl-4">&lt;</span><span class="hl-5">show-appreciation</span><span class="hl-2"> </span><span class="hl-6">aria-label</span><span class="hl-2">=</span><span class="hl-7">&quot;Show appreciation&quot;</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;</span><span class="hl-5">button</span><span class="hl-2"> </span><span class="hl-6">type</span><span class="hl-2">=</span><span class="hl-7">&quot;button&quot;</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;</span><span class="hl-5">span</span><span class="hl-2"> </span><span class="hl-6">class</span><span class="hl-2">=</span><span class="hl-7">&quot;emoji&quot;</span><span class="hl-4">&gt;</span><span class="hl-2">💐</span><span class="hl-4">&lt;/</span><span class="hl-5">span</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;</span><span class="hl-5">span</span><span class="hl-2"> </span><span class="hl-6">class</span><span class="hl-2">=</span><span class="hl-7">&quot;count&quot;</span><span class="hl-4">&gt;</span><span class="hl-2">5</span><span class="hl-4">&lt;/</span><span class="hl-5">span</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;/</span><span class="hl-5">button</span><span class="hl-4">&gt;</span><br/><span class="hl-4">&lt;/</span><span class="hl-5">show-appreciation</span><span class="hl-4">&gt;</span> </code><button type="button">Copy</button></pre> <p>UIElement component:</p> <pre><code class="js"><span class="hl-8">import</span><span class="hl-2"> { </span><span class="hl-9">asInteger</span><span class="hl-2">, </span><span class="hl-9">component</span><span class="hl-2">, </span><span class="hl-9">first</span><span class="hl-2">, </span><span class="hl-9">on</span><span class="hl-2">, </span><span class="hl-9">RESET</span><span class="hl-2">, </span><span class="hl-9">setText</span><span class="hl-2"> } </span><span class="hl-8">from</span><span class="hl-2"> </span><span class="hl-3">&quot;@zeix/ui-element&quot;</span><span class="hl-2">;</span><br/><br/><span class="hl-1">component</span><span class="hl-2">(</span><span class="hl-3">&quot;show-appreciation&quot;</span><span class="hl-2">, {</span><br/><span class="hl-2"> </span><span class="hl-9">count:</span><span class="hl-2"> </span><span class="hl-1">asInteger</span><span class="hl-2">(</span><span class="hl-10">RESET</span><span class="hl-2">) </span><span class="hl-0">// Get initial value from .count element</span><br/><span class="hl-2">}, </span><span class="hl-11">el</span><span class="hl-2"> </span><span class="hl-12">=&gt;</span><span class="hl-2"> [</span><br/><br/><span class="hl-2"> </span><span class="hl-0">// Update count display when state changes</span><br/><span class="hl-2"> </span><span class="hl-1">first</span><span class="hl-2">(</span><span class="hl-3">&quot;.count&quot;</span><span class="hl-2">, </span><span class="hl-1">setText</span><span class="hl-2">(</span><span class="hl-3">&quot;count&quot;</span><span class="hl-2">)),</span><br/><br/><span class="hl-2"> </span><span class="hl-0">// Handle click events to change state</span><br/><span class="hl-2"> </span><span class="hl-1">first</span><span class="hl-2">(</span><span class="hl-3">&quot;button&quot;</span><span class="hl-2">, </span><span class="hl-1">on</span><span class="hl-2">(</span><span class="hl-3">&quot;click&quot;</span><span class="hl-2">, () </span><span class="hl-12">=&gt;</span><span class="hl-2"> { </span><span class="hl-9">el</span><span class="hl-2">.</span><span class="hl-9">count</span><span class="hl-13">++</span><span class="hl-2"> }))</span><br/><span class="hl-2">]);</span> </code><button type="button">Copy</button></pre> <p>Example styles:</p> <pre><code class="css"><span class="hl-5">show-appreciation</span><span class="hl-2"> {</span><br/><span class="hl-2"> </span><span class="hl-14">display</span><span class="hl-2">: </span><span class="hl-15">inline-block</span><span class="hl-2">;</span><br/><br/><span class="hl-2"> &amp; button {</span><br/><span class="hl-2"> </span><span class="hl-14">display</span><span class="hl-2">: </span><span class="hl-15">flex</span><span class="hl-2">;</span><br/><span class="hl-2"> </span><span class="hl-14">flex-direction</span><span class="hl-2">: </span><span class="hl-15">row</span><span class="hl-2">;</span><br/><span class="hl-2"> </span><span class="hl-14">gap</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--space-s</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">border</span><span class="hl-2">: </span><span class="hl-18">1</span><span class="hl-19">px</span><span class="hl-2"> </span><span class="hl-15">solid</span><span class="hl-2"> </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--color-border</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">border-radius</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--space-xs</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">background-color</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--color-secondary</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">color</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--color-text</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">padding</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--space-xs</span><span class="hl-2">) </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--space-s</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">cursor</span><span class="hl-2">: </span><span class="hl-15">pointer</span><span class="hl-2">;</span><br/><span class="hl-2"> </span><span class="hl-14">font-size</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--font-size-m</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">line-height</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--line-height-xs</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">transition</span><span class="hl-2">: transform </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--transition-short</span><span class="hl-2">) </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--easing-inout</span><span class="hl-2">);</span><br/><br/><span class="hl-2"> &amp;:hover {</span><br/><span class="hl-2"> background-color: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--color-secondary-hover</span><span class="hl-2">);</span><br/><span class="hl-2"> }</span><br/><br/><span class="hl-2"> &amp;</span><span class="hl-20">:active</span><span class="hl-2"> {</span><br/><span class="hl-2"> </span><span class="hl-14">background-color</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--color-secondary-active</span><span class="hl-2">);</span><br/><br/><span class="hl-2"> .emoji {</span><br/><span class="hl-2"> </span><span class="hl-14">transform</span><span class="hl-2">: </span><span class="hl-16">scale</span><span class="hl-2">(</span><span class="hl-18">1.1</span><span class="hl-2">);</span><br/><span class="hl-2"> }</span><br/><span class="hl-2"> }</span><br/><span class="hl-2"> }</span><br/><span class="hl-2">}</span> </code><button type="button">Copy</button></pre> <a id="tab-group" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Tab Group<a href="#tab-group" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><p>An example demonstrating how to create a fully accessible tab navigation.</p> <p>Server-rendered markup:</p> <pre><code class="html"><span class="hl-4">&lt;</span><span class="hl-5">tab-group</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;</span><span class="hl-5">div</span><span class="hl-2"> </span><span class="hl-6">role</span><span class="hl-2">=</span><span class="hl-7">&quot;tablist&quot;</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;</span><span class="hl-5">button</span><br/><span class="hl-2"> </span><span class="hl-6">type</span><span class="hl-2">=</span><span class="hl-7">&quot;button&quot;</span><br/><span class="hl-2"> </span><span class="hl-6">role</span><span class="hl-2">=</span><span class="hl-7">&quot;tab&quot;</span><br/><span class="hl-2"> </span><span class="hl-6">id</span><span class="hl-2">=</span><span class="hl-7">&quot;trigger1&quot;</span><br/><span class="hl-2"> </span><span class="hl-6">aria-controls</span><span class="hl-2">=</span><span class="hl-7">&quot;panel1&quot;</span><br/><span class="hl-2"> </span><span class="hl-6">aria-selected</span><span class="hl-2">=</span><span class="hl-7">&quot;true&quot;</span><br/><span class="hl-2"> </span><span class="hl-6">tabindex</span><span class="hl-2">=</span><span class="hl-7">&quot;0&quot;</span><br/><span class="hl-2"> </span><span class="hl-4">&gt;</span><br/><span class="hl-2"> Tab 1</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;/</span><span class="hl-5">button</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;</span><span class="hl-5">button</span><br/><span class="hl-2"> </span><span class="hl-6">type</span><span class="hl-2">=</span><span class="hl-7">&quot;button&quot;</span><br/><span class="hl-2"> </span><span class="hl-6">role</span><span class="hl-2">=</span><span class="hl-7">&quot;tab&quot;</span><br/><span class="hl-2"> </span><span class="hl-6">id</span><span class="hl-2">=</span><span class="hl-7">&quot;trigger2&quot;</span><br/><span class="hl-2"> </span><span class="hl-6">aria-controls</span><span class="hl-2">=</span><span class="hl-7">&quot;panel2&quot;</span><br/><span class="hl-2"> </span><span class="hl-6">aria-selected</span><span class="hl-2">=</span><span class="hl-7">&quot;false&quot;</span><br/><span class="hl-2"> </span><span class="hl-6">tabindex</span><span class="hl-2">=</span><span class="hl-7">&quot;-1&quot;</span><br/><span class="hl-2"> </span><span class="hl-4">&gt;</span><br/><span class="hl-2"> Tab 2</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;/</span><span class="hl-5">button</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;</span><span class="hl-5">button</span><br/><span class="hl-2"> </span><span class="hl-6">type</span><span class="hl-2">=</span><span class="hl-7">&quot;button&quot;</span><br/><span class="hl-2"> </span><span class="hl-6">role</span><span class="hl-2">=</span><span class="hl-7">&quot;tab&quot;</span><br/><span class="hl-2"> </span><span class="hl-6">id</span><span class="hl-2">=</span><span class="hl-7">&quot;trigger3&quot;</span><br/><span class="hl-2"> </span><span class="hl-6">aria-controls</span><span class="hl-2">=</span><span class="hl-7">&quot;panel3&quot;</span><br/><span class="hl-2"> </span><span class="hl-6">aria-selected</span><span class="hl-2">=</span><span class="hl-7">&quot;false&quot;</span><br/><span class="hl-2"> </span><span class="hl-6">tabindex</span><span class="hl-2">=</span><span class="hl-7">&quot;-1&quot;</span><br/><span class="hl-2"> </span><span class="hl-4">&gt;</span><br/><span class="hl-2"> Tab 3</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;/</span><span class="hl-5">button</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;/</span><span class="hl-5">div</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;</span><span class="hl-5">div</span><span class="hl-2"> </span><span class="hl-6">role</span><span class="hl-2">=</span><span class="hl-7">&quot;tabpanel&quot;</span><span class="hl-2"> </span><span class="hl-6">id</span><span class="hl-2">=</span><span class="hl-7">&quot;panel1&quot;</span><span class="hl-2"> </span><span class="hl-6">aria-labelledby</span><span class="hl-2">=</span><span class="hl-7">&quot;trigger1&quot;</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> Tab 1 content</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;/</span><span class="hl-5">div</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;</span><span class="hl-5">div</span><span class="hl-2"> </span><span class="hl-6">role</span><span class="hl-2">=</span><span class="hl-7">&quot;tabpanel&quot;</span><span class="hl-2"> </span><span class="hl-6">id</span><span class="hl-2">=</span><span class="hl-7">&quot;panel2&quot;</span><span class="hl-2"> </span><span class="hl-6">aria-labelledby</span><span class="hl-2">=</span><span class="hl-7">&quot;trigger2&quot;</span><span class="hl-2"> </span><span class="hl-6">hidden</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> Tab 2 content</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;/</span><span class="hl-5">div</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;</span><span class="hl-5">div</span><span class="hl-2"> </span><span class="hl-6">role</span><span class="hl-2">=</span><span class="hl-7">&quot;tabpanel&quot;</span><span class="hl-2"> </span><span class="hl-6">id</span><span class="hl-2">=</span><span class="hl-7">&quot;panel3&quot;</span><span class="hl-2"> </span><span class="hl-6">aria-labelledby</span><span class="hl-2">=</span><span class="hl-7">&quot;trigger3&quot;</span><span class="hl-2"> </span><span class="hl-6">hidden</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> Tab 3 content</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;/</span><span class="hl-5">div</span><span class="hl-4">&gt;</span><br/><span class="hl-4">&lt;/</span><span class="hl-5">tab-group</span><span class="hl-4">&gt;</span> </code><button type="button">Copy</button></pre> <p>UIElement component:</p> <pre><code class="js"><span class="hl-8">import</span><span class="hl-2"> { </span><span class="hl-9">all</span><span class="hl-2">, </span><span class="hl-9">component</span><span class="hl-2">, </span><span class="hl-9">first</span><span class="hl-2">, </span><span class="hl-9">on</span><span class="hl-2">, </span><span class="hl-9">setProperty</span><span class="hl-2"> } </span><span class="hl-8">from</span><span class="hl-2"> </span><span class="hl-3">&quot;@zeix/ui-element&quot;</span><br/><br/><span class="hl-0">// Function to create event handler for arrow key focus management</span><br/><span class="hl-12">const</span><span class="hl-2"> </span><span class="hl-1">manageArrowKeyFocus</span><span class="hl-2"> </span><span class="hl-13">=</span><span class="hl-2"> (</span><span class="hl-11">elements</span><span class="hl-2">, </span><span class="hl-11">index</span><span class="hl-2">) </span><span class="hl-12">=&gt;</span><span class="hl-2"> </span><span class="hl-11">e</span><span class="hl-2"> </span><span class="hl-12">=&gt;</span><span class="hl-2"> {</span><br/><span class="hl-2"> </span><span class="hl-8">if</span><span class="hl-2"> (</span><span class="hl-13">!</span><span class="hl-2">(</span><span class="hl-9">e</span><span class="hl-2"> </span><span class="hl-21">instanceof</span><span class="hl-2"> </span><span class="hl-22">KeyboardEvent</span><span class="hl-2">))</span><br/><span class="hl-2"> </span><span class="hl-8">throw</span><span class="hl-2"> </span><span class="hl-21">new</span><span class="hl-2"> </span><span class="hl-1">TypeError</span><span class="hl-2">(</span><span class="hl-3">&quot;Event is not a KeyboardEvent&quot;</span><span class="hl-2">)</span><br/><span class="hl-2"> </span><span class="hl-12">const</span><span class="hl-2"> </span><span class="hl-10">handledKeys</span><span class="hl-2"> </span><span class="hl-13">=</span><span class="hl-2"> [</span><span class="hl-3">&quot;ArrowLeft&quot;</span><span class="hl-2">, </span><span class="hl-3">&quot;ArrowRight&quot;</span><span class="hl-2">, </span><span class="hl-3">&quot;ArrowUp&quot;</span><span class="hl-2">, </span><span class="hl-3">&quot;ArrowDown&quot;</span><span class="hl-2">, </span><span class="hl-3">&quot;Home&quot;</span><span class="hl-2">, </span><span class="hl-3">&quot;End&quot;</span><span class="hl-2">]</span><br/><span class="hl-2"> </span><span class="hl-8">if</span><span class="hl-2"> (</span><span class="hl-9">handledKeys</span><span class="hl-2">.</span><span class="hl-1">includes</span><span class="hl-2">(</span><span class="hl-9">e</span><span class="hl-2">.</span><span class="hl-9">key</span><span class="hl-2">)) {</span><br/><span class="hl-2"> </span><span class="hl-9">e</span><span class="hl-2">.</span><span class="hl-1">preventDefault</span><span class="hl-2">()</span><br/><span class="hl-2"> </span><span class="hl-8">switch</span><span class="hl-2"> (</span><span class="hl-9">e</span><span class="hl-2">.</span><span class="hl-9">key</span><span class="hl-2">) {</span><br/><span class="hl-2"> </span><span class="hl-8">case</span><span class="hl-2"> </span><span class="hl-3">&quot;ArrowLeft&quot;</span><span class="hl-2">:</span><br/><span class="hl-2"> </span><span class="hl-8">case</span><span class="hl-2"> </span><span class="hl-3">&quot;ArrowUp&quot;</span><span class="hl-2">:</span><br/><span class="hl-2"> </span><span class="hl-9">index</span><span class="hl-2"> </span><span class="hl-13">=</span><span class="hl-2"> </span><span class="hl-9">index</span><span class="hl-2"> </span><span class="hl-13">&lt;</span><span class="hl-2"> </span><span class="hl-18">1</span><span class="hl-2"> </span><span class="hl-13">?</span><span class="hl-2"> </span><span class="hl-9">elements</span><span class="hl-2">.</span><span class="hl-9">length</span><span class="hl-2"> </span><span class="hl-13">-</span><span class="hl-2"> </span><span class="hl-18">1</span><span class="hl-2"> </span><span class="hl-13">:</span><span class="hl-2"> </span><span class="hl-9">index</span><span class="hl-2"> </span><span class="hl-13">-</span><span class="hl-2"> </span><span class="hl-18">1</span><br/><span class="hl-2"> </span><span class="hl-8">break</span><br/><span class="hl-2"> </span><span class="hl-8">case</span><span class="hl-2"> </span><span class="hl-3">&quot;ArrowRight&quot;</span><span class="hl-2">:</span><br/><span class="hl-2"> </span><span class="hl-8">case</span><span class="hl-2"> </span><span class="hl-3">&quot;ArrowDown&quot;</span><span class="hl-2">:</span><br/><span class="hl-2"> </span><span class="hl-9">index</span><span class="hl-2"> </span><span class="hl-13">=</span><span class="hl-2"> </span><span class="hl-9">index</span><span class="hl-2"> </span><span class="hl-13">&gt;=</span><span class="hl-2"> </span><span class="hl-9">elements</span><span class="hl-2">.</span><span class="hl-9">length</span><span class="hl-2"> </span><span class="hl-13">-</span><span class="hl-2"> </span><span class="hl-18">1</span><span class="hl-2"> </span><span class="hl-13">?</span><span class="hl-2"> </span><span class="hl-18">0</span><span class="hl-2"> </span><span class="hl-13">:</span><span class="hl-2"> </span><span class="hl-9">index</span><span class="hl-2"> </span><span class="hl-13">+</span><span class="hl-2"> </span><span class="hl-18">1</span><br/><span class="hl-2"> </span><span class="hl-8">break</span><br/><span class="hl-2"> </span><span class="hl-8">case</span><span class="hl-2"> </span><span class="hl-3">&quot;Home&quot;</span><span class="hl-2">:</span><br/><span class="hl-2"> </span><span class="hl-9">index</span><span class="hl-2"> </span><span class="hl-13">=</span><span class="hl-2"> </span><span class="hl-18">0</span><br/><span class="hl-2"> </span><span class="hl-8">break</span><br/><span class="hl-2"> </span><span class="hl-8">case</span><span class="hl-2"> </span><span class="hl-3">&quot;End&quot;</span><span class="hl-2">:</span><br/><span class="hl-2"> </span><span class="hl-9">index</span><span class="hl-2"> </span><span class="hl-13">=</span><span class="hl-2"> </span><span class="hl-9">elements</span><span class="hl-2">.</span><span class="hl-9">length</span><span class="hl-2"> </span><span class="hl-13">-</span><span class="hl-2"> </span><span class="hl-18">1</span><br/><span class="hl-2"> </span><span class="hl-8">break</span><br/><span class="hl-2"> }</span><br/><span class="hl-2"> </span><span class="hl-8">if</span><span class="hl-2"> (</span><span class="hl-9">elements</span><span class="hl-2">[</span><span class="hl-9">index</span><span class="hl-2">]) </span><span class="hl-9">elements</span><span class="hl-2">[</span><span class="hl-9">index</span><span class="hl-2">].</span><span class="hl-1">focus</span><span class="hl-2">()</span><br/><span class="hl-2"> }</span><br/><span class="hl-2">}</span><br/><br/><span class="hl-0">// Component</span><br/><span class="hl-1">component</span><span class="hl-2">(</span><span class="hl-3">&quot;tab-group&quot;</span><span class="hl-2">, {</span><br/><span class="hl-2"> </span><span class="hl-9">selected:</span><span class="hl-2"> </span><span class="hl-3">&quot;&quot;</span><br/><span class="hl-2">}, </span><span class="hl-11">el</span><span class="hl-2"> </span><span class="hl-12">=&gt;</span><span class="hl-2"> {</span><br/><span class="hl-2"> </span><span class="hl-9">el</span><span class="hl-2">.</span><span class="hl-9">selected</span><span class="hl-2"> </span><span class="hl-13">=</span><span class="hl-2"> </span><span class="hl-9">el</span><span class="hl-2">.</span><span class="hl-1">querySelector</span><span class="hl-2">(</span><span class="hl-3">&quot;[role=tab][aria-selected=true]&quot;</span><span class="hl-2">)?.</span><span class="hl-1">getAttribute</span><span class="hl-2">(</span><span class="hl-3">&quot;aria-controls&quot;</span><span class="hl-2">) </span><span class="hl-13">??</span><span class="hl-2"> </span><span class="hl-3">&quot;&quot;</span><span class="hl-2">;</span><br/><span class="hl-2"> </span><span class="hl-12">const</span><span class="hl-2"> </span><span class="hl-1">isSelected</span><span class="hl-2"> </span><span class="hl-13">=</span><span class="hl-2"> </span><span class="hl-11">target</span><span class="hl-2"> </span><span class="hl-12">=&gt;</span><br/><span class="hl-2"> </span><span class="hl-9">el</span><span class="hl-2">.</span><span class="hl-9">selected</span><span class="hl-2"> </span><span class="hl-13">===</span><span class="hl-2"> </span><span class="hl-9">target</span><span class="hl-2">.</span><span class="hl-1">getAttribute</span><span class="hl-2">(</span><span class="hl-3">&quot;aria-controls&quot;</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-12">const</span><span class="hl-2"> </span><span class="hl-10">tabs</span><span class="hl-2"> </span><span class="hl-13">=</span><span class="hl-2"> </span><span class="hl-9">Array</span><span class="hl-2">.</span><span class="hl-1">from</span><span class="hl-2">(</span><span class="hl-9">el</span><span class="hl-2">.</span><span class="hl-1">querySelectorAll</span><span class="hl-2">(</span><span class="hl-3">&quot;[role=tab]&quot;</span><span class="hl-2">));</span><br/><span class="hl-2"> </span><span class="hl-12">let</span><span class="hl-2"> </span><span class="hl-9">focusIndex</span><span class="hl-2"> </span><span class="hl-13">=</span><span class="hl-2"> </span><span class="hl-18">0</span><span class="hl-2">;</span><br/><br/><span class="hl-2"> </span><span class="hl-8">return</span><span class="hl-2"> [</span><br/><span class="hl-2"> </span><span class="hl-1">first</span><span class="hl-2">(</span><span class="hl-3">&quot;[role=tablist]&quot;</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-1">on</span><span class="hl-2">(</span><span class="hl-3">&quot;keydown&quot;</span><span class="hl-2">, </span><span class="hl-1">manageArrowKeyFocus</span><span class="hl-2">(</span><span class="hl-9">tabs</span><span class="hl-2">, </span><span class="hl-9">focusIndex</span><span class="hl-2">)),</span><br/><span class="hl-2"> ),</span><br/><span class="hl-2"> </span><span class="hl-1">all</span><span class="hl-2">(</span><span class="hl-3">&quot;[role=tab]&quot;</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-1">on</span><span class="hl-2">(</span><span class="hl-3">&quot;click&quot;</span><span class="hl-2">, </span><span class="hl-11">e</span><span class="hl-2"> </span><span class="hl-12">=&gt;</span><span class="hl-2"> {</span><br/><span class="hl-2"> </span><span class="hl-9">el</span><span class="hl-2">.</span><span class="hl-9">selected</span><span class="hl-2"> </span><span class="hl-13">=</span><span class="hl-2"> </span><span class="hl-9">e</span><span class="hl-2">.</span><span class="hl-9">currentTarget</span><span class="hl-2">.</span><span class="hl-1">getAttribute</span><span class="hl-2">(</span><span class="hl-3">&quot;aria-controls&quot;</span><span class="hl-2">) </span><span class="hl-13">??</span><span class="hl-2"> </span><span class="hl-3">&quot;&quot;</span><span class="hl-2">;</span><br/><span class="hl-2"> </span><span class="hl-9">focusIndex</span><span class="hl-2"> </span><span class="hl-13">=</span><span class="hl-2"> </span><span class="hl-9">tabs</span><span class="hl-2">.</span><span class="hl-1">findIndex</span><span class="hl-2">(</span><span class="hl-11">tab</span><span class="hl-2"> </span><span class="hl-12">=&gt;</span><span class="hl-2"> </span><span class="hl-1">isSelected</span><span class="hl-2">(</span><span class="hl-9">tab</span><span class="hl-2">));</span><br/><span class="hl-2"> }),</span><br/><span class="hl-2"> </span><span class="hl-1">setProperty</span><span class="hl-2">(</span><span class="hl-3">&quot;ariaSelected&quot;</span><span class="hl-2">, </span><span class="hl-11">target</span><span class="hl-2"> </span><span class="hl-12">=&gt;</span><span class="hl-2"> </span><span class="hl-1">String</span><span class="hl-2">(</span><span class="hl-1">isSelected</span><span class="hl-2">(</span><span class="hl-9">target</span><span class="hl-2">))),</span><br/><span class="hl-2"> </span><span class="hl-1">setProperty</span><span class="hl-2">(</span><span class="hl-3">&quot;tabIndex&quot;</span><span class="hl-2">, </span><span class="hl-11">target</span><span class="hl-2"> </span><span class="hl-12">=&gt;</span><span class="hl-2"> </span><span class="hl-1">isSelected</span><span class="hl-2">(</span><span class="hl-9">target</span><span class="hl-2">) </span><span class="hl-13">?</span><span class="hl-2"> </span><span class="hl-18">0</span><span class="hl-2"> </span><span class="hl-13">:</span><span class="hl-2"> </span><span class="hl-13">-</span><span class="hl-18">1</span><span class="hl-2">),</span><br/><span class="hl-2"> ),</span><br/><span class="hl-2"> </span><span class="hl-1">all</span><span class="hl-2">(</span><span class="hl-3">&quot;[role=tabpanel]&quot;</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-1">setProperty</span><span class="hl-2">(</span><span class="hl-3">&quot;hidden&quot;</span><span class="hl-2">, </span><span class="hl-11">target</span><span class="hl-2"> </span><span class="hl-12">=&gt;</span><span class="hl-2"> </span><span class="hl-9">el</span><span class="hl-2">.</span><span class="hl-9">selected</span><span class="hl-2"> </span><span class="hl-13">!==</span><span class="hl-2"> </span><span class="hl-9">target</span><span class="hl-2">.</span><span class="hl-9">id</span><span class="hl-2">),</span><br/><span class="hl-2"> )</span><br/><span class="hl-2"> ];</span><br/><span class="hl-2">});</span> </code><button type="button">Copy</button></pre> <p>Example styles:</p> <pre><code class="css"><span class="hl-5">tab-group</span><span class="hl-2"> {</span><br/><span class="hl-2"> </span><span class="hl-14">display</span><span class="hl-2">: </span><span class="hl-15">block</span><span class="hl-2">;</span><br/><span class="hl-2"> </span><span class="hl-14">margin-bottom</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--space-l</span><span class="hl-2">);</span><br/><br/><span class="hl-2"> &gt; [</span><span class="hl-14">role</span><span class="hl-2">=&quot;tablist&quot;] {</span><br/><span class="hl-2"> </span><span class="hl-14">display</span><span class="hl-2">: </span><span class="hl-15">flex</span><span class="hl-2">;</span><br/><span class="hl-2"> </span><span class="hl-14">border-bottom</span><span class="hl-2">: </span><span class="hl-18">1</span><span class="hl-19">px</span><span class="hl-2"> </span><span class="hl-15">solid</span><span class="hl-2"> </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--color-gray-50</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">padding</span><span class="hl-2">: </span><span class="hl-18">0</span><span class="hl-2">;</span><br/><span class="hl-2"> </span><span class="hl-14">margin-bottom</span><span class="hl-2">: </span><span class="hl-18">0</span><span class="hl-2">;</span><br/><br/><span class="hl-2"> &amp; button {</span><br/><span class="hl-2"> </span><span class="hl-14">border</span><span class="hl-2">: </span><span class="hl-18">0</span><span class="hl-2">;</span><br/><span class="hl-2"> </span><span class="hl-14">border-top</span><span class="hl-2">: </span><span class="hl-18">2</span><span class="hl-19">px</span><span class="hl-2"> </span><span class="hl-15">solid</span><span class="hl-2"> </span><span class="hl-15">transparent</span><span class="hl-2">;</span><br/><span class="hl-2"> </span><span class="hl-14">border-bottom-width</span><span class="hl-2">: </span><span class="hl-18">0</span><span class="hl-2">;</span><br/><span class="hl-2"> </span><span class="hl-14">font-family</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--font-family-sans</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">font-size</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--font-size-s</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">font-weight</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--font-weight-bold</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">padding</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--space-s</span><span class="hl-2">) </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--space-m</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">color</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--color-text-soft</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">background-color</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--color-secondary</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">cursor</span><span class="hl-2">: </span><span class="hl-15">pointer</span><span class="hl-2">;</span><br/><span class="hl-2"> </span><span class="hl-14">transition</span><span class="hl-2">: </span><span class="hl-15">all</span><span class="hl-2"> </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--transition-short</span><span class="hl-2">) </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--easing-inout</span><span class="hl-2">);</span><br/><br/><span class="hl-2"> &amp;:hover,</span><br/><span class="hl-2"> &amp;:focus {</span><br/><span class="hl-2"> </span><span class="hl-15">color</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--color-text</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">background-color</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--color-secondary-hover</span><span class="hl-2">);</span><br/><span class="hl-2"> }</span><br/><br/><span class="hl-2"> &amp;</span><span class="hl-20">:active</span><span class="hl-2"> {</span><br/><span class="hl-2"> </span><span class="hl-14">color</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--color-text</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">background-color</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--color-secondary-active</span><span class="hl-2">);</span><br/><span class="hl-2"> }</span><br/><br/><span class="hl-2"> &amp;[</span><span class="hl-6">aria-selected</span><span class="hl-13">=</span><span class="hl-3">&quot;true&quot;</span><span class="hl-2">] {</span><br/><span class="hl-2"> </span><span class="hl-14">color</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--color-primary-active</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">border-top</span><span class="hl-2">: </span><span class="hl-18">3</span><span class="hl-19">px</span><span class="hl-2"> </span><span class="hl-15">solid</span><span class="hl-2"> </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--color-primary</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">background-color</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--color-background</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">margin-bottom</span><span class="hl-2">: </span><span class="hl-18">-1</span><span class="hl-19">px</span><span class="hl-2">;</span><br/><span class="hl-2"> }</span><br/><span class="hl-2"> }</span><br/><span class="hl-2"> }</span><br/><br/><span class="hl-2"> </span><span class="hl-13">&gt;</span><span class="hl-2"> [</span><span class="hl-6">role</span><span class="hl-13">=</span><span class="hl-3">&quot;tabpanel&quot;</span><span class="hl-2">] {</span><br/><span class="hl-2"> </span><span class="hl-14">font-family</span><span class="hl-2">: </span><span class="hl-15">sans-serif</span><span class="hl-2">;</span><br/><span class="hl-2"> </span><span class="hl-14">font-size</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--font-size-m</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">background</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--color-background</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-14">margin-block</span><span class="hl-2">: </span><span class="hl-16">var</span><span class="hl-2">(</span><span class="hl-17">--space-l</span><span class="hl-2">);</span><br/><span class="hl-2"> }</span><br/><span class="hl-2">}</span> </code><button type="button">Copy</button></pre> <a id="lazy-load" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Lazy Load<a href="#lazy-load" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><p>An example demonstrating how to use a custom attribute parser (sanitize an URL) and a signal producer (async fetch) to implement lazy loading.</p> <pre><code class="html"><span class="hl-4">&lt;</span><span class="hl-5">lazy-load</span><span class="hl-2"> </span><span class="hl-6">src</span><span class="hl-2">=</span><span class="hl-7">&quot;/lazy-load/snippet.html&quot;</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;</span><span class="hl-5">div</span><span class="hl-2"> </span><span class="hl-6">class</span><span class="hl-2">=</span><span class="hl-7">&quot;loading&quot;</span><span class="hl-2"> </span><span class="hl-6">role</span><span class="hl-2">=</span><span class="hl-7">&quot;status&quot;</span><span class="hl-4">&gt;</span><span class="hl-2">Loading...</span><span class="hl-4">&lt;/</span><span class="hl-5">div</span><span class="hl-4">&gt;</span><br/><span class="hl-2"> </span><span class="hl-4">&lt;</span><span class="hl-5">div</span><span class="hl-2"> </span><span class="hl-6">class</span><span class="hl-2">=</span><span class="hl-7">&quot;error&quot;</span><span class="hl-2"> </span><span class="hl-6">role</span><span class="hl-2">=</span><span class="hl-7">&quot;alert&quot;</span><span class="hl-2"> </span><span class="hl-6">aria-live</span><span class="hl-2">=</span><span class="hl-7">&quot;polite&quot;</span><span class="hl-4">&gt;&lt;/</span><span class="hl-5">div</span><span class="hl-4">&gt;</span><br/><span class="hl-4">&lt;/</span><span class="hl-5">lazy-load</span><span class="hl-4">&gt;</span> </code><button type="button">Copy</button></pre> <pre><code class="js"><span class="hl-8">import</span><span class="hl-2"> { </span><span class="hl-9">setProperty</span><span class="hl-2">, </span><span class="hl-9">setText</span><span class="hl-2">, </span><span class="hl-9">dangerouslySetInnerHTML</span><span class="hl-2">, </span><span class="hl-9">component</span><span class="hl-2">, </span><span class="hl-9">first</span><span class="hl-2"> } </span><span class="hl-8">from</span><span class="hl-2"> </span><span class="hl-3">&quot;@zeix/ui-element&quot;</span><span class="hl-2">;</span><br/><br/><span class="hl-0">// Attribute Parser uses current element to detect recursion and set error message</span><br/><span class="hl-12">const</span><span class="hl-2"> </span><span class="hl-1">asURL</span><span class="hl-2"> </span><span class="hl-13">=</span><span class="hl-2"> (</span><span class="hl-11">el</span><span class="hl-2">, </span><span class="hl-11">v</span><span class="hl-2">) </span><span class="hl-12">=&gt;</span><span class="hl-2"> {</span><br/><span class="hl-2"> </span><span class="hl-12">let</span><span class="hl-2"> </span><span class="hl-9">value</span><span class="hl-2"> </span><span class="hl-13">=</span><span class="hl-2"> </span><span class="hl-3">&quot;&quot;</span><span class="hl-2">;</span><br/><span class="hl-2"> </span><span class="hl-12">let</span><span class="hl-2"> </span><span class="hl-9">error</span><span class="hl-2"> </span><span class="hl-13">=</span><span class="hl-2"> </span><span class="hl-3">&quot;&quot;</span><span class="hl-2">;</span><br/><span class="hl-2"> </span><span class="hl-8">if</span><span class="hl-2"> (</span><span class="hl-13">!</span><span class="hl-9">v</span><span class="hl-2">) {</span><br/><span class="hl-2"> </span><span class="hl-9">error</span><span class="hl-2"> </span><span class="hl-13">=</span><span class="hl-2"> </span><span class="hl-3">&quot;No URL provided in src attribute&quot;</span><span class="hl-2">;</span><br/><span class="hl-2"> } </span><span class="hl-8">else</span><span class="hl-2"> </span><span class="hl-8">if</span><span class="hl-2"> (</span><br/><span class="hl-2"> (</span><span class="hl-9">el</span><span class="hl-2">.</span><span class="hl-9">parentElement</span><span class="hl-2"> </span><span class="hl-13">||</span><span class="hl-2"> (</span><span class="hl-9">el</span><span class="hl-2">.</span><span class="hl-1">getRootNode</span><span class="hl-2">() </span><span class="hl-8">as</span><span class="hl-2"> </span><span class="hl-22">ShadowRoot</span><span class="hl-2">).</span><span class="hl-9">host</span><span class="hl-2">)?.</span><span class="hl-1">closest</span><span class="hl-2">(</span><br/><span class="hl-2"> </span><span class="hl-3">`</span><span class="hl-21">${</span><span class="hl-9">el</span><span class="hl-23">.</span><span class="hl-9">localName</span><span class="hl-21">}</span><span class="hl-3">[src=&quot;</span><span class="hl-21">${</span><span class="hl-9">v</span><span class="hl-21">}</span><span class="hl-3">&quot;]`</span><span class="hl-2">,</span><br/><span class="hl-2"> )</span><br/><span class=