Text & choices
Strings, booleans and one-of-N choices. All four of these are built-in controls:
they ship in the core bundle and are live the moment tweaks() returns.
Text
A bare string is a text input (a color string becomes the picker). The verbose form adds
placeholder, or rows to turn it into a textarea.
Synthesizers
Voltage in, music out.
const card = target.querySelector(".txt-card");
const panel = tweaks("Text", {
headline: "Synthesizers",
body: { type: "text", value: "Voltage in, music out.", rows: 3,
placeholder: "Card body…" },
});
mount.append(panel.el);
const apply = (p) => {
card.querySelector("h3").textContent = p.headline;
card.querySelector("p").textContent = p.body;
};
panel.on(apply);
apply(panel.params);mount 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).
Checkbox
true or false is all it takes. The control
renders as an inline toggle pill; the param is a plain boolean.
const badge = target.querySelector(".chk-badge");
const panel = tweaks("Checkbox", {
enabled: true,
color: true,
});
mount.append(panel.el);
const apply = (p) => {
badge.style.opacity = p.enabled ? 1 : 0.25;
badge.style.filter = p.color ? "none" : "grayscale(1)";
};
panel.on(apply);
apply(panel.params);List
An array of strings is a dropdown. Options can also be
{ value, label } pairs when the displayed text shouldn't be the value.
Here the value lands on mix-blend-mode. With the list open, typing moves
to the option whose label starts with the letters you just typed, and the same letter
again moves on to the next match; Enter or a click still picks it.
const top = target.querySelector(".lst-b");
const panel = tweaks("List", {
blend: ["normal", "screen", "overlay", "multiply", "difference",
{ value: "plus-lighter", label: "add (plus-lighter)" }],
});
mount.append(panel.el);
const apply = (p) => { top.style.mixBlendMode = p.blend; };
panel.on(apply);
apply(panel.params);Swatches in a list
An option can carry a color: it shows as a swatch beside the label in
the dropdown and, for the chosen option, on the row. The param stays the string you gave
it, so a token palette picks by name and your code applies whatever the name maps to.
const card = target.querySelector(".sw-card");
const fills = { "neutral-10": "#f4f4f5", "neutral-90": "#18181b", accent: "#7C5CFF", warn: "#ff8a5b" };
const panel = tweaks("Tokens", {
fill: { value: "neutral-10", options: [
{ value: "neutral-10", label: "Neutral 10", color: "#f4f4f5" },
{ value: "neutral-90", label: "Neutral 90", color: "#18181b" },
{ value: "accent", label: "Accent", color: "#7C5CFF" },
{ value: "warn", label: "Warn", color: "#ff8a5b" },
] },
});
mount.append(panel.el);
const apply = (p) => {
card.style.background = fills[p.fill];
card.style.color = p.fill === "neutral-10" ? "#222" : "#fff";
card.textContent = panel.el.querySelector(".twk-select-value").textContent;
};
panel.on(apply);
apply(panel.params);Radio grid
A single-select laid out as buttons: { type: "radiogrid" }
(alias "segmented") with cols (2 or 3) to shape the grid; a pill
slides to the pick. With label: "" the row is all buttons, equal widths across
it, and four fit on one line. Nine alignment options, three columns, and the value drops
straight into place-items.
const frame = target.querySelector(".rg-frame");
const panel = tweaks("Radio grid", {
align: { type: "radiogrid", cols: 3, value: "center center", options: [
{ value: "start start", label: "↖" }, { value: "start center", label: "↑" }, { value: "start end", label: "↗" },
{ value: "center start", label: "←" }, { value: "center center", label: "·" }, { value: "center end", label: "→" },
{ value: "end start", label: "↙" }, { value: "end center", label: "↓" }, { value: "end end", label: "↘" },
] },
});
mount.append(panel.el);
const apply = (p) => { frame.style.placeItems = p.align; };
panel.on(apply);
apply(panel.params);