From 984da1706c8545b1adfc082fc3a627b574cf1836 Mon Sep 17 00:00:00 2001 From: stroblme Date: Thu, 15 Oct 2026 16:00:10 +0200 Subject: [PATCH] Draw each dashboard on a round panel's track in its own look The canvas states the look of the dashboard shown, so a neighbour beside it and the one a turn leaves wore that look too. Each page now restates its own look, theme and palette. The ring's paint moves from rules under [data-look] to variables each look sets on its root, which stop at the nearer root where a descendant rule reached into both. --- docs/interface/dashboards.md | 2 +- .../src/components/Dashboard/RoundFace.tsx | 51 ++++++++++++++++--- .../src/components/Dashboard/ui/core/core.css | 43 +++++++++++++++- .../Dashboard/ui/fluksio/fluksio.css | 29 +++-------- .../components/Dashboard/ui/glass/glass.css | 40 ++++----------- .../Dashboard/ui/material/material.css | 28 ++-------- 6 files changed, 107 insertions(+), 86 deletions(-) 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; -}