Tweakit

Color & gradient

The color engine is the deepest part of the kit: a wide-gamut OKLCH picker with CSS Color 4 gamut mapping, shared by the color control and the gradient editor. All three controls on this page are lazy — on the code-split build their modules load the first time a schema asks for them.

Color

Any hex or CSS color-function string is recognized as a shorthand — hex in, but the picker works in OKLCH and can emit oklch(), hex, rgb() or hsl() (switch the format inside the picker). P3-only colors survive instead of clipping. The param is always a CSS-ready string.

#7C5CFF
const blob = target.querySelector(".col-blob path");
const readout = target.querySelector(".col-readout");
const panel = tweaks("Color", {
  tint: "#7C5CFF",   // or { type: "color", value: "oklch(0.65 0.24 295)" }
});
mount.append(panel.el);

const apply = (p) => {
  blob.setAttribute("fill", p.tint);
  readout.textContent = p.tint;
};
panel.on(apply);
panel.ready.then(() => 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).

Gradient

A Figma-style stop editor: drag stops along the bar, double-click the bar (or the + button) to add, select a stop to recolor it with the full picker. The value is { stops: [{ color, pos }], interpolation } — ready to template into any CSS gradient. Stops can be authored in oklch() for wide-gamut ramps.

The ramp blends in whichever colour space you pick in the stop editor: switch the mode to RGB and the blend goes through sRGB (muddier — that's what rgb() blends look like); OKLCH stays perceptually even. That chosen space rides along as interpolation, so dropping it into linear-gradient(in …) makes your CSS match the preview exactly.

const swatch = target.querySelector(".grad-swatch");
const panel = tweaks("Gradient", {
  ramp: { type: "gradient", value: { stops: [
    { color: "oklch(0.72 0.19 25)", pos: 0 },
    { color: "oklch(0.86 0.17 95)", pos: 0.5 },
    { color: "oklch(0.72 0.16 280)", pos: 1 },
  ] } },
  angle: [90, 0, 360, 1],
});
mount.append(panel.el);

const apply = (p) => {
  const stops = p.ramp.stops.map((s) => `${s.color} ${s.pos * 100}%`).join(", ");
  const space = p.ramp.interpolation || "oklch"; // honor the editor's blend space so the swatch matches the picker
  swatch.style.background = `linear-gradient(in ${space} ${p.angle}deg, ${stops})`;
};
panel.on(apply);
panel.ready.then(() => apply(panel.params));

Image

{ type: "image" } is a drop zone and file picker in one row. The param is a data URL — drop a file on the control (or click it) and the tile picks it up as its background.

Drop an image on the control →
const tile = target.querySelector(".img-tile");
const panel = tweaks("Image", {
  texture: { type: "image" },
});
mount.append(panel.el);

const apply = (p) => {
  tile.style.backgroundImage = p.texture ? `url(${p.texture})` : "none";
  tile.querySelector("span").style.opacity = p.texture ? 0 : 1;
};
panel.on(apply);
panel.ready.then(() => apply(panel.params));