UNPKG

lightview

Version:

A reactive UI library with features of Bau, Juris, and HTMX plus safe LLM UI generation

283 lines (260 loc) 12.2 kB
<!-- SEO-friendly SPA Shim --> <script src="/lightview-router.js?base=/index.html"></script> <link rel="stylesheet" href="../components/index.css"> <div class="docs-layout"> <aside class="docs-sidebar" src="./nav.html" data-preserve-scroll="docs-nav"></aside> <main class="docs-content"> <h1>Elements</h1> <p> Lightview is uniquely flexible. It doesn't force you into a single way of describing your DOM. Whether you prefer concise JavaScript functions, structured JSON, or standard HTML, the same signal-based reactivity powers it all. </p> <h2 id="comparison">Comparison</h2> <table class="api-table"> <thead> <tr> <th>Syntax</th> <th>Style</th> <th>Best For</th> <th>Requirement</th> </tr> </thead> <tbody> <tr> <td><strong>Tagged API</strong></td> <td><code>div(h1('Title'))</code></td> <td>Application logic, dynamic UIs</td> <td>Core</td> </tr> <tr> <td><strong>vDOM</strong></td> <td><code>{ tag: 'div', ... }</code></td> <td>Serialization, data-driven UI</td> <td>Core</td> </tr> <tr> <td><strong>Object DOM (oDOM)</strong></td> <td><code>{ div: { ... } }</code></td> <td>Concise templates, config files</td> <td>Lightview X</td> </tr> <tr> <td><strong>Custom Elements</strong></td> <td><code>&lt;lv-button&gt;</code></td> <td>Progressive enhancement, CMS</td> <td>Lightview X</td> </tr> <tr> <td><strong>cDOM (Experimental)</strong></td> <td><code>'sum($/cart/items...price)'</code></td> <td>Declarative logic, LLM generation</td> <td>lightview-cdom.js</td> </tr> </tbody> </table> <p style="margin-top: 1rem; font-style: italic;"> Note: An exciting 5th option is coming, the <strong>Computational DOM</strong>, a.k.a. <strong><a href="/docs/cdom.html">cDOM</a></strong>. </p> <h2 id="tagged-api">Tagged API</h2> <p> Inspired by Bau.js, this is the most concise way to build UIs in JavaScript. Every HTML tag is available as a function. </p> <div id="syntax-tagged"> <pre><script> examplify(document.currentScript.nextElementSibling, { at: document.currentScript.parentElement, scripts: ['/lightview.js', '/lightview-x.js'], type: 'module', minHeight: 120, autoRun: true }); </script><code contenteditable="true">const { signal, tags, $ } = Lightview; const { div, h1, p, button } = tags; const count = signal(0); const app = div({ class: 'container' }, h1('Hello Lightview'), p(() => `Count: ${count.value}`), button({ onclick: () => count.value++ }, 'Click me') ); $('#example').content(app);</code></pre> </div> <p><strong>Pros:</strong> Extremely readable, feels like "HTML in JS" without a compiler, full IDE autocomplete. </p> <h2 id="vdom">vDOM Syntax</h2> <p> Represent your UI as plain JavaScript objects. This is the underlying format for all non-string elements in Lightview. </p> <div id="syntax-vdom"> <pre><script> examplify(document.currentScript.nextElementSibling, { at: document.currentScript.parentElement, scripts: ['/lightview.js', '/lightview-x.js'], type: 'module', minHeight: 120, autoRun: true }); </script><code contenteditable="true">const { signal, element, $, tags } = Lightview; const { div } = tags; const count = signal(0); // will accept either a function or string as tag, function avoids typos better const app = { tag:div, attributes: { class: 'container' }, children: [ { tag: 'h1', attributes: {}, children: ['Hello Lightview'] }, { tag: 'p', attributes: {}, children: [() => `Count: ${count.value}`] }, { tag: 'button', attributes: { onclick: () => count.value++ }, children: ['Click me'] } ]}; $('#example').content(app);</code></pre> </div> <p><strong>Pros:</strong> Unambiguous, easy to serialize/deserialize as JSON, perfect for programmatic generation.</p> <h2 id="object-dom">Object DOM (oDOM)</h2> <p> A more compact JSON representation provided by <strong>Lightview X</strong>. In oDOM, an object with a <strong>single key</strong> and an <strong>object value</strong> represents an element. The key is the <strong>tag name</strong> (e.g., <code>"div"</code>, <code>"span"</code>) or a <strong>custom element function name</strong>. The value object contains the element's attributes. </p> <h3>Reserved Keys</h3> <p> Certain keys on the value object are reserved and have special meaning instead of being treated as attributes: </p> <table class="api-table"> <thead> <tr> <th>Key</th> <th>Purpose</th> </tr> </thead> <tbody> <tr> <td><code>children</code></td> <td>An array of child elements, strings, or reactive functions.</td> </tr> <tr> <td><code>attributes</code></td> <td>An explicit attributes object (not needed in oDOM since non-reserved keys are attributes). If you use this attribute, Lighview MAY assume you are trying to use vDOM not oDOM. Avoid its use except for vDOM. </td> </tr> <tr> <td><code>tag</code></td> <td>Explicitly sets the tag name. If you use this attribute, Lighview MAY assume you are trying to use vDOM not oDOM. Avoid its use except for vDOM.</td> </tr> </tbody> </table> <p> All other keys on the value object are treated as <strong>attribute names</strong>. Keys starting with <code>on</code> (like <code>onclick</code>, <code>onmouseenter</code>) are bound as event handlers. </p> <h3>Array Shorthand</h3> <p> If a tag key has an <strong>array</strong> as its value instead of an object, it is shorthand for <code>{ &lt;key&gt;: { children: &lt;array&gt; } }</code>: </p> <pre><code>// These are equivalent: { ul: [{ li: ['Item 1'] }, { li: ['Item 2'] }] } { ul: { children: [{ li: { children: ['Item 1'] } }, { li: { children: ['Item 2'] } }] } }</code></pre> <h3>Example</h3> <div id="syntax-object"> <pre><script> examplify(document.currentScript.nextElementSibling, { at: document.currentScript.parentElement, scripts: ['/lightview.js', '/lightview-x.js'], type: 'module', minHeight: 120, autoRun: true }); </script><code contenteditable="true">const { signal, tags, $ } = Lightview; const count = signal(0); const app = { div: { class: 'container', children: [ { h1: ['Hello Lightview'] }, { p: { children: [() => `Count: ${count.value}`] } }, { button: { onclick: () => count.value++, children: ['Click me'] } } ]}}; $('#example').content(app);</code></pre> </div> <p><strong>Pros:</strong> Highly readable for templates stored in JSON, significantly less boilerplate than standard vDOM.</p> <h2 id="custom-elements">HTML Custom Elements</h2> <p> Use standard HTML tags to instantiate Lightview components. Ideal for multi-page apps or content managed by a CMS. </p> <pre><code>&lt;!-- Requires registered components &amp; Lightview X --&gt; &lt;lv-card&gt; &lt;h3 slot="title"&gt;User Profile&lt;/h3&gt; &lt;lv-badge color="primary"&gt;Admin&lt;/lv-badge&gt; &lt;p&gt;Active since 2024&lt;/p&gt; &lt;lv-button onclick="alert('Clicked!')"&gt;Settings&lt;/lv-button&gt; &lt;/lv-card&gt;</code></pre> <p><strong>Pros:</strong> Familiar HTML syntax, framework-agnostic, excellent for progressive enhancement of server-rendered pages.</p> <h2 id="pseudo-elements">Pseudo-elements</h2> <p> Lightview supports special "pseudo-elements" that perform specific tasks rather than creating a standard HTML element. </p> <h3 id="shadowdom">shadowDOM</h3> <p> The <code>shadowDOM</code> tag allows you to attach a Shadow Root to the parent element and render children inside it. This is useful for building encapsulated components without manual <code>attachShadow</code> calls. </p> <pre><code>const { tags } = Lightview; const { div, shadowDOM, h2, p } = tags; const MyComponent = () => div( shadowDOM({ mode: 'open', styles: ['/my-styles.css'] }, h2('Encapsulated Title'), p('This content is inside the shadow root.') ) );</code></pre> <h3 id="text">text</h3> <p> The <code>text</code> tag creates a single <code>Text</code> node containing the concatenated content of all its children, separated by spaces. It supports reactivity, meaning if any child is a function, the text node will update automatically. </p> <pre><code>const { tags, signal } = Lightview; const { div, text } = tags; const firstName = signal('John'); const lastName = signal('Doe'); const greeting = div( text('Hello,', () => firstName.value, () => lastName.value, '!') ); // Initial result: <div>Hello, John Doe !</div></code></pre> <h2 id="attributes-events">Events</h2> <p>Use standard event handlers prefixed with "on".</p> <pre><code>button({ onclick: (e) => handleClick(e), onmouseenter: () => setHovered(true), onmouseleave: () => setHovered(false) })</code></pre> <h2 id="children">Children</h2> <p> Children can be strings, numbers, elements, arrays, or functions: </p> <pre><code>div( 'Static text', // String 42, // Number (converted to string) span('Nested element'), // Element () => `Dynamic: ${value.value}`, // Reactive function () => items.value.map(i => li(i.name)), // Reactive list condition && span('Conditional') // Conditional (falsy = not rendered) )</code></pre> <h2 id="dom-el">The domEl Property</h2> <p> Every Lightview element has a <code>domEl</code> property - the actual DOM node: </p> <pre><code>const myDiv = div({ class: 'box' }, 'Hello'); // Access the real DOM element document.body.appendChild(myDiv.domEl); // You can also manipulate it directly myDiv.domEl.classList.add('another-class');</code></pre> </main> </div>