@monabbous/slidebox 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -2
- package/dist/css/slidebox.css +5 -0
- package/experimental/drag.d.mts +15 -0
- package/experimental/drag.mjs +158 -0
- package/less/slidebox.less +5 -0
- package/package.json +5 -2
- package/scss/_mixins.scss +5 -0
- package/tailwind/rules.json +5 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Slidebox
|
|
2
2
|
|
|
3
|
-
An elastic CSS switch built on a native checkbox. No JavaScript, extra elements, or runtime dependencies required.
|
|
3
|
+
An elastic CSS switch built on a native checkbox. No JavaScript, extra elements, or runtime dependencies required. Add optional [experimental pointer dragging](#experimental-pointer-dragging) when you want the thumb to follow your pointer.
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
@@ -199,6 +199,79 @@ theme: {
|
|
|
199
199
|
|
|
200
200
|
CSS custom properties work with every integration. Avoid setting a control both through plugin/mixin defaults and an inherited variable: a value assigned directly to the switch wins over inheritance. Use a utility or a selector targeting that switch to override it.
|
|
201
201
|
|
|
202
|
+
## Experimental pointer dragging
|
|
203
|
+
|
|
204
|
+
**Opt-in and experimental.** The switch works without JavaScript. This separate, dependency-free ES module adds mouse, touch, and pen dragging to a checkbox styled with Slidebox. It never initializes controls automatically. The API and gesture feel may change in future releases.
|
|
205
|
+
|
|
206
|
+
Keep using your chosen CSS, Sass, Less, or Tailwind integration, then attach the enhancement **after the input is mounted**:
|
|
207
|
+
|
|
208
|
+
```html
|
|
209
|
+
<label>
|
|
210
|
+
<input id="notifications" class="slidebox" type="checkbox" role="switch" name="notifications">
|
|
211
|
+
Enable notifications
|
|
212
|
+
</label>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
```js
|
|
216
|
+
import "@monabbous/slidebox/dist/css/slidebox.css"; // Omit for Sass, Less, or Tailwind.
|
|
217
|
+
import { enhanceSlidebox } from "@monabbous/slidebox/experimental/drag.mjs";
|
|
218
|
+
|
|
219
|
+
const input = document.querySelector("#notifications");
|
|
220
|
+
const disableDragging = enhanceSlidebox(input, { threshold: 5 });
|
|
221
|
+
|
|
222
|
+
// Before removing the input, or when disabling this experimental feature:
|
|
223
|
+
// disableDragging();
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
The JavaScript and drag styles must come from the **same package version**. Sass and Less mixins also include the necessary styles, so your checkbox can use a custom class instead of `.slidebox`.
|
|
227
|
+
|
|
228
|
+
### Behavior and configuration
|
|
229
|
+
|
|
230
|
+
- `threshold` is the horizontal movement, in CSS pixels, required to begin dragging. Default: `5`. Must be a finite, non-negative number.
|
|
231
|
+
- The thumb follows the pointer; its trailing edge stretches and relaxes using `--slidebox-deform` and `--slidebox-duration`. Set `--slidebox-deform: 0` to disable stretching. Reduced-motion preferences also disable drag stretching.
|
|
232
|
+
- Releasing at or beyond the midpoint checks the input; releasing before it unchecks it. A changed state emits one bubbling `input` event followed by one bubbling `change` event. Drag movement alone does not emit either event.
|
|
233
|
+
- Normal clicks, label clicks, Space-key activation, form values, and form resets remain native. A completed drag suppresses its following click to prevent a second toggle. No synthetic `click` is emitted for a drag.
|
|
234
|
+
- RTL reverses the gesture direction. Disabled inputs, including those inside a disabled fieldset, cannot be dragged.
|
|
235
|
+
- Vertical touch gestures remain available for scrolling, and pinch zoom remains enabled. Cancellation or lost pointer capture abandons the gesture without committing it.
|
|
236
|
+
- Call the returned cleanup function to stop dragging and restore temporary styles. Cleanup can be called repeatedly. Disabling it during a drag cancels that drag.
|
|
237
|
+
- Repeated calls for the same input return its existing cleanup function. To change `threshold`, clean up and initialize again.
|
|
238
|
+
|
|
239
|
+
Use normal checkbox bindings in your framework. In React, for example, initialize from a ref and return cleanup from an effect:
|
|
240
|
+
|
|
241
|
+
```jsx
|
|
242
|
+
import { useEffect, useRef, useState } from "react";
|
|
243
|
+
import { enhanceSlidebox } from "@monabbous/slidebox/experimental/drag.mjs";
|
|
244
|
+
|
|
245
|
+
function Notifications() {
|
|
246
|
+
const input = useRef(null);
|
|
247
|
+
const [enabled, setEnabled] = useState(false);
|
|
248
|
+
|
|
249
|
+
useEffect(() => {
|
|
250
|
+
const element = input.current;
|
|
251
|
+
// Native listeners also receive the enhancement's programmatic change event.
|
|
252
|
+
const sync = () => setEnabled(element.checked);
|
|
253
|
+
element.addEventListener("change", sync);
|
|
254
|
+
const destroy = enhanceSlidebox(element);
|
|
255
|
+
return () => {
|
|
256
|
+
destroy();
|
|
257
|
+
element.removeEventListener("change", sync);
|
|
258
|
+
};
|
|
259
|
+
}, []);
|
|
260
|
+
|
|
261
|
+
return (
|
|
262
|
+
<label>
|
|
263
|
+
<input ref={input} className="slidebox" type="checkbox" role="switch"
|
|
264
|
+
checked={enabled} onChange={event => setEnabled(event.target.checked)} />
|
|
265
|
+
Enable notifications
|
|
266
|
+
</label>
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
TypeScript declarations ship alongside `drag.mjs`. Importing the module is safe during server rendering, but calling `enhanceSlidebox()` requires a mounted checkbox in a browser supporting Pointer Events and pointer capture. Use a sufficiently wide horizontal Slidebox (width ratio greater than 1). Test touch, scrolling, and your framework's controlled-input behavior before adopting it in production; vertical switches are not supported.
|
|
272
|
+
|
|
273
|
+
Try it in the playground under **03 / Experimental → Enable pointer dragging**. Reset turns the option off. The playground's glass lighting is separate and is not shipped as part of the drag module.
|
|
274
|
+
|
|
202
275
|
## Development
|
|
203
276
|
|
|
204
277
|
```sh
|
|
@@ -208,7 +281,16 @@ npm test
|
|
|
208
281
|
npm pack --dry-run
|
|
209
282
|
```
|
|
210
283
|
|
|
211
|
-
The build extracts component rules from `style.css` and generates CSS, Sass, Less, and Tailwind rules. Tests compile both mixins and the plugin with Tailwind v3 and v4
|
|
284
|
+
The build extracts component rules from `style.css` and generates CSS, Sass, Less, and Tailwind rules. Tests compile both mixins and the plugin with Tailwind v3 and v4, and check the drag module's lifecycle.
|
|
285
|
+
|
|
286
|
+
For real-browser drag and playground checks:
|
|
287
|
+
|
|
288
|
+
```sh
|
|
289
|
+
npx playwright install chromium
|
|
290
|
+
npm run test:browser
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
These run in Chromium; other browsers and devices still need testing. The lab itself is plain HTML/CSS/JavaScript; serve the repository with `python3 -m http.server 8080` and open `http://localhost:8080`.
|
|
212
294
|
|
|
213
295
|
Releases use the manual [Publish to npm](.github/workflows/publish-npm.yml) workflow on `main`, with npm trusted publishing through the `production` environment. Choose a version bump for a new release, or enable `publish_existing_version` to retry the current version without a bump or new tag.
|
|
214
296
|
|
package/dist/css/slidebox.css
CHANGED
|
@@ -50,6 +50,11 @@
|
|
|
50
50
|
.slidebox:not(:checked):active::after { --_end: calc(1 - var(--_deform)); }
|
|
51
51
|
.slidebox:checked:active::after { --_start: calc(1 - var(--_deform)); }
|
|
52
52
|
.slidebox:disabled { cursor: not-allowed; opacity: .5; }
|
|
53
|
+
.slidebox[data-slidebox-dragging]::after {
|
|
54
|
+
margin-inline-start: calc((1em * var(--_container-width-ratio) - 1em) * max(0, var(--slidebox-drag-progress) - max(0, var(--slidebox-drag-stretch, 0))) + var(--_thumb-spacing-ratio) * .5em);
|
|
55
|
+
margin-inline-end: calc((1em * var(--_container-width-ratio) - 1em) * max(0, 1 - var(--slidebox-drag-progress) - max(0, -1 * var(--slidebox-drag-stretch, 0))) + var(--_thumb-spacing-ratio) * .5em);
|
|
56
|
+
transition: none;
|
|
57
|
+
}
|
|
53
58
|
@media (forced-colors: active) {
|
|
54
59
|
.slidebox { --_track: Field; --_border: ButtonBorder; --_off: FieldText; --_on: Highlight; }
|
|
55
60
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface SlideboxDragOptions {
|
|
2
|
+
/** Horizontal movement in CSS pixels before a drag starts. Default: 5. */
|
|
3
|
+
threshold?: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Experimental opt-in pointer dragging for a native checkbox styled by Slidebox.
|
|
8
|
+
* Call after mounting the input. Returns an idempotent cleanup function.
|
|
9
|
+
* Calling again for the same input returns its existing cleanup function;
|
|
10
|
+
* destroy it first to change options. Safe to import during server rendering.
|
|
11
|
+
*/
|
|
12
|
+
export declare function enhanceSlidebox(
|
|
13
|
+
input: HTMLInputElement,
|
|
14
|
+
options?: SlideboxDragOptions,
|
|
15
|
+
): () => void;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
const enhancements = new WeakMap();
|
|
2
|
+
|
|
3
|
+
/** Opt in a native checkbox to experimental horizontal pointer dragging. */
|
|
4
|
+
export function enhanceSlidebox(input, { threshold = 5 } = {}) {
|
|
5
|
+
if (input?.tagName !== "INPUT" || input.type !== "checkbox") {
|
|
6
|
+
throw new TypeError("enhanceSlidebox expects a checkbox input");
|
|
7
|
+
}
|
|
8
|
+
if (!Number.isFinite(threshold) || threshold < 0) {
|
|
9
|
+
throw new RangeError("threshold must be a non-negative number of pixels");
|
|
10
|
+
}
|
|
11
|
+
if (enhancements.has(input)) return enhancements.get(input);
|
|
12
|
+
|
|
13
|
+
const view = input.ownerDocument.defaultView;
|
|
14
|
+
if (!view?.PointerEvent || typeof input.setPointerCapture !== "function") {
|
|
15
|
+
throw new Error("Slidebox dragging requires a browser with Pointer Events and pointer capture");
|
|
16
|
+
}
|
|
17
|
+
const originalTouchAction = input.style.getPropertyValue("touch-action");
|
|
18
|
+
const originalTouchPriority = input.style.getPropertyPriority("touch-action");
|
|
19
|
+
let gesture;
|
|
20
|
+
let suppressed;
|
|
21
|
+
let suppressionTimer;
|
|
22
|
+
input.style.setProperty("touch-action", "pan-y pinch-zoom");
|
|
23
|
+
|
|
24
|
+
function disabled() { return input.matches(":disabled"); }
|
|
25
|
+
function clearSuppression() {
|
|
26
|
+
suppressed = undefined;
|
|
27
|
+
view.clearTimeout(suppressionTimer);
|
|
28
|
+
}
|
|
29
|
+
function suppressClick(id) {
|
|
30
|
+
clearSuppression();
|
|
31
|
+
suppressed = id;
|
|
32
|
+
suppressionTimer = view.setTimeout(clearSuppression, 500);
|
|
33
|
+
}
|
|
34
|
+
function finish(commit = false) {
|
|
35
|
+
if (!gesture) return;
|
|
36
|
+
const current = gesture;
|
|
37
|
+
gesture = undefined;
|
|
38
|
+
if (current.frame !== undefined) view.cancelAnimationFrame(current.frame);
|
|
39
|
+
if (current.dragging) {
|
|
40
|
+
if (current.attribute === null) input.removeAttribute("data-slidebox-dragging");
|
|
41
|
+
else input.setAttribute("data-slidebox-dragging", current.attribute);
|
|
42
|
+
if (current.progressStyle) input.style.setProperty("--slidebox-drag-progress", current.progressStyle, current.progressPriority);
|
|
43
|
+
else input.style.removeProperty("--slidebox-drag-progress");
|
|
44
|
+
if (current.stretchStyle) input.style.setProperty("--slidebox-drag-stretch", current.stretchStyle, current.stretchPriority);
|
|
45
|
+
else input.style.removeProperty("--slidebox-drag-stretch");
|
|
46
|
+
suppressClick(current.id);
|
|
47
|
+
}
|
|
48
|
+
if (input.hasPointerCapture(current.id)) input.releasePointerCapture(current.id);
|
|
49
|
+
if (commit && current.dragging && !disabled()) {
|
|
50
|
+
const checked = current.progress >= .5;
|
|
51
|
+
if (checked !== input.checked) {
|
|
52
|
+
input.checked = checked;
|
|
53
|
+
input.dispatchEvent(new view.Event("input", { bubbles: true }));
|
|
54
|
+
input.dispatchEvent(new view.Event("change", { bubbles: true }));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function down(event) {
|
|
59
|
+
if (gesture || disabled() || !event.isPrimary || event.button !== 0) return;
|
|
60
|
+
clearSuppression();
|
|
61
|
+
const box = input.getBoundingClientRect();
|
|
62
|
+
const travel = box.width - box.height;
|
|
63
|
+
if (travel <= 0) return;
|
|
64
|
+
const computed = view.getComputedStyle(input);
|
|
65
|
+
const deformation = Number.parseFloat(computed.getPropertyValue("--_deform")) || 0;
|
|
66
|
+
const durationValue = computed.getPropertyValue("--_duration").trim();
|
|
67
|
+
const duration = (Number.parseFloat(durationValue) || 0) * (durationValue.endsWith("ms") ? 1 : 1000);
|
|
68
|
+
gesture = {
|
|
69
|
+
id: event.pointerId, x: event.clientX, y: event.clientY, travel,
|
|
70
|
+
start: input.checked ? 1 : 0, progress: input.checked ? 1 : 0,
|
|
71
|
+
direction: computed.direction === "rtl" ? -1 : 1,
|
|
72
|
+
deform: view.matchMedia?.("(prefers-reduced-motion: reduce)").matches ? 0 : Math.max(0, Math.min(1, deformation)),
|
|
73
|
+
duration, stretch: 0,
|
|
74
|
+
attribute: input.getAttribute("data-slidebox-dragging"),
|
|
75
|
+
progressStyle: input.style.getPropertyValue("--slidebox-drag-progress"),
|
|
76
|
+
progressPriority: input.style.getPropertyPriority("--slidebox-drag-progress"),
|
|
77
|
+
stretchStyle: input.style.getPropertyValue("--slidebox-drag-stretch"),
|
|
78
|
+
stretchPriority: input.style.getPropertyPriority("--slidebox-drag-stretch"),
|
|
79
|
+
dragging: false,
|
|
80
|
+
};
|
|
81
|
+
input.setPointerCapture(event.pointerId);
|
|
82
|
+
}
|
|
83
|
+
function relax(now) {
|
|
84
|
+
if (!gesture) return;
|
|
85
|
+
const elapsed = gesture.lastFrame === undefined ? 16 : Math.max(0, now - gesture.lastFrame);
|
|
86
|
+
gesture.lastFrame = now;
|
|
87
|
+
gesture.stretch *= Math.exp(-elapsed / Math.max(16, gesture.duration * .35));
|
|
88
|
+
if (Math.abs(gesture.stretch) < .0001) gesture.stretch = 0;
|
|
89
|
+
input.style.setProperty("--slidebox-drag-stretch", String(gesture.stretch));
|
|
90
|
+
gesture.frame = gesture.stretch ? view.requestAnimationFrame(relax) : undefined;
|
|
91
|
+
if (!gesture.stretch) gesture.lastFrame = undefined;
|
|
92
|
+
}
|
|
93
|
+
function move(event) {
|
|
94
|
+
if (!gesture || event.pointerId !== gesture.id) return;
|
|
95
|
+
if (disabled()) { finish(); return; }
|
|
96
|
+
const dx = event.clientX - gesture.x;
|
|
97
|
+
const dy = event.clientY - gesture.y;
|
|
98
|
+
if (!gesture.dragging) {
|
|
99
|
+
// Leave vertical gestures to page scrolling and taps to native clicks.
|
|
100
|
+
if (Math.abs(dy) > threshold && Math.abs(dy) > Math.abs(dx)) { finish(); return; }
|
|
101
|
+
if (Math.abs(dx) <= threshold) return;
|
|
102
|
+
gesture.dragging = true;
|
|
103
|
+
input.setAttribute("data-slidebox-dragging", "");
|
|
104
|
+
input.focus({ preventScroll: true });
|
|
105
|
+
}
|
|
106
|
+
const progress = Math.max(0, Math.min(1, gesture.start + dx * gesture.direction / gesture.travel));
|
|
107
|
+
gesture.stretch = Math.max(-gesture.deform, Math.min(gesture.deform, gesture.stretch + progress - gesture.progress));
|
|
108
|
+
gesture.progress = progress;
|
|
109
|
+
input.style.setProperty("--slidebox-drag-progress", String(gesture.progress));
|
|
110
|
+
input.style.setProperty("--slidebox-drag-stretch", String(gesture.stretch));
|
|
111
|
+
if (gesture.stretch && gesture.frame === undefined) gesture.frame = view.requestAnimationFrame(relax);
|
|
112
|
+
event.preventDefault();
|
|
113
|
+
}
|
|
114
|
+
function up(event) {
|
|
115
|
+
if (!gesture || event.pointerId !== gesture.id) return;
|
|
116
|
+
move(event);
|
|
117
|
+
finish(true);
|
|
118
|
+
}
|
|
119
|
+
function cancel(event) {
|
|
120
|
+
if (gesture && event.pointerId === gesture.id) finish();
|
|
121
|
+
}
|
|
122
|
+
function click(event) {
|
|
123
|
+
// Keyboard activation has detail=0 and must retain native behavior.
|
|
124
|
+
if (suppressed === undefined || event.detail === 0) return;
|
|
125
|
+
if ("pointerId" in event && event.pointerId !== suppressed) return;
|
|
126
|
+
event.preventDefault();
|
|
127
|
+
event.stopImmediatePropagation();
|
|
128
|
+
clearSuppression();
|
|
129
|
+
}
|
|
130
|
+
function reset() { finish(); }
|
|
131
|
+
const form = input.form;
|
|
132
|
+
input.addEventListener("pointerdown", down);
|
|
133
|
+
input.addEventListener("pointermove", move);
|
|
134
|
+
input.addEventListener("pointerup", up);
|
|
135
|
+
input.addEventListener("pointercancel", cancel);
|
|
136
|
+
input.addEventListener("lostpointercapture", cancel);
|
|
137
|
+
input.addEventListener("click", click, true);
|
|
138
|
+
form?.addEventListener("reset", reset);
|
|
139
|
+
|
|
140
|
+
function destroy() {
|
|
141
|
+
finish();
|
|
142
|
+
clearSuppression();
|
|
143
|
+
input.removeEventListener("pointerdown", down);
|
|
144
|
+
input.removeEventListener("pointermove", move);
|
|
145
|
+
input.removeEventListener("pointerup", up);
|
|
146
|
+
input.removeEventListener("pointercancel", cancel);
|
|
147
|
+
input.removeEventListener("lostpointercapture", cancel);
|
|
148
|
+
input.removeEventListener("click", click, true);
|
|
149
|
+
form?.removeEventListener("reset", reset);
|
|
150
|
+
if (input.style.getPropertyValue("touch-action") === "pan-y pinch-zoom") {
|
|
151
|
+
if (originalTouchAction) input.style.setProperty("touch-action", originalTouchAction, originalTouchPriority);
|
|
152
|
+
else input.style.removeProperty("touch-action");
|
|
153
|
+
}
|
|
154
|
+
enhancements.delete(input);
|
|
155
|
+
}
|
|
156
|
+
enhancements.set(input, destroy);
|
|
157
|
+
return destroy;
|
|
158
|
+
}
|
package/less/slidebox.less
CHANGED
|
@@ -65,6 +65,11 @@
|
|
|
65
65
|
&:not(:checked):active::after { --_end: calc(1 - var(--_deform)); }
|
|
66
66
|
&:checked:active::after { --_start: calc(1 - var(--_deform)); }
|
|
67
67
|
&:disabled { cursor: not-allowed; opacity: .5; }
|
|
68
|
+
&[data-slidebox-dragging]::after {
|
|
69
|
+
margin-inline-start: ~"calc((1em * var(--_container-width-ratio) - 1em) * max(0, var(--slidebox-drag-progress) - max(0, var(--slidebox-drag-stretch, 0))) + var(--_thumb-spacing-ratio) * .5em)";
|
|
70
|
+
margin-inline-end: ~"calc((1em * var(--_container-width-ratio) - 1em) * max(0, 1 - var(--slidebox-drag-progress) - max(0, -1 * var(--slidebox-drag-stretch, 0))) + var(--_thumb-spacing-ratio) * .5em)";
|
|
71
|
+
transition: none;
|
|
72
|
+
}
|
|
68
73
|
@media (forced-colors: active) {
|
|
69
74
|
& { --_track: Field; --_border: ButtonBorder; --_off: FieldText; --_on: Highlight; }
|
|
70
75
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monabbous/slidebox",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Elastic, system-color-aware CSS checkbox switch.",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"style": "./dist/css/slidebox.css",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"dist/css/slidebox.css",
|
|
11
11
|
"scss/",
|
|
12
12
|
"less/",
|
|
13
|
-
"tailwind/"
|
|
13
|
+
"tailwind/",
|
|
14
|
+
"experimental/"
|
|
14
15
|
],
|
|
15
16
|
"publishConfig": {
|
|
16
17
|
"access": "public"
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
"scripts": {
|
|
35
36
|
"build": "node scripts/build.mjs",
|
|
36
37
|
"test": "node --test tests/*.test.mjs",
|
|
38
|
+
"test:browser": "playwright test",
|
|
37
39
|
"prepack": "npm run build"
|
|
38
40
|
},
|
|
39
41
|
"keywords": [
|
|
@@ -47,6 +49,7 @@
|
|
|
47
49
|
"system-colors"
|
|
48
50
|
],
|
|
49
51
|
"devDependencies": {
|
|
52
|
+
"@playwright/test": "^1.64.1",
|
|
50
53
|
"@tailwindcss/node": "^4.3.3",
|
|
51
54
|
"less": "^4.9.1",
|
|
52
55
|
"postcss": "^8.5.32",
|
package/scss/_mixins.scss
CHANGED
|
@@ -59,6 +59,11 @@ $defaults: () !default;
|
|
|
59
59
|
&:not(:checked):active::after { --_end: calc(1 - var(--_deform)); }
|
|
60
60
|
&:checked:active::after { --_start: calc(1 - var(--_deform)); }
|
|
61
61
|
&:disabled { cursor: not-allowed; opacity: .5; }
|
|
62
|
+
&[data-slidebox-dragging]::after {
|
|
63
|
+
margin-inline-start: calc((1em * var(--_container-width-ratio) - 1em) * max(0, var(--slidebox-drag-progress) - max(0, var(--slidebox-drag-stretch, 0))) + var(--_thumb-spacing-ratio) * .5em);
|
|
64
|
+
margin-inline-end: calc((1em * var(--_container-width-ratio) - 1em) * max(0, 1 - var(--slidebox-drag-progress) - max(0, -1 * var(--slidebox-drag-stretch, 0))) + var(--_thumb-spacing-ratio) * .5em);
|
|
65
|
+
transition: none;
|
|
66
|
+
}
|
|
62
67
|
@media (forced-colors: active) {
|
|
63
68
|
& { --_track: Field; --_border: ButtonBorder; --_off: FieldText; --_on: Highlight; }
|
|
64
69
|
}
|
package/tailwind/rules.json
CHANGED
|
@@ -60,6 +60,11 @@
|
|
|
60
60
|
"cursor": "not-allowed",
|
|
61
61
|
"opacity": ".5"
|
|
62
62
|
},
|
|
63
|
+
"&[data-slidebox-dragging]::after": {
|
|
64
|
+
"margin-inline-start": "calc((1em * var(--_container-width-ratio) - 1em) * max(0, var(--slidebox-drag-progress) - max(0, var(--slidebox-drag-stretch, 0))) + var(--_thumb-spacing-ratio) * .5em)",
|
|
65
|
+
"margin-inline-end": "calc((1em * var(--_container-width-ratio) - 1em) * max(0, 1 - var(--slidebox-drag-progress) - max(0, -1 * var(--slidebox-drag-stretch, 0))) + var(--_thumb-spacing-ratio) * .5em)",
|
|
66
|
+
"transition": "none"
|
|
67
|
+
},
|
|
63
68
|
"@media (forced-colors: active)": {
|
|
64
69
|
"&": {
|
|
65
70
|
"--_track": "Field",
|