{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "fluid-slider",
  "type": "registry:ui",
  "title": "Fluid Slider",
  "description": "A labeled Base UI range control with direct surface selection, a dedicated draggable indicator, and restrained boundary resistance.",
  "files": [
    {
      "path": "registry/sonaui/fluid-slider/fluid-slider.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport { Slider } from \"@base-ui/react/slider\";\nimport {\n  animate,\n  motion,\n  useMotionValue,\n  useReducedMotion,\n  useTransform,\n} from \"motion/react\";\nimport {\n  type ChangeEvent,\n  type CSSProperties,\n  type KeyboardEvent,\n  type ReactNode,\n  useEffect,\n  useId,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\n\nimport { cn } from \"@/lib/sona-utils\";\n\nexport interface FluidSliderProps {\n  /** Visible label associated with the slider. */\n  label: ReactNode;\n  /** Controlled slider value. @default undefined */\n  value?: number;\n  /** Initial value for uncontrolled usage. @default 50 */\n  defaultValue?: number;\n  /** Called continuously while the value changes. @default undefined */\n  onValueChange?: (\n    value: number,\n    details: Slider.Root.ChangeEventDetails,\n  ) => void;\n  /** Called when a pointer or keyboard interaction commits. @default undefined */\n  onValueCommitted?: (\n    value: number,\n    details: Slider.Root.CommitEventDetails,\n  ) => void;\n  /** Minimum allowed value. @default 0 */\n  min?: number;\n  /** Maximum allowed value. @default 100 */\n  max?: number;\n  /** Smallest value increment. @default 1 */\n  step?: number;\n  /** Increment used by Page Up, Page Down, and modified arrow keys. @default 10 */\n  largeStep?: number;\n  /** Numeric positions rendered as quiet reference marks. @default [] */\n  marks?: number[];\n  /** Formats the visible and accessible value text. @default undefined */\n  formatValue?: (value: number) => string;\n  /** Intl options used when no custom formatter is supplied. @default undefined */\n  format?: Intl.NumberFormatOptions;\n  /** Locale used by the default number formatter. @default undefined */\n  locale?: Intl.LocalesArgument;\n  /** Whether the trailing formatted value is visible. @default true */\n  showValue?: boolean;\n  /** Whether the visible value can be edited as an exact number. @default true */\n  editableValue?: boolean;\n  /** Whether the active boundary grip is visible. @default true */\n  showHandle?: boolean;\n  /** Whether the slider ignores user interaction. @default false */\n  disabled?: boolean;\n  /** Name submitted with the slider's hidden range input. @default undefined */\n  name?: string;\n  /** ID of the form that owns the slider input. @default undefined */\n  form?: string;\n  /** Accessible name used instead of the visible label. @default undefined */\n  ariaLabel?: string;\n  /** Additional classes for the slider root. @default undefined */\n  className?: string;\n  /** Additional classes for the track surface. @default undefined */\n  trackClassName?: string;\n  /** Additional classes for the filled surface. @default undefined */\n  surfaceClassName?: string;\n  /** Additional classes for the fixed label. @default undefined */\n  labelClassName?: string;\n  /** Additional classes for the trailing value. @default undefined */\n  valueClassName?: string;\n  /** Additional classes for the boundary grip. @default undefined */\n  handleClassName?: string;\n}\n\nconst tokenStyle = {\n  \"--fluid-slider-track\": \"var(--background)\",\n  \"--fluid-slider-surface\":\n    \"color-mix(in oklab, var(--primary) 16%, var(--background))\",\n  \"--fluid-slider-surface-active\":\n    \"color-mix(in oklab, var(--primary) 22%, var(--background))\",\n  \"--fluid-slider-handle\": \"var(--primary)\",\n  \"--fluid-slider-label\": \"var(--foreground)\",\n  \"--fluid-slider-value\": \"var(--muted-foreground)\",\n  \"--fluid-slider-mark\":\n    \"color-mix(in oklab, var(--foreground) 32%, transparent)\",\n  \"--fluid-slider-focus-ring\": \"var(--ring)\",\n  \"--fluid-slider-disabled-opacity\": \"0.48\",\n  \"--fluid-slider-border-radius\": \"var(--radius)\",\n} as CSSProperties;\n\nfunction clamp(value: number, min: number, max: number) {\n  return Math.min(Math.max(value, min), max);\n}\n\nfunction rubberband(overshoot: number) {\n  const deadZone = 32;\n  const maximumStretch = 8;\n  const cursorRange = 200;\n  const direction = Math.sign(overshoot);\n  const distancePastEdge = Math.max(0, Math.abs(overshoot) - deadZone);\n\n  return (\n    direction *\n    maximumStretch *\n    Math.sqrt(Math.min(distancePastEdge / cursorRange, 1))\n  );\n}\n\nfunction decimalPlaces(value: number) {\n  const [, exponent = \"0\"] = String(value).toLowerCase().split(\"e\");\n  const fraction = String(value).split(\".\")[1]?.length ?? 0;\n  return Math.max(0, fraction - Number(exponent));\n}\n\nfunction roundToStep(value: number, min: number, max: number, step: number) {\n  const clamped = clamp(value, min, max);\n  if (clamped === min || clamped === max || step <= 0) return clamped;\n  const rounded = min + Math.round((clamped - min) / step) * step;\n  return Number(\n    clamp(rounded, min, max).toFixed(\n      Math.max(decimalPlaces(step), decimalPlaces(min), decimalPlaces(max)),\n    ),\n  );\n}\n\nfunction snapToMagneticDecile(value: number, min: number, max: number) {\n  if (max <= min) return min;\n  const normalized = (value - min) / (max - min);\n  const nearest = Math.round(normalized * 10) / 10;\n  return Math.abs(normalized - nearest) <= 0.03125\n    ? min + nearest * (max - min)\n    : value;\n}\n\nexport default function FluidSlider({\n  label,\n  value,\n  defaultValue = 50,\n  onValueChange,\n  onValueCommitted,\n  min = 0,\n  max = 100,\n  step = 1,\n  largeStep = 10,\n  marks = [],\n  formatValue,\n  format,\n  locale,\n  showValue = true,\n  editableValue = true,\n  showHandle = true,\n  disabled = false,\n  name,\n  form,\n  ariaLabel,\n  className,\n  trackClassName,\n  surfaceClassName,\n  labelClassName,\n  valueClassName,\n  handleClassName,\n}: FluidSliderProps) {\n  const labelId = useId();\n  const shouldReduceMotion = useReducedMotion();\n  const [internalValue, setInternalValue] = useState(() =>\n    clamp(defaultValue, min, max),\n  );\n  const [editingValue, setEditingValue] = useState(false);\n  const [inputValue, setInputValue] = useState(\"\");\n  const currentValue = clamp(value ?? internalValue, min, max);\n  const range = max - min;\n  const progress = range === 0 ? 0 : ((currentValue - min) / range) * 100;\n  const controlRef = useRef<HTMLDivElement>(null);\n  const valueInputRef = useRef<HTMLInputElement>(null);\n  const pointerActiveRef = useRef(false);\n  const trackPressRef = useRef(false);\n  const dragMovedRef = useRef(false);\n  const pointerStartXRef = useRef(0);\n  const fillAnimationRef = useRef<ReturnType<typeof animate> | null>(null);\n  const grabOffsetRef = useRef(0);\n  const pointerVelocityRef = useRef(0);\n  const previousPointerRef = useRef({ x: 0, time: 0 });\n  const settleAnimationRef = useRef<ReturnType<typeof animate> | null>(null);\n  const progressMotion = useMotionValue(progress / 100);\n  const overshoot = useMotionValue(0);\n  const trackWidth = useMotionValue(1);\n  const direction = useMotionValue(1);\n  const press = useMotionValue(0);\n  const pressAnimationRef = useRef<ReturnType<typeof animate> | null>(null);\n\n  useEffect(() => {\n    const target = progress / 100;\n    fillAnimationRef.current?.stop();\n\n    // A tap on the inactive track (pointer pressed but not yet dragged) should\n    // glide the fill to the new value. Dragging tracks 1:1 and keyboard/external\n    // changes stay instant, per the interaction spec.\n    const isTap = pointerActiveRef.current && !dragMovedRef.current;\n    if (isTap && !shouldReduceMotion) {\n      fillAnimationRef.current = animate(progressMotion, target, {\n        type: \"spring\",\n        stiffness: 420,\n        damping: 40,\n        mass: 0.9,\n      });\n    } else {\n      progressMotion.set(target);\n    }\n  }, [progress, progressMotion, shouldReduceMotion]);\n\n  useEffect(() => {\n    const control = controlRef.current;\n    if (!control) return;\n\n    const updateWidth = () =>\n      trackWidth.set(control.getBoundingClientRect().width);\n    updateWidth();\n\n    const resizeObserver = new ResizeObserver(updateWidth);\n    resizeObserver.observe(control);\n    return () => resizeObserver.disconnect();\n  }, [trackWidth]);\n\n  useEffect(\n    () => () => {\n      settleAnimationRef.current?.stop();\n      pressAnimationRef.current?.stop();\n      fillAnimationRef.current?.stop();\n    },\n    [],\n  );\n\n  // The filled fraction (0–1), including any damped overshoot past the bounds.\n  // Clip-path, not scaleX, sizes the fill so its border-radius stays a true,\n  // undistorted match for the track corners at every value.\n  const fillFraction = useTransform(() => {\n    const width = Math.max(trackWidth.get(), 1);\n    const directionalOvershoot = (overshoot.get() / width) * direction.get();\n    return clamp(progressMotion.get() + directionalOvershoot, 0, 1);\n  });\n  const surfaceClipPath = useTransform(() => {\n    const inset = (1 - fillFraction.get()) * 100;\n    const isRtl = direction.get() === -1;\n    const insetEnd = isRtl ? 0 : inset;\n    const insetStart = isRtl ? inset : 0;\n    return `inset(0px ${insetEnd}% 0px ${insetStart}% round var(--fluid-slider-border-radius))`;\n  });\n  // The visual track stretches beyond the active edge as the drag exceeds the\n  // boundary. Keeping this separate from Slider.Control preserves its stable\n  // hit target and Base UI's range semantics.\n  const rubberBandWidth = useTransform(\n    overshoot,\n    (stretch) => `calc(100% + ${Math.abs(stretch)}px)`,\n  );\n  const rubberBandX = useTransform(overshoot, (stretch) =>\n    stretch < 0 ? stretch : 0,\n  );\n\n  // Immediate-response squash: the active surface compresses slightly while\n  // pressed and eases back on release. Presentation only — never the value.\n  const surfaceScaleY = useTransform(press, [0, 1], [1, 0.98]);\n\n  const animatePress = (target: number) => {\n    pressAnimationRef.current?.stop();\n    if (shouldReduceMotion) {\n      press.set(target);\n      return;\n    }\n    pressAnimationRef.current = animate(press, target, {\n      type: \"spring\",\n      stiffness: 640,\n      damping: 34,\n      mass: 0.6,\n    });\n  };\n\n  const formatter = useMemo(\n    () => new Intl.NumberFormat(locale, format),\n    [format, locale],\n  );\n  const formattedValue = formatValue\n    ? formatValue(currentValue)\n    : formatter.format(currentValue);\n  const visibleMarks = marks.filter((mark) => mark >= min && mark <= max);\n\n  useEffect(() => {\n    if (!editingValue) return;\n    valueInputRef.current?.focus();\n    valueInputRef.current?.select();\n  }, [editingValue]);\n\n  const settleOvershoot = () => {\n    pointerActiveRef.current = false;\n    trackPressRef.current = false;\n    controlRef.current?.removeAttribute(\"data-pressed\");\n    animatePress(0);\n    if (overshoot.get() === 0) return;\n\n    settleAnimationRef.current?.stop();\n    if (shouldReduceMotion) {\n      overshoot.set(0);\n      return;\n    }\n\n    settleAnimationRef.current = animate(overshoot, 0, {\n      type: \"spring\",\n      stiffness: 520,\n      damping: 42,\n      mass: 0.65,\n      velocity: pointerVelocityRef.current,\n    });\n  };\n\n  // Base UI's own track-press handler can't resolve the thumb when it is\n  // rendered through motion (getFingerState returns null), so a bare-track tap\n  // never sets the value. Since Slider.Root is fully controlled by this\n  // component, we drive Base UI's hidden range input directly: it fires the\n  // real onValueChange with proper event details, which flows through the\n  // Slider.Root handler below.\n  const applyValue = (nextValue: number) => {\n    const control = controlRef.current;\n    if (!control) return;\n\n    const input = control.querySelector<HTMLInputElement>(\n      'input[type=\"range\"]',\n    );\n    if (!input || Number(input.value) === nextValue) return;\n\n    const nativeSetter = Object.getOwnPropertyDescriptor(\n      HTMLInputElement.prototype,\n      \"value\",\n    )?.set;\n    nativeSetter?.call(input, String(nextValue));\n    input.dispatchEvent(new Event(\"input\", { bubbles: true }));\n  };\n\n  const applyValueFromClientX = (clientX: number) => {\n    const control = controlRef.current;\n    if (!control) return;\n\n    const rect = control.getBoundingClientRect();\n    if (rect.width === 0) return;\n\n    const isRtl = getComputedStyle(control).direction === \"rtl\";\n    let ratio = (clientX - rect.left) / rect.width;\n    if (isRtl) ratio = 1 - ratio;\n    ratio = clamp(ratio, 0, 1);\n\n    const raw = min + ratio * range;\n    const stepCount = range / step;\n    const snapped =\n      stepCount <= 10\n        ? roundToStep(raw, min, max, step)\n        : roundToStep(snapToMagneticDecile(raw, min, max), min, max, step);\n\n    applyValue(snapped);\n  };\n\n  const startValueEdit = () => {\n    if (disabled || !editableValue) return;\n    setInputValue(\n      currentValue.toFixed(\n        Math.max(decimalPlaces(step), decimalPlaces(min), decimalPlaces(max)),\n      ),\n    );\n    setEditingValue(true);\n  };\n\n  const finishValueEdit = (commit: boolean) => {\n    if (commit) {\n      const parsed = Number(inputValue);\n      if (Number.isFinite(parsed))\n        applyValue(roundToStep(parsed, min, max, step));\n    }\n    setEditingValue(false);\n    controlRef.current\n      ?.querySelector<HTMLInputElement>('input[type=\"range\"]')\n      ?.focus({ preventScroll: true });\n  };\n\n  const handleValueInputChange = (event: ChangeEvent<HTMLInputElement>) => {\n    setInputValue(event.target.value);\n  };\n\n  const handleValueInputKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n    if (event.key !== \"Enter\" && event.key !== \"Escape\") return;\n    event.preventDefault();\n    finishValueEdit(event.key === \"Enter\");\n  };\n\n  const handlePointerDown = (event: PointerEvent) => {\n    if (disabled || event.button !== 0) return;\n\n    if (\n      (event.target as HTMLElement).closest(\"[data-fluid-slider-value-control]\")\n    ) {\n      return;\n    }\n\n    settleAnimationRef.current?.stop();\n    pointerActiveRef.current = true;\n    dragMovedRef.current = false;\n    pointerStartXRef.current = event.clientX;\n    controlRef.current?.setAttribute(\"data-pressed\", \"\");\n    animatePress(1);\n    pointerVelocityRef.current = 0;\n    previousPointerRef.current = { x: event.clientX, time: event.timeStamp };\n\n    const control = controlRef.current;\n    if (control) {\n      const rect = control.getBoundingClientRect();\n      trackWidth.set(rect.width);\n      direction.set(getComputedStyle(control).direction === \"rtl\" ? -1 : 1);\n    }\n\n    const target = event.target as HTMLElement;\n    const pressedHandle = target.closest<HTMLElement>(\n      \"[data-fluid-slider-hit]\",\n    );\n    const thumb = target.closest<HTMLElement>(\"[data-fluid-slider-thumb]\");\n\n    if (pressedHandle && thumb) {\n      trackPressRef.current = false;\n      const thumbRect = thumb.getBoundingClientRect();\n      grabOffsetRef.current =\n        event.clientX - (thumbRect.left + thumbRect.width / 2);\n    } else {\n      // A surface press is a positional command, not a drag gesture. Move the\n      // value to that point immediately; continuous adjustment begins only\n      // from the boundary indicator.\n      trackPressRef.current = true;\n      grabOffsetRef.current = 0;\n      overshoot.set(0);\n      applyValueFromClientX(event.clientX);\n    }\n  };\n\n  const handlePointerMove = (event: PointerEvent) => {\n    if (!pointerActiveRef.current || disabled) return;\n\n    // Once the indicator crosses the drag threshold, stop any pending tap\n    // glide so the boundary stays locked 1:1 to the pointer. A surface press\n    // is a positional command, so its glide must run to completion.\n    if (\n      !trackPressRef.current &&\n      !dragMovedRef.current &&\n      Math.abs(event.clientX - pointerStartXRef.current) > 8\n    ) {\n      dragMovedRef.current = true;\n      fillAnimationRef.current?.stop();\n    }\n\n    // Surface presses set one value on pointer-down. Only the indicator is a\n    // continuous drag target.\n    if (trackPressRef.current) return;\n\n    const previous = previousPointerRef.current;\n    const elapsed = Math.max(event.timeStamp - previous.time, 1);\n    pointerVelocityRef.current =\n      ((event.clientX - previous.x) / elapsed) * 1000;\n    previousPointerRef.current = { x: event.clientX, time: event.timeStamp };\n\n    if (shouldReduceMotion) {\n      overshoot.set(0);\n      return;\n    }\n\n    const control = controlRef.current;\n    if (!control) return;\n\n    const rect = control.getBoundingClientRect();\n    const presentedBoundary = event.clientX - grabOffsetRef.current;\n    const rawOvershoot =\n      presentedBoundary < rect.left\n        ? presentedBoundary - rect.left\n        : presentedBoundary > rect.right\n          ? presentedBoundary - rect.right\n          : 0;\n\n    overshoot.set(rubberband(rawOvershoot));\n  };\n\n  useEffect(() => {\n    const control = controlRef.current;\n    if (!control) return;\n\n    control.addEventListener(\"pointerdown\", handlePointerDown);\n    control.addEventListener(\"pointermove\", handlePointerMove);\n    control.addEventListener(\"pointerup\", settleOvershoot);\n    control.addEventListener(\"pointercancel\", settleOvershoot);\n    control.addEventListener(\"lostpointercapture\", settleOvershoot);\n\n    return () => {\n      control.removeEventListener(\"pointerdown\", handlePointerDown);\n      control.removeEventListener(\"pointermove\", handlePointerMove);\n      control.removeEventListener(\"pointerup\", settleOvershoot);\n      control.removeEventListener(\"pointercancel\", settleOvershoot);\n      control.removeEventListener(\"lostpointercapture\", settleOvershoot);\n    };\n  });\n\n  const fluidStyle = {\n    ...tokenStyle,\n    \"--fluid-slider-progress\": progress,\n  } as CSSProperties;\n\n  return (\n    <Slider.Root\n      aria-labelledby={ariaLabel ? undefined : labelId}\n      value={currentValue}\n      min={min}\n      max={max}\n      step={step}\n      largeStep={largeStep}\n      format={format}\n      locale={locale}\n      disabled={disabled}\n      name={name}\n      form={form}\n      onValueChange={(nextValue, details) => {\n        if (value === undefined) setInternalValue(nextValue);\n        onValueChange?.(nextValue, details);\n      }}\n      onValueCommitted={onValueCommitted}\n      className={cn(\n        \"group/fluid-slider w-full min-h-12 max-w-xl select-none rounded-xl\",\n        disabled &&\n          \"pointer-events-none opacity-(--fluid-slider-disabled-opacity)\",\n        className,\n      )}\n      style={fluidStyle}\n    >\n      <Slider.Control\n        ref={controlRef}\n        data-fluid-slider-control=\"\"\n        className={cn(\n          \"group/fluid-slider-control relative h-12 w-full cursor-pointer rounded-2xl outline-none touch-pan-y\",\n          \"has-[input:focus-visible]:ring-2 has-[input:focus-visible]:ring-(--fluid-slider-focus-ring) has-[input:focus-visible]:ring-offset-2 has-[input:focus-visible]:ring-offset-background overflow-visible rounded-(--fluid-slider-border-radius)\",\n          trackClassName,\n        )}\n      >\n        <Slider.Track className=\"relative h-12 w-full [container-type:inline-size]\">\n          <motion.div\n            aria-hidden=\"true\"\n            className=\"pointer-events-none absolute inset-y-0 start-0\"\n            style={{ width: rubberBandWidth, x: rubberBandX }}\n          >\n            <div className=\"absolute inset-0 rounded-(--fluid-slider-border-radius) bg-(--fluid-slider-track) shadow-[inset_0_1px_0_color-mix(in_oklab,var(--foreground)_4%,transparent)]\" />\n\n            <Slider.Indicator\n              render={\n                <motion.div\n                  style={{ clipPath: surfaceClipPath, scaleY: surfaceScaleY }}\n                />\n              }\n              className={cn(\n                \"w-full! absolute inset-0 origin-left rounded-(--fluid-slider-border-radius) bg-(--fluid-slider-surface) shadow-[inset_0_1px_0_color-mix(in_oklab,var(--background)_55%,transparent),0_1px_2px_color-mix(in_oklab,var(--foreground)_5%,transparent)] transition-[filter,background-color] duration-150 rtl:origin-right\",\n                surfaceClassName,\n              )}\n              style={{ insetInlineStart: 0, insetInlineEnd: 0 }}\n            />\n\n            {visibleMarks.map((mark) => {\n              const markProgress =\n                range === 0 ? 0 : ((mark - min) / range) * 100;\n              return (\n                <span\n                  key={mark}\n                  className=\"absolute top-1/2 z-5 size-1.5 -translate-x-1/2 -translate-y-1/2 rounded-full bg-(--fluid-slider-mark) rtl:translate-x-1/2\"\n                  style={{ insetInlineStart: `${markProgress}%` }}\n                />\n              );\n            })}\n          </motion.div>\n\n          <Slider.Label\n            className={cn(\n              \"pointer-events-none absolute inset-y-0 start-5 z-10 flex items-center truncate pe-24 font-medium text-(--fluid-slider-label) text-sm\",\n              labelClassName,\n            )}\n          >\n            <span id={labelId}>{label}</span>\n          </Slider.Label>\n\n          {showValue && (\n            <div\n              data-fluid-slider-value-control=\"\"\n              className=\"absolute inset-y-0 end-5 z-30 flex items-center\"\n            >\n              {editingValue ? (\n                <input\n                  ref={valueInputRef}\n                  type=\"text\"\n                  inputMode=\"decimal\"\n                  aria-label=\"Edit slider value\"\n                  value={inputValue}\n                  onChange={handleValueInputChange}\n                  onKeyDown={handleValueInputKeyDown}\n                  onBlur={() => finishValueEdit(true)}\n                  className={cn(\n                    \"w-[5.5ch] bg-transparent text-end font-medium text-(--fluid-slider-label) text-sm tabular-nums outline-none\",\n                    valueClassName,\n                  )}\n                />\n              ) : (\n                <button\n                  type=\"button\"\n                  disabled={!editableValue || disabled}\n                  onClick={startValueEdit}\n                  className={cn(\n                    \"font-medium text-(--fluid-slider-value) text-sm tabular-nums outline-none transition-colors duration-150 focus-visible:text-(--fluid-slider-label) disabled:cursor-default disabled:pointer-events-none\",\n                    editableValue &&\n                      \"cursor-text hover:text-(--fluid-slider-label)\",\n                    valueClassName,\n                  )}\n                >\n                  {formattedValue}\n                </button>\n              )}\n            </div>\n          )}\n\n          <Slider.Thumb\n            render={<motion.div style={{ x: overshoot }} />}\n            data-fluid-slider-thumb=\"\"\n            aria-label={ariaLabel}\n            aria-valuetext={formattedValue}\n            className=\"absolute z-20 h-full w-0 outline-none\"\n          >\n            <span\n              data-fluid-slider-hit=\"\"\n              aria-hidden=\"true\"\n              className=\"absolute inset-y-0 z-20 w-11 -translate-x-1/2 cursor-grab active:cursor-grabbing rtl:translate-x-1/2\"\n              style={{\n                insetInlineStart: \"50%\",\n              }}\n            />\n            {showHandle && (\n              <span\n                aria-hidden=\"true\"\n                className={cn(\n                  \"pointer-events-none absolute top-1/2 start-1/2 h-7 w-1 -translate-x-1/2 -translate-y-1/2 rounded-full bg-(--fluid-slider-handle) opacity-90 shadow-[0_0_0_1px_color-mix(in_oklab,var(--background)_30%,transparent)] transition-[height,opacity] duration-150 group-data-[pressed]/fluid-slider-control:h-8 group-data-[pressed]/fluid-slider-control:opacity-100 rtl:translate-x-1/2\",\n                  handleClassName,\n                )}\n              />\n            )}\n          </Slider.Thumb>\n        </Slider.Track>\n      </Slider.Control>\n    </Slider.Root>\n  );\n}\n",
      "target": "components/ui/fluid-slider/fluid-slider.tsx"
    }
  ],
  "dependencies": [
    "@base-ui/react",
    "motion"
  ],
  "registryDependencies": [
    "@sona-ui/sona-utils"
  ]
}