Let the slider keep the drag the browser wanted to take
Docs / docs (push) Successful in 22s
Playwright Tests / test-playwright (1, 2) (push) Failing after 2m38s
Playwright Tests / test-playwright (2, 2) (push) Failing after 1m41s
pre-commit / pre-commit (push) Failing after 2m47s
Test Backend / test-backend (push) Successful in 2m19s
Compose Smoke Test / test-compose (push) Successful in 32s
Playwright Tests / merge-reports (push) Failing after 1m4s

Dragging the brightness slider on the wall panel set the handle and
published nothing; tapping a point on the track worked. The control is a
native range input laid transparent over the drawn track, and only the
release publishes — but the input never said the drag was its own. On a
touch panel a sideways swipe is a pan, or a back-navigation, so the
browser took the pointer over mid-drag and ended it in `pointercancel`.
The value had followed the finger and was never sent.

`touch-action: none`, as the colour disk beside it has always had. The
release also answers `pointercancel` and `lostpointercapture` now, which
covers a mouse let go outside the input and leaves no way for a draft to
sit there unpublished.

Two checks: a drag across the track reaches the engine, and the input
still owns its gesture. The second fails against a build without the
CSS, which is what makes it worth having.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012n6CehUsHYYUXJ48rxaD18
This commit is contained in:
2026-08-26 13:32:22 +02:00
co-authored by Claude Opus 5
parent 95aae7b95f
commit bda67d1c0d
3 changed files with 158 additions and 0 deletions
@@ -203,6 +203,13 @@ export function useSliderDrag({
onChange: (event: React.ChangeEvent<HTMLInputElement>) =>
setDraft(Number(event.target.value)),
onPointerUp: release,
// A drag can end without a `pointerup` on this element: the browser can
// claim the gesture and cancel the pointer, and a mouse released outside
// the input reports the release elsewhere. Either way the draft would sit
// there unpublished, showing a value nobody was ever sent. `release`
// ignores a second call, so overlapping ends are free.
onPointerCancel: release,
onLostPointerCapture: release,
onKeyUp: release,
onBlur: release,
},
@@ -250,6 +250,13 @@
background: none;
opacity: 0;
cursor: pointer;
/* The drag is the control, so the browser may not read it as a gesture of
its own. Without this a sideways swipe on a touch panel is a pan — or a
back-navigation — and the browser takes the pointer over mid-drag, which
ends the drag in `pointercancel` rather than `pointerup`. The value
followed the finger and was never published. Same reason `.dui-disc`
has it. */
touch-action: none;
}
.dui-slider[data-orientation="vertical"] .dui-slider-input {