Structure
Past a handful of controls, a flat panel stops scanning. Folders and tabs group; buttons and separators punctuate. Folders, buttons, button groups and separators are built in; tabs load lazily.
Folder
Any nested plain object becomes a collapsible folder, and its children
land on params as a nested object — here the whole
shadow folder composes one box-shadow. Folders nest
as deep as you'd ever want.
const card = target.querySelector(".fld-card");
const panel = tweaks("Folder", {
label: "Stacked",
shadow: { // nested object → folder
x: [0, -40, 40, 1],
y: [12, -40, 40, 1],
blur: [32, 0, 90, 1],
alpha: [0.5, 0, 1, 0.01],
},
});
mount.append(panel.el);
const apply = (p) => {
card.textContent = p.label;
card.style.boxShadow = `${p.shadow.x}px ${p.shadow.y}px ${p.shadow.blur}px rgba(0, 0, 0, ${p.shadow.alpha})`;
};
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).
Tabs
{ type: "tabs", pages: { … } } splits a panel into pages —
each page is just another schema. Params nest by page:
params.look.fill.color, params.look.stroke.width.
const shape = target.querySelector(".tab-shape path");
const panel = tweaks("Tabs", {
look: { type: "tabs", pages: {
Fill: { color: "#7C5CFF", opacity: [1, 0, 1, 0.01] },
Stroke: { color: "#888888", width: [2, 0, 14, 1], dashed: false },
} },
});
mount.append(panel.el);
const apply = (p) => {
shape.setAttribute("fill", p.look.fill.color);
shape.setAttribute("fill-opacity", p.look.fill.opacity);
shape.setAttribute("stroke", p.look.stroke.color);
shape.setAttribute("stroke-width", p.look.stroke.width);
shape.setAttribute("stroke-dasharray", p.look.stroke.dashed ? "10 8" : "0");
};
panel.on(apply);
panel.ready.then(() => apply(panel.params)); // tabs (and color) load lazilyButton, button group & separator
A bare { action: fn } is a button; buttongroup
packs several into one row; { type: "separator" } draws the line
between concerns. Buttons don't produce params — they just fire.
const orbit = target.querySelector(".act-orbit");
const planet = target.querySelector(".act-planet");
const panel = tweaks("Actions", {
pulse: { type: "button", label: "Pulse", action: () => {
planet.classList.remove("act-pulse");
requestAnimationFrame(() => planet.classList.add("act-pulse"));
} },
playback: { type: "buttongroup", buttons: {
Play: () => { orbit.style.animationPlayState = "running"; },
Pause: () => { orbit.style.animationPlayState = "paused"; },
} },
line: { type: "separator" },
seconds: [6, 1, 12, 0.5],
});
mount.append(panel.el);
const apply = (p) => { orbit.style.animationDuration = `${p.seconds}s`; };
panel.on(apply);
apply(panel.params);