Add a colour-wheel widget to the dashboard

A custom hue ring — a conic gradient, not a canvas — with saturation and
brightness sliders beside or under it depending on the tile's shape, sized
for a wall panel and reachable from a keyboard. It publishes [h, s, v] by
default, which is what the reference installation's DMX encoders read, and
`format` switches that to [r, g, b] or "#rrggbb".

`usePublish` moves to its own module so a widget in a file of its own can
reach it without importing `widgets.tsx` back.
This commit is contained in:
2026-08-22 12:50:59 +02:00
parent 1b1b530cfa
commit 8224d12c8c
12 changed files with 725 additions and 99 deletions
@@ -90,6 +90,51 @@
box-shadow: inset 0 0 0 2px var(--primary);
}
/*
* The colour wheel, sized to whatever tile it was put on.
*
* `min(100cqw, 100cqh)` is what keeps one square inside a box of any shape
* without measuring anything in JS — the box is the query container, so the
* wheel reads its height as well as its width. The ring itself is a conic
* gradient set on the element, so nothing here repaints per frame.
*/
.widget-wheel-box {
container-type: size;
}
.widget-wheel {
width: min(100cqw, 100cqh);
aspect-ratio: 1;
}
/* The tile is its own query container, so the widget can be laid out by the
shape it was given rather than by the viewport — a wall panel's rows are
often wider than they are tall, and a wheel stacked above two sliders in one
of those is a dot. A container cannot answer a query about itself, which is
what the inner `-body` is for. */
.widget-color {
container-type: size;
}
@container (min-aspect-ratio: 3 / 2) {
.widget-color-body {
flex-direction: row;
align-items: center;
}
/* Square by its height, taken from the tile: the wheel keeps whatever room
the row has and the components take the rest of the width. */
.widget-color-body > .widget-wheel-box {
flex: none;
width: 100cqh;
}
.widget-color-levels {
flex: 1;
min-width: 0;
}
}
/*
* Motion. A value settling is a neutral state change; a selection indicator
* moving is emphasized (Material). `<MotionConfig reducedMotion="user">` only