# Markup

> Markup-driven panels: enhance() upgrades [data-twk] HTML into live controls, no JavaScript required.

The page: https://ryankiley.github.io/tweakit/markup.html. Every page as Markdown: https://ryankiley.github.io/tweakit/llms.txt

The second way in: write `[data-twk]` markup and `enhance()` upgrades each host into a live control in place. Importing `tweakit` binds nothing by itself. Import `tweakit/auto` instead, the same API, and it runs `enhance(document)` once the page has parsed; this page does, so the first three examples are plain HTML, no page script at all. Or call `enhance()` yourself, for the whole page or for DOM added later.

## A static panel shell

A `.twk-panel` shell with `[data-twk]` hosts inside becomes the real thing: collapsible header, copy + reset toolbar operating over its controls, the works. Each control writes its live value back onto its host's `data-value` attribute. A `data-key` names the control in the toolbar's copied JSON (and stands in for a missing `data-label`); without one, the label is the key.

```html
<div class="twk-panel" data-mode="inline" style="max-width: 300px">
  <div class="twk-header"><span class="twk-title">Static</span></div>
  <div class="twk-controls">
    <div data-twk="slider" data-label="Blur" data-value="12" data-min="0" data-max="40" data-unit="px"></div>
    <div data-twk="checkbox" data-label="Visible" data-checked="true"></div>
    <div data-twk="list" data-label="Blend" data-options="normal, multiply, screen" data-value="normal"></div>
  </div>
</div>
```

## Heavy controls from markup

Most controls work declaratively: their config flattens into `data-*` attributes, and the lazy ones load on demand exactly as they do from a schema. Markup understands `slider`, `number`, `checkbox` (with `data-options` it becomes a radio grid), `radiogrid` (or `segmented`), `list`, `text`, `color`, `gradient`, `image`, `button`, `buttongroup`, `separator`, `interval`, `spring`, `cubicbezier`, `motion`, `shadow`, `sides`, `point`, `plot`, `monitor` and `fpsgraph`, plus the `folder` wrapper below. An `image` with `data-options`, a comma list of URLs, is the gallery. `tabs` is panel-only; a host naming it is left untouched.

```html
<div class="twk-panel" data-mode="inline" style="max-width: 300px">
  <div class="twk-header"><span class="twk-title">Heavy</span></div>
  <div class="twk-controls">
    <div data-twk="color" data-label="Tint" data-value="#7C5CFF"></div>
    <div data-twk="spring" data-label="Motion" data-stiffness="220" data-damping="18" data-mass="1"></div>
    <div data-twk="point" data-label="Offset" data-components="X,Y" data-value="0,0" data-min="-50" data-max="50" data-pad="true"></div>
    <div data-twk="interval" data-label="Band" data-value="30,70" data-min="0" data-max="100" data-step="1"></div>
  </div>
</div>
```

## Bare hosts, anywhere

Hosts don't need a panel shell: a `[data-twk]` div in any layout enhances in place (a `data-hint` rides along as the ⓘ tooltip). A `data-twk="folder"` wraps its children in a collapsible group.

```html
<div style="max-width: 300px">
  <div data-twk="folder" data-label="Inline anywhere">
    <div data-twk="slider" data-label="Speed" data-value="1.5" data-min="0" data-max="3" data-step="0.1"
         data-hint="Just a div in the page"></div>
    <div data-twk="radiogrid" data-label="Size" data-options="S, M, L, XL" data-cols="2" data-value="M"></div>
  </div>
</div>
```

## Listening for changes

Every edit on a host writes its `data-value` and dispatches a bubbling `twk:change` event with `{ key, value }` in `detail` (`key` is the host's `data-key`, or its label), so one listener on an ancestor hears every control under it. The host also keeps its handle at `host._tw.ctrl`: `get()` reads the value and `set(v)` applies one *and* runs the same change path (data-value + event). Drag the slider, then hit the button.

```js
const log = target.querySelector(".mk-log");
target.addEventListener("twk:change", (e) => {
  log.textContent = e.detail.key + " → " + JSON.stringify(e.detail.value) + "  (data-value=" + e.target.dataset.value + ")";
});
enhance(target).then(() => {
  const host = target.querySelector("[data-twk]");
  target.querySelector(".mk-set").addEventListener("click", () => host._tw.ctrl.set(Math.round(Math.random() * 40)));
});
```

## Binding on load, or yourself

`tweakit/auto` covers the document as it parsed. Without it, call `enhance()`, which binds the whole document by default. For markup you inject later (a modal, a CMS block, a partial render), call it on the new root, which counts too when it is itself a `[data-twk]` host. Hosts that are already live are skipped, so re-running is safe. With the single-file build and no bundler, the same call goes in your module script: `import { enhance } from "./tweaks.js"; enhance();`

```js
import "tweakit/auto";   // binds the page's [data-twk] hosts once it has parsed

// or, from tweakit or tweakit/single:
import { enhance } from "tweakit";

await enhance();         // the whole document
modal.innerHTML = controlsMarkup;
await enhance(modal);    // resolves once any lazy modules have loaded
```
