diff --git a/docs/interface/dashboards.md b/docs/interface/dashboards.md
index ffcf9f5..da58efe 100644
--- a/docs/interface/dashboards.md
+++ b/docs/interface/dashboards.md
@@ -330,7 +330,7 @@ dashboard's look's.
| What | Moves | From → to | Timing | Input during it |
|---|---|---|---|---|
| Face paging (round) | the faces, side by side on a track | the track slides one face width S, the new face coming in from the side it lies on (the next from the right). A dot that skips faces brings its face in beside the one shown, so a turn is always one width | spring, from where the track is (a swipe's release speed carried on) | a new turn while one runs: the running one lands at once and the next starts |
-| Dashboard paging (round panel) | the dashboard, and beside it the next one | slides S up (the next, from below) or down (the previous, from above), the next one drawn on the face it opens on; once landed, it is the dashboard shown, and its name pill shows for a moment. One opened by its address replaces the last at once | spring | further paging waits for the turn to land |
+| Dashboard paging (round panel) | the dashboard, and beside it the next one | slides S up (the next, from below) or down (the previous, from above), the next one drawn on the face it opens on; once landed, it is the dashboard shown, and its name pill shows for a moment. Each one on the track wears its own look, theme and palette. The ground is the dashboard shown's and changes over as the turn begins, without a fade. One opened by its address replaces the last at once | spring | further paging waits for the turn to land |
| Page dot | the current dot and the last | muted at 0.5 opacity ↔ text colour at 1 | 200 ms, cubic-bezier(0.4, 0, 0.2, 1) | retargets |
| Dashboard name | the pill, on landing on another dashboard | opacity 0 → 1, held 1000 ms, 1 → 0 | in 200 ms, out 150 ms, cubic-bezier(0.4, 0, 0.2, 1) | — |
| Ring value set by a notch, a key or the engine | the value arc's end, the handle (slider, selector, colour) | from where it is drawn to the new fraction of the sweep | spring | each notch retargets from where it is, at the speed it has |
diff --git a/frontend/src/components/Dashboard/RoundFace.tsx b/frontend/src/components/Dashboard/RoundFace.tsx
index fb9f84c..3832a60 100644
--- a/frontend/src/components/Dashboard/RoundFace.tsx
+++ b/frontend/src/components/Dashboard/RoundFace.tsx
@@ -15,6 +15,7 @@ import type { WidgetDef } from "@/client"
import { slotsFor } from "@/components/Common/UplotChart"
import { fadeIn } from "@/lib/motion"
import { useShortcuts } from "@/lib/shortcuts"
+import { cn } from "@/lib/utils"
import { useClock } from "./ClockWidget"
import type { Dashboard } from "./DashboardView"
import { useBoundValue, useBoundValues } from "./dataContext"
@@ -40,7 +41,7 @@ import {
text,
} from "./ui/core/config"
import { TESTID } from "./ui/core/contract"
-import { useLook } from "./ui/core/look"
+import { LookProvider, useCanvasRoot, useLook } from "./ui/core/look"
import { LOOK } from "./ui/core/motion"
import {
ANNOUNCE_MS,
@@ -334,12 +335,7 @@ export function RoundPanel({
return (
// Keyed by name, so a neighbour becomes the one shown, and back,
// without being drawn afresh: its own face memory and all.
-
+
{editing ? (
view
@@ -347,13 +343,52 @@ export function RoundPanel({
{view}
)}
-
+
)
})}
)
}
+/**
+ * One dashboard on a round panel's track, in its own look.
+ *
+ * The canvas states the look, theme and palette of the dashboard shown, so a
+ * neighbour beside it, or the one a turn is leaving, would wear those too.
+ * Each page restates its own instead. The ground stays the canvas's, which
+ * changes over as soon as the turn begins.
+ */
+function TrackPage({
+ dashboard,
+ at,
+ size,
+ children,
+}: {
+ dashboard: Dashboard
+ /** Its place on the track, in screens from the one shown. */
+ at: number
+ size: number
+ children: React.ReactNode
+}) {
+ // Touch belongs to the screen, so it is the canvas's whichever page this is.
+ const { touch } = useLook()
+ const root = useCanvasRoot(dashboard, touch)
+ return (
+
+
+ {children}
+
+
+ )
+}
+
/** A dashboard's icon and title: what a panel shows for a moment of a
* dashboard it has just landed on. Opaque, so the face under it does not
* read through the name. */
diff --git a/frontend/src/components/Dashboard/ui/core/core.css b/frontend/src/components/Dashboard/ui/core/core.css
index 7303dbb..c49ae2e 100644
--- a/frontend/src/components/Dashboard/ui/core/core.css
+++ b/frontend/src/components/Dashboard/ui/core/core.css
@@ -425,7 +425,7 @@
);
--dui-reading: var(--foreground);
--dui-reading-reached: var(--primary-foreground);
- --dui-reading-on-handle: var(--foreground);
+ --dui-reading-on-handle: var(--dui-on-handle, var(--foreground));
}
/*
@@ -785,6 +785,47 @@
outline: none;
}
+/*
+ * The ring's paint. Each look states it on its root as these variables rather
+ * than in rules under `[data-look]`: a round panel draws a dashboard beside
+ * one in another look, and a descendant rule would reach into both, where an
+ * inherited value stops at the nearer root. Reset on every root, so a look
+ * that leaves one out takes the fallback rather than the outer root's.
+ */
+:where([data-look]) {
+ --dui-ring-track: initial;
+ --dui-ring-glow: initial;
+ --dui-ring-handle: initial;
+ --dui-ring-handle-edge: initial;
+ --dui-ring-handle-edge-width: initial;
+ --dui-ring-handle-shadow: initial;
+ --dui-ring-swatch: initial;
+ --dui-ring-swatch-width: initial;
+ --dui-on-handle: initial;
+}
+
+.dui-ring-track {
+ stroke: var(--dui-ring-track, var(--muted));
+}
+
+.dui-ring-fill {
+ stroke: var(--primary);
+ filter: var(--dui-ring-glow, none);
+}
+
+.dui-ring-handle {
+ fill: var(--dui-ring-handle, var(--primary));
+ stroke: var(--dui-ring-handle-edge, none);
+ stroke-width: var(--dui-ring-handle-edge-width, 0);
+ filter: var(--dui-ring-handle-shadow, none);
+}
+
+/* A colour's handle is filled with the colour, so only its edge is the look's. */
+.dui-ring-swatch {
+ stroke: var(--dui-ring-swatch, var(--card));
+ stroke-width: var(--dui-ring-swatch-width, 3px);
+}
+
.dui-ring-hit {
pointer-events: stroke;
cursor: pointer;
diff --git a/frontend/src/components/Dashboard/ui/fluksio/fluksio.css b/frontend/src/components/Dashboard/ui/fluksio/fluksio.css
index 150d871..f3a2795 100644
--- a/frontend/src/components/Dashboard/ui/fluksio/fluksio.css
+++ b/frontend/src/components/Dashboard/ui/fluksio/fluksio.css
@@ -21,6 +21,12 @@
[data-look="fluksio"] {
--fx-radius: var(--radius-lg);
--fx-field: var(--radius-md);
+ /* A round face's ring (`core.css`): the slider's handle, a ring of primary
+ around the page colour. */
+ --dui-ring-handle: var(--background);
+ --dui-ring-handle-edge: var(--primary);
+ --dui-ring-handle-edge-width: 2px;
+ --dui-ring-handle-shadow: drop-shadow(0 1px 2px rgb(0 0 0 / 0.2));
}
/* --- surfaces ------------------------------------------------------------ */
@@ -315,26 +321,3 @@
outline: none;
box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 50%, transparent);
}
-
-/* --- a round dashboard's ring -------------------------------------------- */
-
-[data-look="fluksio"] .dui-ring-track {
- stroke: var(--muted);
-}
-
-[data-look="fluksio"] .dui-ring-fill {
- stroke: var(--primary);
-}
-
-/* The slider's handle: a ring of primary around the page colour. */
-[data-look="fluksio"] .dui-ring-handle {
- fill: var(--background);
- stroke: var(--primary);
- stroke-width: 2px;
- filter: drop-shadow(0 1px 2px rgb(0 0 0 / 0.2));
-}
-
-[data-look="fluksio"] .dui-ring-swatch {
- stroke: var(--card);
- stroke-width: 3px;
-}
diff --git a/frontend/src/components/Dashboard/ui/glass/glass.css b/frontend/src/components/Dashboard/ui/glass/glass.css
index ce34197..3b2f57a 100644
--- a/frontend/src/components/Dashboard/ui/glass/glass.css
+++ b/frontend/src/components/Dashboard/ui/glass/glass.css
@@ -29,6 +29,17 @@
/* A round face's press, which has no card behind it. See below. */
--gl-press: var(--gl-bg);
--gl-press-shadow: var(--gl-inset);
+ /* A round face's ring (`core.css`): lit, as the gauge's arc is, under a
+ white handle, which a reading's dot on it shows dark against. */
+ --dui-ring-track: var(--gl-track);
+ --dui-ring-glow: drop-shadow(0 0 4px var(--primary));
+ --dui-ring-handle: #fff;
+ --dui-ring-handle-edge: var(--gl-border);
+ --dui-ring-handle-edge-width: 1px;
+ --dui-ring-handle-shadow: drop-shadow(0 2px 8px rgb(0 0 0 / 0.35));
+ --dui-ring-swatch: rgb(255 255 255 / 0.85);
+ --dui-ring-swatch-width: 2px;
+ --dui-on-handle: rgb(0 0 0 / 0.75);
}
[data-look="glass"].light {
@@ -433,32 +444,3 @@
outline: 2px solid var(--ring);
outline-offset: 2px;
}
-
-/* --- a round dashboard's ring -------------------------------------------- */
-
-[data-look="glass"] .dui-ring-track {
- stroke: var(--gl-track);
-}
-
-/* Lit, as the gauge's arc is. */
-[data-look="glass"] .dui-ring-fill {
- stroke: var(--primary);
- filter: drop-shadow(0 0 4px var(--primary));
-}
-
-[data-look="glass"] .dui-ring-handle {
- fill: #fff;
- stroke: var(--gl-border);
- stroke-width: 1px;
- filter: drop-shadow(0 2px 8px rgb(0 0 0 / 0.35));
-}
-
-/* A reading's dot on the handle, which is white here in either theme. */
-[data-look="glass"] :is(.dui-ring, .dui-slider) {
- --dui-reading-on-handle: rgb(0 0 0 / 0.75);
-}
-
-[data-look="glass"] .dui-ring-swatch {
- stroke: rgb(255 255 255 / 0.85);
- stroke-width: 2px;
-}
diff --git a/frontend/src/components/Dashboard/ui/material/material.css b/frontend/src/components/Dashboard/ui/material/material.css
index 22475bd..4ee9420 100644
--- a/frontend/src/components/Dashboard/ui/material/material.css
+++ b/frontend/src/components/Dashboard/ui/material/material.css
@@ -19,6 +19,10 @@
--m3-sc: color-mix(in oklab, var(--primary) 22%, var(--card));
--m3-outline: var(--border);
--m3-radius: 1rem;
+ /* A round face's ring (`core.css`): a tonal track under a primary handle,
+ which a reading's dot on it shows light against. */
+ --dui-ring-track: var(--m3-sc);
+ --dui-on-handle: var(--primary-foreground);
}
/* --- surfaces ----------------------------------------------------------- */
@@ -319,27 +323,3 @@
outline: 2px solid var(--primary);
outline-offset: 2px;
}
-
-/* --- a round dashboard's ring -------------------------------------------- */
-
-[data-look="material"] .dui-ring-track {
- stroke: var(--m3-sc);
-}
-
-[data-look="material"] .dui-ring-fill {
- stroke: var(--primary);
-}
-
-[data-look="material"] .dui-ring-handle {
- fill: var(--primary);
-}
-
-/* A reading's dot on the handle, which is primary here. */
-[data-look="material"] :is(.dui-ring, .dui-slider) {
- --dui-reading-on-handle: var(--primary-foreground);
-}
-
-[data-look="material"] .dui-ring-swatch {
- stroke: var(--card);
- stroke-width: 3px;
-}