# cuel

> Tiny, powerful data binding & web application framework

Latest version **0.0.18** (published 2026-08-05) · MIT license · 0 weekly downloads

## Install

```sh
npm install cuel
pnpm add cuel
yarn add cuel
bun add cuel
```

## Health

**Score 60/100 (C)** — status: active.

Positive: esm support; no vulnerabilities; recently updated; high maintenance score.

Warnings: low downloads; no types; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.0.18 |
| Published | 2026-08-05 |
| First published | 2023-12-13 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | ESM |
| Dependencies | 0 |
| Unpacked size | 80.1 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | Thorsten Roggendorf |
| Maintainers | schrotie |
| Keywords | browser, javascript, library, web-components |

## Links

- npm: https://www.npmjs.com/package/cuel
- Repository: https://codeberg.org/schrotie/cuel
- Issues: https://codeberg.org/schrotie/cuel/issues
- npm.io page: https://npm.io/package/cuel

## Alternatives

- [@opentelemetry/exporter-zipkin](https://npm.io/package/@opentelemetry/exporter-zipkin.md) — 14.8M weekly downloads
- [pusher-js](https://npm.io/package/pusher-js.md) — 2.0M weekly downloads
- [browserify](https://npm.io/package/browserify.md) — 1.7M weekly downloads
- [sqs-consumer](https://npm.io/package/sqs-consumer.md) — 1.7M weekly downloads
- [@sanity/eventsource](https://npm.io/package/@sanity/eventsource.md) — 930.8K weekly downloads

## Recent versions

- 0.0.18 (latest) — 2026-08-05
- 0.0.17 — 2026-05-06
- 0.0.16 — 2026-05-05
- 0.0.15 — 2026-04-14
- 0.0.14 — 2026-04-07
- 0.0.13 — 2026-04-01
- 0.0.12 — 2026-03-20
- 0.0.11 — 2026-03-20
- 0.0.10 — 2026-03-20
- 0.0.7 — 2024-03-05
- 0.0.6 — 2024-02-08
- 0.0.5 — 2024-02-02
- 0.0.4 — 2023-12-21
- 0.0.3 — 2023-12-18
- 0.0.2 — 2023-12-13
- … 1 more at https://npm.io/package/cuel/versions

## README

# cuel

Tiny, powerful data binding & web application framework

<a id="2helloWorld"></a>
## Hello world!

```html
<!doctype html>
<html><body>
<hello-world></hello-world>
<script type=module>
	import {cuel} from "../cuel.mjs"
	cuel('hello-world', {
		set toggle(v) {
			this.msg = this.msg === 'Hello' ? 'Goodbye' : 'Hello';
		}
	}, `<h2>{{msg}} World!</h2>
	    <button !click=toggle>Toggle Greeting</button>
	`);
</script>
</body></html>
```
<a id="2toc"></a>
## Table of Contents

* [Hello world!](#2helloWorld)
* [TOC](#2toc)
* [Features](#2features)
* [Why](#2why)
* [Demo](#2demo)
* [Documentation](#2documentation)
  * [Import](#3import)
  * [Data-Binding](#3dataBinding)
    * [Introduction](#4introduction)
    * [Binding Types](#4bindingTypes)
      * [Attributes, Properties and Text](#5attributesPropertiesText)
      * [Events, Methods and Nodes](#5eventsMethodsNodes)
    * [Change Notifications](#4changeNotifications)
    * [Host Binding](#4hostBinding)
  * [Render](#3render)
  * [ifTemplate](#3ifTemplate)
    * [Custom Condisitions](#4customCondisitions)
  * [Dynamic Binding](#3dynamicBinding)
  * [loopTemplate](#3loopTemplate)
    * [Chunking](#4chunk)
    * [When to Use Handlebars](#4whenToUseHandlebars)
  * [Nested Content-Proxies](#3nestedContentProxies)
    * ["Root" Bindings](#4rootBindings)
  * [Style](#3style)
* [Design Considerations](#2designConsiderations)
  * [SPA Architecture](#3spaArchitecture)
  * [State Management](#3stateManagement)
    * [Multiplexing](#4multiplexing)
    * [Events](#4events)

<a id="2features"></a>
## Features

* 2-Way binding between DOM and JavaScript Objects
* *full* DOM access
* scoped binding
* web component based view-modules with JS-code, HTML-templates and styles
* dynamically binding added DOM
* conditional rendering
* iterative rendering (rendering from array-data)
* ponyfill-style non-intrusive behavior, that *only ever* applies where you invoke it
* all that at < 800 lines of code

<a id="2why"></a>
## Why?

I want a framework that is so small, that it's easy to maintain. Because if you
use it, you own it. You have to update it until it falls out of fashion and then
take over maintenance completely. Cuel is about 800 lines of nice readable
code. It relies on pretty few concepts making it quite easy to fully understand
what's going on in a codebase that is very maintainable.

I want a framework, that just works and works without magic. No compilation, no
custom language like JSX. Cuel code will be easily readable by developers 20
years from now. Because it heavily leverages standards.

I want a framework, that gives me awesome performance where I need it. A decent
web app should clock in at maybe 100K, not megabytes. Cuel is 5K gzipped.
And this is not optimized for size. No magic means I know what's going on and
can optimize for CPU or RAM where needed.

Finally I want a framework that helps me structuring script and DOM but does not
get in my way. I want easy power, native DOM APIs where I need them and nice
bindings where I don't.

<a id="2demo"></a>
## Demo

Cuel comes with an app demo, that shows most features you'll need to write web
apps. You may

```shell
git clone https://codeberg.org/schrotie/cuel.git
cd cuel
npm install
npm start
```

This should open the demo in your browser. Note this will only work in Chrome
or Firefox as of this writing. Other browsers need importmaps support as
described below! Check out the source code in demo/app/ and see in your
browser, what it does. Change it! The browser should automatically reload. I
guess, that's the quickest way to learn Cuel for most of you.

You may come back to the documentation in order to understand, how the features
work, what's actually going on there.

<a id="2documentation"></a>
## Documentation

I designed cuel to give me decoupled dynamic view components on the simplest
possible code base. It does this by leveraging web components and these are a
very fine choice for this. However, web components can be many, many things and
cuel was designed with one very specific use case in mind.

Thus cuel may help you with all your web component related coding and I'd be
very happy if it did. But I designed it to decouple my apps into several views
and decouple these view's logic from the notoriously hard-to-test-with DOM APIs.

Cuel gives you precisely one function and that is a pimped up version of the
[built in](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define)

```JavaScript
customElements.define(name, constructor, options)
```

Indeed, whatever you can pass to `customElements.define`, you can pass to
`cuel`! And more - cuel's key feature is, that you can pass it a template in
which you define mustache/handlebars style bindings. When your component is
instantiated, the template will become DOM ("light" or "shadow" depending on how
you passed the template) and the instance of your component's JavaScript `class`
will have accessors to directly read and write the bound DOM-stuff.

Because cuel was written for cookie cutting apps into view modules, it defaults
to rendering `light` DOM - usually you don't want your view components
completely decoupled from your app's CSS as is the case for
[shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).

So instead of the last `options` argument, you may just pass your template and
have it rendered a light DOM, when your component is `connected`:

```JavaScript
cuel('hello-world', class extends HTMLElement {}, 'Hello world!');
```

When you then (or before) put `<hello-world></hello-world>` into your HTML, that
will read "Hello world!" (yes, looks like a questionable deal put like this,
but bear with me). In order to further minimize boilerplate, cuel also lets you
provide a mixin instead of a class extending `HTMLElement`. It will create an
`HTMLElement` itself using your mixing. So in the simplest case you just do

```JavaScript
cuel('hello-world', {}, 'Hello world!');
```

You only *need* to provide an HTMLElement if you want to customize the
`constructor` method.

So `cuel` accepts two mandatory and one optional argument. The first argument
is a string giving the tag-name of your custom element. The second argument is
a `class` or a mixin implementing the element. And the last argument - if
provided - is a light-DOM-template-string or an options object.

The options object may have the following properties:

* `extends` see custom elements documentation for details
* `template` light-DOM-template-string OR
* `shadow`  shadow-DOM-template-string
* `ifTemplate` map of subtemplates that are conditionally rendered
* `loopTemplate` map of subtemplates that are iteratively rendered
* `style` CSS string to be applied to the document's style
* `hook` internal hook API used to interact with cuel-x, may be changed anytime

We'll cover `ifTemplate`, `loopTemplate` and `style` in detail below.

<a id="3import"></a>
### `import 'cuel'`, Development & Deployment

I hedge a deep and well-fostered hate against having a build step in my
development cycle and my apps work without one. If you want to import cuel
you have two options: import the bundled cuel.min.mjs or the source cuel.mjs.
You may use absolute paths to their position in your node_modules folder.

I recommend using the source version and doing all your bundling yourself.

If you want to import it with a nice `import {cuel} from 'cuel', you should use
import-maps which are supported in the major browsers with the exception of IE
12 (aka "Safari").

Put something like this into the head of your HTML:

```HTML
<script type="importmap">
	{"imports": {"cuel": "./node_modules/cuel/cuel.mjs"}}
</script>
```

and you're good to go for development. You can then just `import {cuel} from 'cuel'` in your code and develop on your sources, not some weird artifact that
resembles what you developed after having that artifact and the browser jump
throw a dozen or so hoops.

For production you absolutely want to have a build step that bundles your app. I
recommend esbuild for this. It's the perfect tool for this task.

#### other imports

I also developed cuel-x as a simple state manager that works well with cuel. If you have a favorite state manager, absolutely use that and connect it with cuel. Ask for support if you need assistance with that.

And if you want all the glory of cuel with cuel-x, your importmap should look like this:

```HTML
<script type="importmap">
	{"imports": {
		"cuel": "./node_modules/cuel/cuel.mjs",
		"cuel-x": "./node_modules/cuel-x/index.mjs"
	}}
</script>
```


<a id="3dataBinding"></a>
### data binding

<a id="4introduction"></a>
#### Introduction

Cuel supports a variant of the mustache/handlebars style binding that may be
somewhat familiar by now. Just put a name in double curly brackets into your
template and have cuel create a binding for you:

```JavaScript
cuel('hello-world', {
	doSomething() {
		this.content = 'Hello world!'; // set the content of this element
		console.log(this.content);     // -> Hello world!
	}
}, '{{content}}');
```

This creates a text node inside the `<hello-world>` tags and when `doSomething`
is called, sets the text of the node to "Hello world!". This is a text content
binding. There are eight types of bindings in two categories. The categories
are content bindings and element bindings. Element bindings bind something of
the respective DOM element itself, while content bindings affect an element's
content. What you get depends on where in your template and how you define it:

```HTML
<div ...{{elementBinding}}> {{contentBinding}}</div>
```

One general note that applies to most binding type: Only `null` means nothing.
Setting `null` on an attribute will remove it, setting `false` will set the
text "false", though.

<a id="4bindingTypes"></a>
#### Binding Types

So here are examples for all binding types:

| Type           | Binding                          | Shorthand                    | Example Code (demo/app/) |
|----------------|----------------------------------|------------------------------|--------------------------|
| **Attribute**  | `attributeName={{accessorA}}`    | `@attributeName=accessorA`   | binding-types.mjs        |
| **Property**   | `.domPropertyName={{accessorP}}` | `.domPropertyName=accessorP` | binding-types.mjs        |
| **Event**      | `!domEventName={{accessorE}}`    | `!domEventName=accessorE`    | binding-types.mjs        |
| **Method**     | `domNodeMethod()={{methodName}}` | `domNodeMethod()=methodName` | binding-types.mjs        |
| **Node**       | `*={{accessorN}}`                | `*=accessorN`                | binding-types.mjs        |
| **Text**       | `{{accessorT}}`                  |                              | binding-types.mjs        |
| **if**         | `{{accessorI}}`                  | `<ìmg ?=accessorI>`          | conditional-rendering.mjs|
| **loop**       | `{{accessorL}}`                  | `<img []=accessorL>`         | looped-rendering.mjs     |

The first five are element bindings, Text, if and loop are content bindings.

<a id="5attributesPropertiesText"></a>
##### Atributes, Properties and Text

Note that on the object implementing your custom element everything translates
to properties, except for DOM methods, which become methods on the object, too.
You can always *access* bound stuff from the JavaScript object.

If you take the attribute binding from the table above, for example,
`element.accessorA` (or `this.accessorA` in a method of the element!) will
*return* the current value of the attribute of the element. The attribute value
is not stored somewhere, but read from the DOM when you call the property getter
`element.accessorA`. It will then call `element.getAttribute('attributeName')`.

You can also always do `element.accessorA = 'x'`. This will invoke
`element.setAttribute('attributeName', 'x')`.

This works exactly the same with property and text bindings.

You may "stuff" attribute, property, and text bindings. If you bind a class
attribute for example: `<div class="oneClass {{boundClass}}">`, then setting
`element.boundClass` will always leave "oneClass" untouched.

<a id="5eventsMethodsNodes"></a>
##### Events, Methods and Nodes

You cannot *get* events (`element.accessorE` will raise an exception) and you
cannot *set* methods or nodes (`element.methodName = 'x'` and
`element.accessorN = 'x'` will each raise exceptions).

Nodes you can only *get* and then have full access to their DOM APIs.

Exception: you *can* set cuel-component-nodes. Cuel considers them proxy
objects with which to manipulate DOM. Setting an object to a
cuel-component-node will internally `Object.apply(node, obj)`. This *only*
works, when the cuel-component has already been initialized. That means,
you'll have to include it's definition before setting to it. Otherwise
you get an exception.

Methods can only be called. `element.methodName()` will call the node's
`domNodeMethod` with the same arguments.

You can assign to events, but you should assign DOM events to them, e.g.
`element.accessorE = new CustomEvent('foo', {detail: 'bar'})`. This will emit
the event on the bound node. You *may* also just pass the custom event ini and
cuel will generate the event for you:

```JavaScript
cuel('event-binding',
	{emit() {this.event = {detail: 'data', bubbles: true};}},
	'<div !bang={{event}}></div>'
);
```

When `element.emit()` is called cuel will do

```JavaScript
divElement.dispatchEvent(new CustomEvent('bang', {detail: 'data', bubbles: true}));
```

<a id="4changeNotifications"></a>
#### Change Notifications

Cuel can also track changes and events in the DOM for you. It will do that,
if it finds a *setter* for the bound property on the bound object. Let's assume
the JavaScript class of the object bound in the table above looks like this:

```JavaScript
class MyDomHandler {
	set accessorA(a) {}
	set accessorE(e) {}
	set accessorT(t) {}
}
```

Then Cuel will call the respective setter when the bound attribute or text
changes, or accessorE if the event `domEventName` is triggered on the bound DOM
node.

Note that you can then not set the respective DOM property yourself using that
accessor! This inherently prevents circular bindings. There are still ways to
shoot yourself in the foot with circular bindings, but not that easyly.

This does notably *not* work for DOM properties! Use events to track their
changes.


<a id="4hostBinding"></a>
#### Host Binding

Your host node - the element which you are defining with cuel, i.e. with
customElements.define - is not part of you element's template. Thus you cannot
do data bindings to the host node in the template. If you need data bindings
on the host node, you may use the `host` option in the options object:

```JavaScript
cuel('hello-world', {}, {host: '.innerHTML=content'});
const helloWorld = document.createElement('hello-world');
helloWorld.content = 'Hello world!';
document.body.appendChild(helloWorld);
```

You may create any number of bindings on the host node.


<a id="3render"></a>
### class, mix-in & the `render` method

As noted above, you can pass cuel a class or a mix-in.

```JavaScript
cuel('cl-ass', class extends HTMLElement {});
cuel('mix-in', {});
```

Now, `customElements.define`, which cuel calls for you, requires something
extending `HTMLElement` so cuel will create one for you if it does not get one,
adding the methods from the `mix-in` to it. In _any case_ it will add
a `render` method to your element, which will be called when the element is
connected to the DOM. For this it will create a `connectedCallback` which
renders your element.

Note: in case you do not provide a `connectedCallback`, the `connectedCallback`
created by cuel will do the initial rendering and there will be no extra `render`
method!

However, if you provide a `connectedCallback` yourself, cuel will generate a
`render` method for you. Thus, if you provide a `connectedCallback`, you should
probably call `this.render()` in your `connectedCallback` method - or elsewhere
in the lifecycle of your element.

```JavaScript
cuel('mix-in',
{
	connectedCallback() {
		this.render();
	}
},
'Hello world!'
);
```

<a id="3ifTemplate"></a>
### ifTemplate

`ifTemplate` is cuel's conditional rendering facility.

```JavaScript
function render(count, template) {
	cuel(`if-template-${count}`, {}, template);
	const ift = document.createElement('if-template-1');
	document.body.append(ift);
	return ift;
}
const ift1 = render(1, '<span ?=greet>Hello!</span>');
ift1.greet = true; // now you see "Hello!"
ift1.greet = null; // now you don't
/*** You'll often want to render DOM with placeholders conditionally:      ***/
const ift2 = render(2, '<span ?=greeting>{{greet}} {{whom}}!</span>');
ift2.greeting = {greet: 'Hello', whom: 'World'};  // now you see "Hello World!"
/*** Or just render some configurable text:                                ***/
const ift3 = render(3, '<span ?=text>{{*}}</span>');
ift3.text = 'Hello World!';  // now you see "Hello World!"
/*** Render several shallow nodes:                                         ***/
const ift4 = render(4, '<template ?=who>Hello {{*}}!</template>');
ift4.who = 'World'; // now you see "Hello World!", just text, no extra elements
/*** Finally, conditionally render and bind cuel (cuel only!) components:  ***/
cuel('sub-component', {}, '{{greeting}} from sub-component!');
const ift5 = render(5, '<sub-component ?*=sub></sub-component');
ift5.sub = {greeting: 'Hello'}; // now you see "Hello from sub-component!"
```

Conditionally rendered DOM can be put into separate named HTML templates. Your
component adds the conditionally rendered part to *its* (your component's)
template by addressing it (in curly braces) by the sub-template's name. Your
component must also list the sub-template in its `ifTemplate` option:

```JavaScript
cuel('if-template', {}, {
	template: '{{showFirst}}{{showSecond}}',
	ifTemplate: {
		showFirst: 'first',
		showSecond: 'second',
	}
});

const ift = document.createElement('if-template');
document.body.append(ift);
ift.showFirst = true; // now you see "first"
ift.showFirst = null; // now you don't
```

When a cuel element has `ifTemplate` properties, cuel adds conditional
properties with the names of the conditional templates to the parent element.
When such a property gets a non-null value, the respective sub-template is
rendered, when it gets null, it gets removed.

If you want to render conditional shallow DOM, you can either use the
`ifTemplate` option as shown above, or you can use the `?` attribute on a
`<template>` element:

```JavaScript
cuel('if-template', {}, '<template ?=greet>Hello!<br></template>');
```

<a id="4customConditions"></a>
#### Custom Conditions

You can define your own conditions in the `ifTemplate` option:

```JavaScript
cuel('if-template', {}, {
	template: '{{myIf}}',
	ifTemplate: {myIf: {
		if: x => x % 2,
		template: 'odd',
	}},
});

const ift = document.createElement('if-template');
document.body.append(ift);
ift.myIf = 3;  // now you see "odd"
ift.myIf = 2; // now you don't
```

<a id="3dynamicBinding"></a>
### dynamic binding

Consider this code:

```JavaScript
const ift = document.createElement('if-template');
document.body.append(ift);
ift.greet = true;
cuel('if-template', {}, '<span ?=greet>Hello!</span>');
```

Note how the element is first created, then its conditional property is set and
only *then* it is defined. Before that definition, `greet` has no special
meaning, it's just a random property name with the value `true` added to an
unknown custom element. But when cuel instantiates your custom element it will
pick up the already existing property and in this case render the conditional
sub-template text "Hello!".

Cuel does that with all bound properties.

<a id="3loopTemplate"></a>
### loopTemplate

`loopTemplate` is somewhat similar to `ifTemplate`. But it will instantiate its
content once for each element of the array you set to the respectively named
property.

```JavaScript
cuel('loop-template', {}, '<ul><li []=list>{{label}}: {{value}}</li></ul>');

const loopt = document.createElement('loop-template');
document.body.append(loopt);
loopt.list = [
    {label: 'first  item', value: 1},
    {label: 'second item', value: 2},
];
```

This renders an unordered list with two items:

* first item: 1
* second item: 2

As with `ifTemplate`s, you can also render shallow looped DOM inline:

```JavaScript
cuel('loop-template', {}, '<template []=list>Value: {{*}}<br></template>');
const loopt = document.createElement('loop-template');
document.body.append(loopt);
loopt.list = [1, 2, 3];
```

This renders:
```HTML
Value: 1<br>
Value: 2<br>
Value: 3<br>
```

And bind sub-components:

```JavaScript
cuel('loop-template', {}, '<sub-component []*=list></sub-component>');
const loopt = document.createElement('loop-template');
document.body.append(loopt);
loopt.list = [
    {greeting: 'Hello'},
    {greeting: 'Hi'},
];
```

This renders:
```HTML
Hello from sub-component!
Hi from sub-component!
```

You can also loop `<template>` as with `ìfTemplate`, in order to render shallow
nodes or elements.

<a id="4chunk"></a>
#### `chunk`

`loopTemplate` supports the `chunk` option to perform chunked rendering:

```JavaScript
cuel('loop-template', {}, '<ul><li [500]=list>{{value}}</li></ul>');
const loopt = document.createElement('loop-template');
document.body.append(loopt);
loopt.list = (new Array(100000)).fill(0).map(() => ({a: Math.random()}));
```

This renders a list with 100.000 items. On my computer that takes a few seconds,
your milage may vary. Now that's a lot of items. More realistic cases render
fewer items but with complex DOM and more computations to render the dynamic
items. So cases where the user experiences lag do occur.

The above example renders without impeding user interaction though. It does so by
chunking the rendering: it renders 500 items at each iteration and then returns
the control to the browser. Cuel repeats that until all 100.000 items are
rendered.

You *should* aim to render only what the user is currently seeing and then you
will almost never experience performance problems when rendering. However, if
you render big arrays or very complex DOM, you *may* use chunked rendering.
That way the browser won't freeze while rendering.

*Above examples in handlebars syntax:*
```JavaScript
// First example
cuel('loop-template', {}, {
    template: '<ul>{{list}}</ul>',
    loopTemplate: {  list: '<li>{{label}}: {{value}}</li>'}
});
// Second example:
cuel('loop-template', {}, {
    template:     '{{list}}'
    loopTemplate:   {list: 'Value: {{*}}<br>'
});
// Third example:
cuel('loop-template', {}, {
    template: '<ul>{{list}}</ul>',
    loopTemplate: {  list: {
        template: '<li>{{value}}</li>',
        chunk: 500,
    }}
});
```

<a id="4whenToUseHandlebars"></a>
#### When to Use Handlebars

While shorthand syntax is recommended for most cases, handlebars syntax is
useful for specific edge cases:

**Multiple CSS Classes:**
```html
<!-- Can't do this with shorthand -->
<div class="foo bar {{myClass}}"></div>
```

**Complex Nested Structures:**
```html
<!-- When you need explicit control over template structure -->
<div>{{complexNestedContent}}</div>
```

Use shorthand for 90% of cases, and fall back to handlebars when you encounter
these specific edge cases.

<a id="3nestedContentProxies"></a>
### Nested Content-Proxies

Cuel implements data bindings with proxy properties. Your custom element gets
properties (getters and setters) that allow you to access the DOM properties of
bound stuff in your light- or shadow-DOM.

With `ifTemplate` and `loopTemplate` you nest templates inside your custom
element's template. Now nested templates may contain their own bindings. In
particular `loopTemplate` cannot bind to simple proxy properties of your
element, because there is an array of things. Since `loopTemplate` needs
something special there, `ifTemplate` simply behaves in a similar fashion.

Consider this code:

```JavaScript
cuel('if-template', {}, '<span ?=conditional>{{nested}}</span>');

const ift = document.createElement('if-template');
document.body.append(ift);
ift.conditional = {nested: 'foo'}; // now you see 'foo'
ift.conditional.nested = 'bar';    // now you see 'bar'
```

Behind the scenes cuel creates custom elements for your nested templates. And
these custom elements have proxy properties for *their* data binding. So above,
`ift.conditional` is a proxy property of your custom element and it is a
specialized (specialized as *conditional*) node-accessor. See the binding types
above. A *conditional* is a node-binding, i.e. the first kind of content
binding.

The getter returns the custom element created for your nested template. And
that custom element has a proxy property `nested`. The `nested` property of the
`conditional` custom element is a text-(content-)accessor with which you can get
and set the inner text of your template.

You may begin to see, how this is not just consistent with the way things have
to be for `loopTemplate`, but genuinely useful. It may be a bit of hard to grasp
recursive logic at first, but when you get it, it is rather simple, just the
same as cuel is anyway. And it is pretty useful to be able to assign your
data-objects to your DOM-objects and have them rendered as expected.

Now for `loopTemplate`s the node accessor will return an array of custom
elements (each of the same type). Those will have their own proxy properties
(accessors) as defined in your loop template.

Thus you can as easily access you nested dynamic DOM, as you can the rest of
your template. However, cuel does nothing to prevent you from shooting yourself
into your foot. If you try to access DOM that isn't there (possibly not there,
*yet*, because you render chunks), you'll get an exception.

However, if you want change notifications on your nested DOM, your out of luck.
Or rather, you should implement another cuel custom element that is
conditionally (or loopedly) rendered and implements its own change handling.
Keep in mind, though, that events may bubble: in many cases you just want to
handle an event triggered in you dynamic DOM and you can add a handler on a
parent element of that dynamic DOM.

<a id="4rootBindings"></a>
#### "Root" Bindings

In the above example we showed how nested proxies have their own proxy property
accessors. In order to set these properties, you need to pass an object with
the respective properties. For complex nested proxies this is ideal. However,
if you want to set one simple property, that approach is pretty cumbersome.
In order to alleviate this, cuel supports a special syntax here for binding
the whole passed value instead of individual properties:
```JavaScript
cuel('if-template', {}, '<span ?=conditional>{{*}}</span>);

const ift = document.createElement('if-template');
document.body.append(ift);
ift.conditional = 'foo'; // now you see 'foo'
ift.conditional = 'bar'; // now you see 'bar'
ift.conditional = null;  // now you see naught
```


<a id="3style"></a>
### Style

The `style` option allows you to automatically add CSS to the document when creating a cuel component:

```javascript
cuel('styled-component', {}, {
	template: 'content',
	style: ':scope { color: red; background: yellow; }'
});
```

**Scoped Styling**: Styles are automatically scoped to the component using CSS `@scope` at-rule:

```css
@scope (styled-component) {
	:scope {
		color: red;
		background: yellow;
	}
}
```

This ensures styles only apply to your component and don't leak to other
elements on the page.

Please refer to the
[MDN documentation](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@scope)
for more information on the `@scope` at-rule.

**Usage**: Perfect for component-specific styling without needing separate CSS
files or global stylesheets.

<a id="2designConsiderations"></a>
## Design Considerations

This section does not document any of cuel's functionality, but explains some
peculiarities of cuel that I encountered while developing a somewhat complex
application on top of it. I also try to give guidance on how to deal with these
peculiarities.

<a id="3spaArchitecture"></a>
### SPA Architecture

Cuel is designed to be as minimal and maintainable as possible while offering
a minimal reasonable feature set for covering the data binding side of modern
SPAs. The standard architecture of a modern SPA looks something like this:

* Data Store
* Data logic
* Data Binding
* DOM

The by far most complex and extensive code lives behind the DOM part - it's the
API to the native browser code. Cuel fills the Data Binding part with
custom-elements and proxies. The value of good data binding is that it
simplifies the code that uses it, _and makes it easier to test and argue about_!

Now a complex data binding API is counterproductive to that. And that API itself
needs to be tested and argued about. Thus I tried to find a compromise that will
force you to adapt to that simplicity in some places and makes it easy to fall
back on the full complexity of the native APIs where necessary.

The traditional "god frameworks" (like Angular, Vue, React) cover all aspects
of modern SPAs. Cuel only addresses a part of that. You should fill the other
parts with something else.

<a id="3stateManagement"></a>
### State Management

I recommend adding some state management framework. You can also write your own
if you feel confident about that. I use my own JavaScript proxy based state
management library called [xt8](https://codeberg.org/schrotie/xt8). If you have
no idea what to use: if your app is rather complex and you'll have several
people working on it, redux will be a solid choice. If your app is _extremely_
simple you may skip the separate sate management and manage state inside your
cuel components. If your in between xt8 or another simpler state manager may be
a good choice.

<a id="4noExtraProperties"></a>
### No Extra Properties

One peculiarity of cuel that I encountered again and again is that it's very
restrictive with regards to the data you can throw at it. Suppose you have a
component like this:

```JavaScript
cuel('my-component', {}, `
{{shallow}}
{{deep.a}}
{{deep.b}}
`);
```

Now this will work:

```JavaScript
Object.assign(myComponent, {shallow: 'foo', deep: {a: 'bar', b: 'baz'}, c: 'c'});
```

But this will not:

```JavaScript
Object.assign(myComponent, {deep: {a: 'bar', b: 'baz', c: 'c'}});
```

The latter will throw an exception. Nested proxy objects may receive incomplete
data but are not extensible. One reason for doing this is that I want to force
consistency between the data model and the DOM binding. Often an additional
property that is not bound will be a bug. On the other hand, if I supported
that, cuel would have to manage the additional properties you add to the
proxies. Remember, cuel does _not_ manage state, it just offers proxy accessors
to DOM APIs. State is the root of much evil in programming so cuel tries to play
it safe there.

That means you'll have to consider this in your data model design.

<a id="4multiplexing"></a>
### Multiplexing

Another similar point is the following:

```JavaScript
cuel('my-component', {}, `{{a}}{{a}}`);
```

Often you'll want one model property to affect multiple properties in the
DOM - and vice versa. While this would be possible (I implemented it in cuel's
predecessor bindom), it is quite a can of worms. In my opinion the value of
simplicity discussed above trumps the usefulness of this feature.

However, it is relatively simple and straightforward to add this at another
point in your code. Assuming you'll use some state management framework it is
advisable to write some code that facilitates connecting the state to the
databinding. Indeed, if you use cuel's own
[cuel-x](https://codeberg.org/schrotie/cuel-x) functionality to do that binding
for you, you'll already get this multicasting in the connect code. Please refer to [cuel-x](https://codeberg.org/schrotie/cuel-x) documentation for more details.


<a id="4events"></a>
#### Events

Speaking of events - cuel does not notify you about property changes. That means
if you have an input element and you want your code triggered when the value
changes, you should add an event handler for that:

```JavaScript
cuel('my-component', {
	set userText(evt) {
		changeUserTextState(this.userText);
	}
}, `<input type="text" .value=userText !change=userTextChange/>`
);
```

It is quite possible to automate away the event handling. But that is not a
transparent API. Cuel would need to choose the event for you (`change`, `input`,
`keyDown`, etc.) and it would need to cover several other cases like checkbox
value and so on. All the while that would not save you all that much code on
your side. So the call for simplicity again trumps such features.

As a simple guideline: state changes should usually trigger DOM write accessors
while DOM events should call state action handlers.

---
_Source: https://npm.io/package/cuel · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
