From 3b9d3f908dde2cbf5636076feace57101a8b3b49 Mon Sep 17 00:00:00 2001 From: ollyhearn Date: Tue, 2 Jun 2026 15:26:32 +0300 Subject: [PATCH] feat: animated slider --- src/components/slider/index.tsx | 8 ++++++++ src/styles/components.css | 12 +++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/components/slider/index.tsx b/src/components/slider/index.tsx index 4cba023..235407d 100644 --- a/src/components/slider/index.tsx +++ b/src/components/slider/index.tsx @@ -22,6 +22,11 @@ type SliderProps = Omit, 'classNam notches?: NotchPlacement; /** Thumb shape. `'square'` (default) has a small border-radius; `'round'` is a full circle. */ knobStyle?: KnobStyle; + /** + * Enable step-glide animation. Defaults to `true` when `marks` is set, `false` otherwise. + * Explicitly setting this always overrides the default. + */ + animated?: boolean; className?: string; }; @@ -73,6 +78,7 @@ export const Slider = ({ marks, notches = 'bottom', knobStyle = 'square', + animated, min = 0, max = 100, step = 1, @@ -84,10 +90,12 @@ export const Slider = ({ const hasLabels = resolved.some((m) => m.label != null); const showTop = hasMarks && (notches === 'top' || notches === 'both'); const showBottom = hasMarks && (notches === 'bottom' || notches === 'both'); + const isAnimated = animated !== undefined ? animated : hasMarks; const cls = [ 'modern-sk-slider', `modern-sk-slider--knob-${knobStyle}`, + isAnimated && 'modern-sk-slider--animated', hasMarks && 'modern-sk-slider--has-marks', hasLabels && 'modern-sk-slider--has-labels', showTop && 'modern-sk-slider--notch-top', diff --git a/src/styles/components.css b/src/styles/components.css index bb72e43..d7bfc23 100644 --- a/src/styles/components.css +++ b/src/styles/components.css @@ -626,8 +626,10 @@ textarea.modern-sk-field { border-radius: 3px; background: linear-gradient(90deg, var(--lime-deep), var(--lime)); box-shadow: 0 0 10px rgba(190, 242, 100, 0.4); - /* Radix positions the range via left/right offsets (not width); ease those - so the fill glides between discrete steps while dragging. */ +} +/* Radix positions the range via left/right offsets (not width); ease those + so the fill glides between discrete steps while dragging. */ +.modern-sk-slider--animated .modern-sk-slider__range { transition: left 0.12s ease-out, right 0.12s ease-out; @@ -647,12 +649,12 @@ textarea.modern-sk-field { /* Radix sets the step position (left) on a WRAPPER span around the thumb, not on the thumb element itself — so the transition must live on that wrapper. The wrapper is the slider's direct child span that isn't the track. */ -.modern-sk-slider > span:not(.modern-sk-slider__track) { +.modern-sk-slider--animated > span:not(.modern-sk-slider__track) { transition: left 0.12s ease-out; } @media (prefers-reduced-motion: reduce) { - .modern-sk-slider > span:not(.modern-sk-slider__track), - .modern-sk-slider__range { + .modern-sk-slider--animated > span:not(.modern-sk-slider__track), + .modern-sk-slider--animated .modern-sk-slider__range { transition: none; } }