---
name: aberdeen
description: Expert guidance for building reactive UIs with the Aberdeen library. Covers element creation, reactive proxy state, efficient list rendering, CSS shortcuts, UI components, routing, transitions, and optimistic updates.
---

Aberdeen is a reactive UI library using fine-grained reactivity via JavaScript Proxies. No virtual DOM, no build step required.

# Guidance for AI Assistants

1. **Never concatenate user data** - Use `A('input value=', data)` not `A('input value=${data}')`
2. **Pass observables directly** - Use `text=', ref(obj, 'key')` to avoid parent scope subscriptions
3. **Use `onEach` for lists** - Never iterate proxy arrays with `for`/`map` in render functions
4. **Class instances are great** - Better than plain objects for typed, structured state
5. **CSS shortcuts** - Use $3, $4 for spacing (1rem, 2rem), $primary for colors (assuming setVarSpacingCssVars is used and cssVars colors are defined)
6. **Minimal scopes** - Smaller reactive scopes = fewer DOM updates
7. **Function components** - Create reusable UI components as regular functions starting with 'draw' (like drawMainMenu(settings) or drawProfilePage(user))
8. **Prefix proxied objects** - As a convention, prefix variable names that contain proxied objects with '$' (e.g. `$user`, `$settings`)
9. **Think about rerenders** - When you read from a proxied object (like `let n = $user.name;`), the containing A(() => {..}) or A('div', () => {}) function will rerun on change - plan on which level you want updates to trigger


# Tutorial

## Creating elements

This is a complete Aberdeen application:

```javascript
import A from 'aberdeen';
A('h3#Hello world');
```

It adds a `<h3>Hello world</h3>` element to the `<body>` (which is the default mount point).

The {@link aberdeen.A} function accepts various forms of arguments, which can be combined.

When a string is passed:
- The inital part (if any) is the name of the element to be created.
- One or multiple CSS classes can be added to the 'current' element, by prefixing them with a `.`.
- Content text can be added by prefixing it with a `#`. This has to be the last thing in the string: the text runs on to the end of it, so anything written after the `#` becomes part of the text.

Instead of the `#` prefix for text content, you can also use the `text=` property, like this: `A('h3 text="Hello world"')`. The double quotes are needed here only because our text contains a space. This is also how to give an element text when more should follow in the same string, as `#` would swallow it: `A('button text=Undo click=', undo)` does what you'd expect, while `A('button #Undo click=', undo)` gives you a button labelled `Undo click=` — and, since nothing is left waiting for it, `undo` is taken as a content function and run right there.

For simple formatting, use `rich=` which supports `*italic*`, `**bold**`, `` `code` ``, and `[links](url)`:

```javascript
A('p rich="This is *italic*, **bold**, and `code` with a [link](/path)."');
```

`A()` can accept multiple strings, so the following lines are equivalent:

```javascript
A('button.outline.secondary#Pressing me does nothing!');
A('button', '.outline', '.secondary', '#Pressing me does nothing!');
```

Also, we can create multiple nested DOM elements in a single {@link aberdeen.A} invocation, *if* the parents need to have only a single child. For instance:

```javascript
A('div.box', '#Text within the div element...', 'input');
```

Note that you can play around, modifying any example while seeing its live result by pressing the *Edit* button that appears when hovering over an example!

In order to pass in additional properties and attributes to the 'current' DOM element, we can use the `key=value` or `key=`, value syntax. So to extend the above example:

```javascript
A('div.box id=cityContainer input value=London placeholder=City');
```

Note that `value` doesn't become an HTML attribute. This (together with `selectedIndex`) is one of two special cases, where Aberdeen applies it as a DOM property instead, in order to preserve the variable type (as attributes can only be strings).

When a value ends with `=`, the next argument is used as its value. This is used for dynamic values and event listeners. So to always log the current input value to the console you can do:

```javascript
A('div.box input value=Marshmallow input=', el => console.log(el.target.value));
```

Note that the example is interactive - try typing something!

> **Note:** {@link aberdeen.A} also accepts object syntax as an alternative to strings (see the API reference), but the string syntax shown here is more concise and is recommended for most use cases.

## Inline styles

To set inline CSS styles on elements, use the `property:value` (short form) or `property: value containing spaces;` (long form) syntax:

```javascript
A('p color:red padding:8px background-color:#a882 border: 2px solid #a884; #Styled text');
```

### Property shortcuts

Aberdeen provides shortcuts for commonly used CSS properties, making your code more concise.

| Shortcut | Expands to |
|----------|------------|
| `m`, `mt`, `mb`, `ml`, `mr` | `margin`, `margin-top`, `margin-bottom`, `margin-left`, `margin-right` |
| `mv`, `mh` | Vertical (top+bottom) or horizontal (left+right) margins |
| `p`, `pt`, `pb`, `pl`, `pr` | `padding`, `padding-top`, `padding-bottom`, `padding-left`, `padding-right` |
| `pv`, `ph` | Vertical or horizontal padding |
| `w`, `h` | `width`, `height` |
| `bg` | `background` |
| `fg` | `color` |
| `r` | `border-radius` |

```javascript
A('div mv:10px ph:20px bg:lightblue r:10% #Styled box');
```

### CSS variables

Values starting with `$` expand to native CSS custom properties via `var(--name)`. The {@link aberdeen.cssVars | A.cssVars} object offers a convenient way of setting and updating CSS custom properties at the `:root` level.

When you add the first property to `A.cssVars`, Aberdeen automatically creates a reactive `<style>` tag in `<head>` containing the CSS custom property declarations.

```javascript
import A from 'aberdeen';

A.cssVars.primary = '#3b82f6';
A.cssVars.danger = '#ef4444';
A.cssVars.textLight = '#f8fafc';

A('button bg:$primary fg:$textLight #Primary');
A('button bg:$danger fg:$textLight #Danger');
```

The above generates CSS like `background: var(--primary)` and automatically injects a `:root` style defining the actual values. Since this uses native CSS custom properties, changes to `A.cssVars` automatically propagate to all elements using those values.

### Spacing variables

You can optionally initialize `A.cssVars` with keys `1` through `12` mapping to an exponential `rem` scale using {@link aberdeen.setSpacingCssVars | A.setSpacingCssVars}. Since CSS custom property names can't start with a digit, numeric keys are prefixed with `m` (e.g., `$3` becomes `var(--m3)`):

```javascript
import A from 'aberdeen';

A.setSpacingCssVars(); // Default: base=1, unit='rem'
// Or customize: A.setSpacingCssVars(16, 'px') or A.setSpacingCssVars(1, 'em')
```

| Value | CSS Output | Result (default) |
|-------|------------|------------------|
| `$1` | `var(--m1)` | 0.25rem |
| `$2` | `var(--m2)` | 0.5rem |
| `$3` | `var(--m3)` | 1rem |
| `$4` | `var(--m4)` | 2rem |
| `$5` | `var(--m5)` | 4rem |
| ... | ... | 2^(n-3) rem |

```javascript
A('div mt:$3 ph:$4 #This text has 1rem top margin, 2rem left+right padding');
```

If you want different spacing, you can customize the base and unit when calling `A.setSpacingCssVars()`, or dynamically modify the values.

These shortcuts and variables are also available when using {@link aberdeen.insertCss | A.insertCss}.

## Nesting content
Of course, putting everything in a single {@link aberdeen.A} call will get messy soon, and you'll often want to nest more than one child within a parent. To do that, you can pass in a *content* function to {@link aberdeen.A}, like this:

```javascript
A('div.box.row id=cityContainer', () => {
    A('input value=London placeholder=City');
    A('button text=Confirm click=', () => alert("You got it!"));
});
```

Why are we passing in a function instead of just, say, an array of children? I'm glad you asked! :-) For each such function Aberdeen will create an *observer*, which will play a major part in what comes next...

## Observable objects
Aberdeen's reactivity system is built around observable objects. These are created using the {@link aberdeen.proxy | A.proxy} function:

By convention variables that hold proxied values are prefixed with `$` so reactive reads stand out.

When you access properties of a proxied object within an observer function (the function passed to {@link aberdeen.A}), Aberdeen automatically tracks these dependencies. If the values change later, the observer function will re-run, updating only the affected parts of the DOM.

```javascript
import A from 'aberdeen';

const $user = A.proxy({
    name: 'Alice',
    age: 28,
    city: 'Aberdeen',
});

A('div', () => {
    A(`h3#Hello, ${$user.name}!`);
    A(`p#You are ${$user.age} years old.`);
});

setInterval(() => {
    $user.name = 'Bob';
    $user.age++;
}, 2000);
```

As the content function of our `div` is subscribed to both `$user.name` and `$user.age`, modifying either of these would trigger a re-run of that function, first undoing any side-effects (most notably: inserting DOM elements) of the earlier run. If, however `$user.city` is changed, no re-run would be triggered as the function is not subscribed to that property.

So if either property changes, both the `<h3>` and `<p>` are recreated as the inner most observer function tracking the changes is re-run. If you want to redraw on an even granular level, you can of course:

```javascript
const $user = A.proxy({
    name: 'Alice',
    age: 28,
});

A('div', () => {
    A(`h3`, () => {
        console.log('Name draws:', $user.name)
        A(`#Hello, ${$user.name}!`);
    });
    A(`p`, () => {
        console.log('Age draws:', $user.age)
        A(`#You are ${$user.age} years old.`);
    });
});

setInterval(() => {
    $user.age++;
}, 2000);
```

Now, updating `$user.name` would only cause the *Hello* text node to be replaced, leaving the `<div>`, `<h3>` and `<p>` elements as they were.

## Conditional rendering

Within an observer function (such as created by passing a function to {@link aberdeen.A}), you can use regular JavaScript logic. Like `if` and `else`, for instance:

```javascript
const $user = A.proxy({
    loggedIn: false
});

A('div', () => {
    if ($user.loggedIn) {
        A('button.outline text=Logout click=', () => $user.loggedIn = false);
    } else {
        A('button text=Login click=', () => $user.loggedIn = true);
    }
});
```

## Observable primitive values

The {@link aberdeen.proxy | A.proxy} method wraps an object in a JavaScript [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). As this doesn't work for primitive values (like numbers, strings and booleans), the method will *create* an object in order to make it observable. The observable value is made available as its `.value` property.

```javascript
const $count = A.proxy(42);
A('div.row', () => {
    // This scope will not have to redraw
    A('button text=- click=', () => $count.value--);
    A('div text=', $count);
    A('button text=+ click=', () => $count.value++);
});
```

The reason the `div.row` scope doesn't redraw when `$count.value` changes is that we're passing the entire `$count` observable object to the `text:` property. Aberdeen then internally subscribes to `$count.value` for just that text node, ensuring minimal updates.

If we would have done `A('div', {text: $count.value});` instead, we *would* have subscribed to `$count.value` within the `div.row` scope, meaning we'd be redrawing the two buttons and the div every time the count changes.

This also works for other properties, such as inline styles:

```javascript
import A from 'aberdeen';

const $textColor = A.proxy('blue');

A('div.box color:', $textColor, '#Click me to change color', 'click=', () => {
    $textColor.value = $textColor.value === 'blue' ? 'red' : 'blue';
});
```

This way, when `$textColor.value` changes, only the style is updated without recreating the element.

## Observable arrays and sets

You can create observable arrays too. They work just like regular arrays, apart from being observable.

```javascript
const $items = A.proxy([1, 2, 3]);

A('h3', () => {
    // This subscribes to the length of the array and to the value at `$items.length-1` in the array.
    A('#Last item: '+$items[$items.length-1]);
})

A('ul', () => {
    // This subscribes to the entire array, and thus redraws all <li>s when any item changes.
    // In the next section, we'll learn about a better way.
    for (const item of $items) {
        A(`li#Item ${item}`);
    }
});

A('button text=Add click=', () => $items.push($items.length+1));
```

Observable `Set`s work too. They preserve normal Set semantics, including `.size`. When you iterate them with `A.onEach()`, by default they are sorted by value (or an error is thrown if the value is not a number, string or an array of those).

```javascript
const $tags = A.proxy(new Set(['ui', 'tiny']));

A('div', () => {
    A(`#Tag count: ${$tags.size}`);
});

A('ul', () => {
    A.onEach($tags, tag => { // Ordered by tag
        A(`li#${tag}`);
    });
});

A('button text=Add fast click=', () => $tags.add('fast'));
```

## TypeScript and classes

Though this tutorial mostly uses plain JavaScript to explain the concepts, Aberdeen is written in and aimed towards TypeScript.

Class instances, like any other object, can be proxied to make them reactive.

```typescript
class Widget {
    constructor(public name: string, public width: number, public height: number) {}
    grow() { this.width *= 2; }
    toString() { return `${this.name}Widget (${this.width}x${this.height})`; }
}

let $graph: Widget = A.proxy(new Widget('Graph', 200, 100));

A('h3', () => A('#'+$graph));
A('button text=Grow click=', () => $graph.grow());
```

The type returned by {@link aberdeen.proxy | A.proxy} matches the input type, meaning the type system does not distinguish proxied and unproxied objects. That makes sense, as they have the exact same methods and properties (though proxied objects may have additional side effects).

## Efficient list rendering with A.onEach
For rendering lists efficiently, Aberdeen provides the {@link aberdeen.onEach | A.onEach} function. It takes three arguments:
1. The array to iterate over.
2. A render function that receives the item and its index.
3. An optional order function, that returns the value by which the item is to be sorted. By default, the output is sorted by array index.

```javascript
import A from 'aberdeen';

const $items = A.proxy([]);

const randomInt = (max) => parseInt(Math.random() * max);
const randomWord = () => Math.random().toString(36).substring(2, 12).replace(/[0-9]+/g, '').replace(/^\w/, c => c.toUpperCase());

// Make random mutations
setInterval(() => {
    if (randomInt(3)) $items[randomInt(7)] = {label: randomWord(), prio: randomInt(4)};
    else delete $items[randomInt(7)];
}, 500);

A('div.row.wide height:250px', () => {
    A('div.box#By index', () => {
        A.onEach($items, ($item, index) => {
            // Called only for items that are created/updated
            A(`li#${$item.label} (prio ${$item.prio})`)
        });
    })
    A('div.box#By label', () => {
        A.onEach($items, ($item, index) => {
            A(`li#${$item.label} (prio ${$item.prio})`)
        }, $item => $item.label);
    })
    A('div.box#By desc prio, then label', () => {
        A.onEach($items, ($item, index) => {
            A(`li#${$item.label} (prio ${$item.prio})`)
        }, $item => [-$item.prio, $item.label]);
    })
})
```

We can also use {@link aberdeen.onEach | A.onEach} to reactively iterate over *objects*, arrays, `Map`s and `Set`s. For objects and `Map`s, the render and order functions receive `(value, key)` instead of `(value, index)`. For `Set`s, they receive only the value. By default, Sets are ordered by that value, which only works for numbers, strings and arrays of those, so Sets of objects need an explicit order function.

```javascript
const $pairs = A.proxy({A: 'Y', B: 'X',});

const randomWord = () => Math.random().toString(36).substring(2, 12).replace(/[0-9]+/g, '').replace(/^\w/, c => c.toUpperCase());

A('button text="Add item" click=', () => $pairs[randomWord()] = randomWord());

A('div.row.wide margin-top:1em', () => {
    A('div.box#By key', () => {
        A.onEach($pairs, (value, key) => {
            A(`li#${key}: ${value}`)
        });
    })
    A('div.box#By desc value', () => {
        A.onEach($pairs, (value, key) => {
            A(`li#${key}: ${value}`)
        }, value => A.invertString(value));
    })
})
```

Note the use of the provided {@link aberdeen.invertString | A.invertString} function to reverse-sort by a string value.

The order function runs in a reactive scope of its own. When observable data that it reads changes the resulting sort key, the item's DOM elements are *moved* to their new position without being redrawn. State like `<input>` values survives such a move, and in browsers supporting the `moveBefore` API, so do focus, text selection and CSS animations. Of course, any parts of the item that reactively depend on the changed data will still update (in place), as usual.

## Two-way binding
Aberdeen makes it easy to create two-way bindings between form elements (the various `<input>` types, `<textarea>` and `<select>`) and your data, by passing an observable object with a `.value` as `bind:` property to {@link aberdeen.A}.

To bind to object properties not named `.value` (e.g., `$user.name`), use {@link aberdeen.ref | A.ref}. This creates a new observable A.proxy whose `.value` property directly maps to the specified property (e.g., `name`) on your original observable object (e.g., `$user`).

```javascript
import A from 'aberdeen';

const $user = A.proxy({
    name: 'Alice',
    active: false
});

// Text input binding
A('input placeholder=Name bind=', A.ref($user, 'name'));

// Checkbox binding
A('label', () => {
    A('input type=checkbox bind=', A.ref($user, 'active'));
}, '#Active');

// Display the current state
A('div.box', () => {
    A(`p#Name: ${$user.name} `, () => {
        // Binding works both ways
        A('button.outline.secondary#!', {
            click: () => $user.name += '!'
        });
    });
    A(`p#Status: ${$user.active ? 'Active' : 'Inactive'}`);
});
```

## CSS
Through the {@link aberdeen.insertCss | A.insertCss} function, Aberdeen provides a way to create component-local CSS.

For simple single-element styles, you can pass a string directly:

```javascript
import A from 'aberdeen';

const simpleCard = A.insertCss("bg:#f0f0f0 p:$3 r:8px");
A('div', simpleCard, '#Card content');
```

For more complex styles with nested selectors, pass an object where each key is a selector and each value is a style string using the same `property:value` syntax as inline styles:

```javascript
import A from 'aberdeen';

// Create a CSS class that can be applied to elements
const myBoxStyle = A.insertCss({
    "&": "border-color:#6936cd background-color:#1b0447",
    "button": "background-color:#6936cd border:0 transition: box-shadow 0.3s; box-shadow: 0 0 4px #ff6a0044;",
    "button:hover": "box-shadow: 0 0 16px #ff6a0088;",
    "@media (max-width: 600px)": "p:$1", // Media query is scoped to myBoxStyle as well
});

// myBoxStyle is now something like ".AbdStl1", the name for a generated CSS class.
// Here's how to use it:
A('div.box', myBoxStyle, 'button#Click me');
```

The `"&"` selector refers to the element with the generated class itself. Child selectors like `"button"` are scoped to descendants of that element, while pseudo-selectors like `"&:hover"` apply to the element itself.

This allows you to create single-file components with advanced CSS rules. The {@link aberdeen.insertGlobalCss | A.insertGlobalCss} function can be used to add CSS without a class prefix - it accepts the same string or object syntax.

Both functions support the same CSS shortcuts and variables as inline styles (see above). For example:

```javascript
import A from 'aberdeen';
A.cssVars.boxBg = '#f0f0e0';
A.insertGlobalCss({
    "body": "m:0", // Using shortcut for margin
    "form": "bg:$boxBg mv:$3" // Using background shortcut, CSS variable, and spacing value
});
```

Of course, if you dislike JavaScript-based CSS and/or prefer to use some other way to style your components, you can just ignore this Aberdeen function.

## Transitions
Aberdeen allows you to easily apply transitions on element creation and element destruction:

```javascript
let titleStyle = A.insertCss({
    "&": "transition: all 1s ease-out; transform-origin: left center;",
    "&.faded": "opacity:0",
    "&.imploded": "transform:scale(0.1)",
    "&.exploded": "transform:scale(5)"
});

const $show = A.proxy(true);
A('label', () => {
    A('input type=checkbox bind=', $show);
    A('#Show title');
});
A(() => {
    if (!$show.value) return;
    A('h2#(Dis)appearing text', titleStyle, 'create=faded.imploded destroy=faded.exploded');
});
```

- The creation transition works by briefly adding the given CSS classes on element creation, and immediately removing them after the initial browser layout has taken place.
- The destruction transition works by delaying the removal of the element from the DOM by two seconds (currently hardcoded - should be enough for any reasonable transition), while adding the given CSS classes.

Though this approach is easy (you just need to provide some CSS), you may require more control over the specifics, for instance in order to animate the layout height (or width) taken by the element as well. (Note how the document height changes in the example above are rather ugly.) For this, `create` and `destroy` may be functions instead of CSS class names. For more control, create and destroy can also accept functions. While custom function details are beyond this tutorial, Aberdeen offers ready-made {@link transitions.grow} and {@link transitions.shrink} transition functions (which also serve as excellent examples for creating your own):

```javascript
import A from 'aberdeen';
import { grow, shrink } from 'aberdeen/transitions';

const $items = A.proxy([]);

const randomInt = (max) => parseInt(Math.random() * max);
const randomWord = () => Math.random().toString(36).substring(2, 12).replace(/[0-9]+/g, '').replace(/^\w/, c => c.toUpperCase());

// Make random mutations
setInterval(() => {
    if (randomInt(3)) $items[randomInt(7)] = {label: randomWord(), prio: randomInt(4)};
    else delete $items[randomInt(7)];
}, 500);

A('div.row.wide height:250px', () => {
    A('div.box#By index', () => {
        A.onEach($items, ($item, index) => {
            A(`li#${$item.label} (prio ${$item.prio})`, {create: grow, destroy: shrink})
        });
    })
    A('div.box#By label', () => {
        A.onEach($items, ($item, index) => {
            A(`li#${$item.label} (prio ${$item.prio})`, {create: grow, destroy: shrink})
        }, $item => $item.label);
    })
    A('div.box#By desc prio, then label', () => {
        A.onEach($items, ($item, index) => {
            A(`li#${$item.label} (prio ${$item.prio})`, {create: grow, destroy: shrink})
        }, $item => [-$item.prio, $item.label]);
    })
});
```

## Advanced: Peeking without subscribing

Sometimes you need to read reactive data inside an observer scope without creating a subscription to that data. The {@link aberdeen.peek | A.peek} function allows you to do this:

```javascript
import A from 'aberdeen';

const $data = A.proxy({ a: 1, b: 2 });

A(() => {
    // This scope only re-runs when $data.a changes
    // Changes to $data.b won't trigger a re-render
    A(`h2#a == ${$data.a} && b == ${A.peek($data, 'b')}`);
});

A(`button text="a++ (will update)" click=`, () => $data.a++);
A(`button ml:1rem text="b++ (won't update)" click=`, () => $data.b++);
```

You can also pass a function to `A.peek()` to execute it without any subscriptions:

```javascript
const $a = A.proxy(42);
const $b = A.proxy(7);
const sum = A.peek(() => $a.value + $b.value); // Reads both without subscribing
A('#Sum is: '+sum);
setInterval(() => $a.value++, 1000); // Won't update
```

This can be useful to avoid rerenders (of even rerender loops) when you only need a point-in-time snapshot of some reactive data.

## Derived values
An observer scope doesn't *need* to create DOM elements. It may also perform other side effects, such as modifying other observable objects. For instance:

```javascript
// NOTE: See below for a better way.
const $original = A.proxy(1);
const $derived = A.proxy();
A(() => {
    $derived.value = $original.value * 42;
});

A('h3 text=', $derived);
A('button text=Increment click=', () => $original.value++);
```

The {@link aberdeen.derive | A.derive} function makes the above a little easier. It works just like passing a function to {@link aberdeen.A}, creating an observer, the only difference being that the value returned by the function is reactively assigned to the `value` property of the observable object returned by `derive`. So the above could also be written as:

```javascript
const $original = A.proxy(1);
const $derived = A.derive(() => $original.value * 42);

A('h3 text=', $derived);
A('button text=Increment click=', () => $original.value++);
```

For deriving values from (possibly large) arrays, objects, Maps or Sets, Aberdeen provides specialized functions that enable fast, incremental updates to derived data: {@link aberdeen.map | A.map} (each item becomes zero or one derived item), {@link aberdeen.multiMap | A.multiMap} (each item becomes any number of derived items), {@link aberdeen.count | A.count} (reactively counts the number of object properties or collection items), {@link aberdeen.isEmpty | A.isEmpty} (true when the object/array/Map/Set has no items) and {@link aberdeen.partition | A.partition} (sorts each item into one or more buckets). An example:

```javascript
import A from 'aberdeen';

// Create some random data
const $people = A.proxy({});
const randomInt = (max) => parseInt(Math.random() * max);
setInterval(() => {
    $people[randomInt(250)] = {height: 150+randomInt(60), weight: 45+randomInt(90)};
}, 250);

// Do some mapping, counting and observing
const $totalCount = A.count($people);
const $bmis = A.map($people,
    $person => Math.round($person.weight / (($person.height/100) ** 2))
);
const $overweightBmis = A.map($bmis, // Use A.map() as a filter
    bmi => bmi > 25 ? bmi : undefined
); 
const $overweightCount = A.count($overweightBmis);
const $message = A.derive(
    () => `There are ${$totalCount.value} people, of which ${$overweightCount.value} are overweight.`
);

// Show the results
A('p text=', $message);
A(() => {
    // isEmpty only causes a re-run when the count changes between zero and non-zero
    if (A.isEmpty($overweightBmis)) return;
    A('p#These are their BMIs:', () => {
        A.onEach($overweightBmis, bmi => A('# '+bmi), bmi => -bmi);
        // Sort by descending BMI
    });
})
```

## UI Components

UI Components in Aberdeen are just functions, named `draw<Something>` by convention, that use {@link aberdeen.A} to create some DOM structure. They can accept arguments, return (proxied) values and create local (proxied) state just like any other function.

```javascript
function drawCounter(initialValue = 0) {
    const $count = A.proxy(initialValue);
    A('div.row', () => {
        A('button text=- click=', () => $count.value--);
        A('div text=', $count);
        A('button text=+ click=', () => $count.value++);
    });
    return $count; // Return the reactive count value for external use
}
// Create multiple independent instances:
drawCounter();
const $second = drawCounter(42);
A('input value=', $second); // Bind the second counter to an input field
```

## Debugging with A.dump()

The {@link aberdeen.dump | A.dump} function creates a live, interactive tree view of any data structure in the DOM. It's particularly useful for debugging reactive state:

```javascript
import A from 'aberdeen';

const $state = A.proxy({
    user: { name: 'Frank', kids: 1 },
    items: ['a', 'b']
});

A('h2#Live State Dump');
A.dump($state);

// The A.dump updates automatically as $state changes
A('button text="Update state" click=', () => {
    $state.user.kids++;
    $state.items.push('new');
});
```

The A.dump renders recursively using `<ul>` and `<li>` elements, showing all properties and their values. It updates reactively when any proxied data changes. It is intended for debugging, though with some CSS styling you may find it useful in some simple real-world scenarios as well.

## Developer tools

Aberdeen has an in-browser inspector for its reactive scope tree. See [developer-tools.md](developer-tools.md).

## html-to-aberdeen

This tool allows you to convert a block of HTML into Aberdeen syntax. See [html-to-aberdeen.md](html-to-aberdeen.md).

## Routing

Aberdeen provides an optional built-in router via the {@link route} module. The router is reactive and integrates seamlessly with browser history.

The {@link route.current} object is an observable that reflects the current URL:

```javascript
import A from 'aberdeen';
import * as route from 'aberdeen/route';

A(() => {
    A(`p#Path string: ${route.current.path}`); // eg "/example/123"
    A(`p#Path segments: ${JSON.stringify(route.current.p)}`); // eg ["example", "123"]
});
```

To navigate programmatically, use {@link route.go}:

```javascript
import A from 'aberdeen';
import * as route from 'aberdeen/route';

A('h2', () => A('#', route.current.path));

A('button#Go to settings', {
    click: () => route.go('/settings')
});

// Or using path segments
A('button ml:1rem #Go to user 123', {
    click: () => route.go({p: ['users', 123]})
});
```

For convenience, you can call {@link route.interceptLinks} once to automatically convert clicks on local `<a>` tags into Aberdeen routing, so you can use regular anchor tags without manual click handlers. Example: `A('a href=/settings text=Settings')`.

```javascript
import A from 'aberdeen';
import * as route from 'aberdeen/route';

route.interceptLinks(); // Just once on startup

A('h2', () => A('#', route.current.path));

A('a role=button href=/settings #Go to settings')
```

The {@link route.push} function is useful for overlays that should be closeable with browser back:

```javascript
import A from 'aberdeen';
import * as route from 'aberdeen/route';

A('button#Open modal', {
    click: () => route.push({state: {modal: 'settings'}})
});

A(() => {
    if (!route.current.state.modal) return;
    A('div.modal-overlay', {
        click: () => route.back({state: {modal: undefined}})
    }, () => {
        A('div.modal#Modal content here');
    });
});
```

When some pages shouldn't be left without asking (think unsaved changes), register a navigation guard using {@link route.setGuard}. It's consulted before any navigation — including the browser's own back/forward buttons, which are undone when the guard answers `false`:

```javascript
import A from 'aberdeen';
import * as route from 'aberdeen/route';

const doc = A.proxy({dirty: true});
route.setGuard(() => !doc.dirty || confirm('Discard unsaved changes?'));

A('button#Go elsewhere', {
    click: () => route.go('/elsewhere')
});
```

Guards may be async, and navigation functions report whether the navigation actually happened — see the {@link route.setGuard} reference for details.

Optionally, you can use the {@link dispatcher.Dispatcher} class for declarative routing. It allows you to register route patterns with associated handler functions, which are invoked when the current route matches the pattern. It can match typed parameters and rest parameters.

## Prediction

When building interactive applications with client-server communication, Aberdeen's prediction system allows for optimistic UI updates. The {@link prediction.applyPrediction} function records changes to any proxied objects made within its callback. These changes are treated as *predictions* that may later be confirmed or reverted based on server responses. When a server response arrives, the {@link prediction.applyCanon} function applies authoritative changes from the server, reverting any conflicting predictions while attempting to reapply non-conflicting ones.

## Full Example: Multi-page App

Here's a complete example (a contact manager) demonstrating routing, state management, CSS, dark mode, and dynamic content. See [full-example-multi-page-app.md](full-example-multi-page-app.md).

## Further reading

If you've understood all/most of the above, you should be ready to get going with Aberdeen! You may also find these links helpful:

- [Reference documentation](https://aberdeenjs.org/modules.html)
- [Examples](https://aberdeenjs.org/#examples)

# API Reference

The sections below summarize each module's exports; the linked `.md` files within this skill directory contain detailed reference docs for individual symbols.

## Core (aberdeen)

Import as `import A from 'aberdeen'`. `A` is itself a callable function for building reactive DOM (creating elements, setting attributes, adding content); every other Aberdeen function is also available as a property on it (e.g. `A.proxy`, `A.onEach`).

## [runQueue](runQueue.md) · function

Forces the immediate and synchronous execution of all pending reactive updates.

## [freeze](freeze.md) · function

Pause processing of reactive updates until the returned *thaw* function is called.

## [invertString](invertString.md) · function

Creates a new string that has the opposite sort order compared to the input string.

## [onEach](onEach.md) · function

## [isEmpty](isEmpty.md) · function

Reactively checks if an observable array, object, Map, or Set is empty.

## [count](count.md) · function

Reactively counts the number of properties in an object.

## [proxy](proxy.md) · function

## [unproxy](unproxy.md) · function

Returns the original, underlying data target from a reactive proxy created by `proxy`.
If the input `target` is not a proxy, it is returned directly.

## [copy](copy.md) · function

Recursively copies properties or array items from `src` to `dst`.
It's designed to work efficiently with reactive proxies created by `proxy`.

## [merge](merge.md) · function

Like `copy`, but uses merge semantics. Properties in `dst` not present in `src` are kept.
`null`/`undefined` in `src` delete properties in `dst`.

## [setSpacingCssVars](setSpacingCssVars.md) · function

Initializes `cssVars[0]` through `cssVars[12]` with an exponential spacing scale.

## [darkMode](darkMode.md) · function

Returns whether the user's browser prefers a dark color scheme.

## [clone](clone.md) · function

Clone an (optionally proxied) object or array.

## [ref](ref.md) · function

Creates a reactive reference (`{ value: T }`-like object) to a specific value
within a proxied object or array.

## [disableCreateDestroy](disableCreateDestroy.md) · function

Make the `create` and `destroy` special properties no-ops.

## [A](A.md) · function

The core function for building reactive user interfaces in Aberdeen. It creates and inserts new DOM elements
and sets attributes/properties/event listeners on DOM elements. It does so in a reactive way, meaning that
changes will be (mostly) undone when the current *scope* is destroyed or will be re-execute.

## [insertCss](insertCss.md) · function

Inserts CSS rules into the document, scoping them with a unique class name.

## [insertGlobalCss](insertGlobalCss.md) · function

Inserts CSS rules globally (unscoped).

## [setErrorHandler](setErrorHandler.md) · function

Sets a custom error handler function for errors that occur asynchronously
within reactive scopes (e.g., during updates triggered by proxy changes in
`derive` or | A render functions).

## [clean](clean.md) · function

Registers a cleanup function to be executed just before the current reactive scope
is destroyed or redraws.

## [derive](derive.md) · function

Creates a reactive scope that automatically re-executes the provided function
whenever any proxied data (created by `proxy`) read during its last execution changes, storing
its return value in an observable.

## [mount](mount.md) · function

Attaches a reactive Aberdeen UI fragment to an existing DOM element. Without the use of
this function, | A will assume `document.body` as its root.

## [unmountAll](unmountAll.md) · function

Removes all Aberdeen-managed DOM nodes and stops all active reactive scopes
(created by `mount`, `derive`, | A with functions, etc.).

## [peek](peek.md) · function

Executes a function or retrieves a value *without* creating subscriptions in the current reactive scope, and returns its result.

## [map](map.md) · function

When using a Map as `source`.

## [multiMap](multiMap.md) · function

When using an array as `source`.

## [partition](partition.md) · function

When using an object as `array`.

## [dump](dump.md) · function

Renders a live, recursive dump of a proxied data structure (or any value)
into the DOM at the current | A insertion point.

## EMPTY · constant

**Value:** `unique symbol`

## ValueRef · interface

**Type Parameters:**

- `T`

### valueRef.value · member

**Type:** `T`

## [PromiseProxy](PromiseProxy.md) · interface

When `proxy` is called with a Promise, the returned object has this shape.

## [OPAQUE](OPAQUE.md) · constant

A symbol that controls how Aberdeen handles an object in copy operations and proxy wrapping.

## NO_COPY · constant

Use `OPAQUE` instead. This is an alias kept for backward compatibility.

**Value:** `symbol`

## [cssVars](cssVars.md) · constant

A reactive object containing CSS variable definitions.

## CUSTOM_DUMP · constant

When set on an object or its prototype chain, `dump` calls this as a render function
(with the object as `this`) instead of its default recursive rendering. If the value is not 
a function, it's treated as a string to display.

**Value:** `unique symbol`


## Routing (aberdeen/route)

## [setLog](setLog.md) · function

Configure logging on route changes.

## [setGuard](setGuard.md) · function

Register a navigation guard, or unregister it by passing `null`. At most one
guard can be registered at a time; the previously registered guard (or
`null`) is returned, so a temporary guard can be chained or restored later.

## [go](go.md) · function

Navigate to a new URL by pushing a new history entry.

## [matchCurrent](matchCurrent.md) · function

Returns `true` if the current route matches `target`.

## [push](push.md) · function

Modify the current route by merging `target` into it (using | A.merge), pushing a new history entry.

## [back](back.md) · function

Try to go back in history to the first entry that matches the given target. If none is found, the given state will replace the current page. This is useful for "cancel" or "close" actions that should return to the previous page if possible, but create a new page if not (for instance when arriving at the current page through a direct link).

## [up](up.md) · function

Navigate up in the path hierarchy, by going back to the first history entry
that has a shorter path than the current one. If there's none, we just shorten
the current path.

## [persistScroll](persistScroll.md) · function

Restore and store the vertical and horizontal scroll position for
the parent element to the page state.

## [interceptLinks](interceptLinks.md) · function

Intercept clicks and Enter key presses on links (`<a>` tags) and use Aberdeen routing
instead of browser navigation for local paths (paths without a protocol or host).

## [Route](Route.md) · interface

The class for the global `route` object.

## RouteGuard · type

A navigation guard, as registered by `setGuard`: called with the route
we're about to move to and the route we're at now, before the change is
applied. Return `false` — or a promise resolving to `false` — to veto the
change; any other return value lets it through.

**Type:** `(to: Route, from: Route) => boolean | Promise<boolean>`

## current · constant

The global `Route` object reflecting the current URL and browser history state. Changes you make to this affect the current browser history item (modifying the URL if needed).

**Value:** `Route`

## [LinkHandler](LinkHandler.md) · type

A link handler, as optionally passed to `interceptLinks`: called for
every local-link activation that passed the built-in exclusions, with the
resolved URL, the anchor element the activation landed on, and the DOM event.


## Path matching (aberdeen/dispatcher)

## MATCH_FAILED · constant

Symbol to return when a custom `Dispatcher.addRoute` matcher cannot match a segment.

**Value:** `unique symbol`

## MATCH_REST · constant

Special `Dispatcher.addRoute` matcher that matches the rest of the segments as an array of strings.

**Value:** `unique symbol`

## [Dispatcher](Dispatcher.md) · class

Simple route matcher and dispatcher.


## Optimistic UI (aberdeen/prediction)

## [applyPrediction](applyPrediction.md) · function

Run the provided function, while treating all changes to Observables as predictions,
meaning they will be reverted when changes come back from the server (or some other
async source).

## [applyCanon](applyCanon.md) · function

Temporarily revert all outstanding predictions, optionally run the provided function
(which will generally make authoritative changes to the data based on a server response),
and then attempt to reapply the predictions on top of the new canonical state, dropping
any predictions that can no longer be applied cleanly (the data has been modified) or
that were specified in `dropPredictions`.

## Patch · type

Represents a set of changes that can be applied to proxied objects.
This is an opaque type - its internal structure is not part of the public API.

**Type:** `Map<TargetType, Map<any, [any, any]>>`


## Transitions (aberdeen/transitions)

## [grow](grow.md) · function

Do a grow transition for the given element. This is meant to be used as a
handler for the `create` property.

## [shrink](shrink.md) · function

Do a shrink transition for the given element, and remove it from the DOM
afterwards. This is meant to be used as a handler for the `destroy` property.

