Quick tour
The schema is the whole API. Every property becomes a control, inferred from the shape of its value — no registration, no builders. This page is the grammar.
One schema, one scene
Six values, six different shapes, six controls. Everything driving the
tile on the left is plain data on panel.params.
const card = target.querySelector(".qt-card");
const panel = tweaks("Scene", {
size: [140, 60, 240, 1], // [value, min, max, step] → slider
blur: 0, // bare number → slider over a sensible range
caption: "Shorthands", // string → text input
show: true, // boolean → checkbox
blend: ["normal", "screen", "overlay", "multiply"], // array → list
glow: "#7C5CFF", // color string → wide-gamut picker
});
mount.append(panel.el);
const apply = (p) => {
card.style.width = `${p.size}px`;
card.style.height = `${p.size}px`;
card.style.filter = `blur(${p.blur * 24}px)`;
card.style.mixBlendMode = p.blend;
card.style.background = p.glow;
card.style.boxShadow = `0 0 70px ${p.glow}`;
card.style.opacity = p.show ? 1 : 0.06;
card.querySelector("span").textContent = p.caption;
};
panel.on(apply);
panel.ready.then(() => apply(panel.params)); // color is lazy on the split buildmount is the panel's slot inside the stage; target is the demo surface it controls. In your own page you'd just document.body.append(panel.el).
The inference table
Everything the inference understands, in one place:
| You write | You get | params value |
|---|---|---|
24 | slider over a sensible range | number |
[1.2, 0, 3, 0.1] | slider with min / max / step | number |
[[20, 80], 0, 100] | interval (dual-handle range) | [lo, hi] |
true | checkbox | boolean |
"Hello" | text input | string |
"#7C5CFF" | wide-gamut color picker | color string |
["a", "b"] | dropdown list | string |
{ action: fn } | button | — |
{ x: 0, y: 10 } | folder (collapsible group) | nested object |
{ type: "…", … } | that control, verbatim | per control |
The { type } forms unlock everything the shorthands can't say —
the heavy controls (gradient,
spring, cubic-bézier, plot,
monitors…) and per-control options.
The verbose escape hatch
Any control can be written as { type, … } to reach options the
shorthand can't express — and every object form accepts render,
disabled and hint (covered in
the panel API). Hover the ⓘ beside “Opacity”.
const tile = target.querySelector(".qt-tile");
const panel = tweaks("Verbose", {
opacity: { type: "slider", value: 1, min: 0, max: 1, step: 0.05,
hint: "Alpha blend of the tile" },
zoom: { type: "number", value: 100, min: 25, max: 400, step: 5 },
fit: { type: "segmented", options: ["cover", "contain", "auto"] },
});
mount.append(panel.el);
const apply = (p) => {
tile.style.opacity = p.opacity;
tile.style.transform = `scale(${p.zoom / 100})`;
tile.style.backgroundSize = p.fit;
};
panel.on(apply);
apply(panel.params); // every control here is built-in → params are live already