{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "slider",
  "type": "registry:ui",
  "title": "Slider",
  "description": "Slider on Base UI with a hairline rail, a keycap thumb, a value bubble while dragging, optional labelled marks, Intl number formatting and vertical support.",
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/slider.tsx",
      "type": "registry:ui",
      "target": "components/ui/slider.tsx",
      "content": "\"use client\"\nimport * as React from \"react\"\nimport { Slider as SliderPrimitive } from \"@base-ui/react/slider\"\nimport { cn } from \"@/lib/utils\"\n\n/**\n * Slider - hairline rail, keycap thumb and a value bubble while you drag or tab to it.\n * Base UI for pointer, keyboard and form behaviour. Hairline rail with\n * an accent fill, a raised keycap thumb whose ring grows on hover, drag and\n * focus, and an ink value bubble that settles in above the thumb while you\n * drag or tab to it. Optional marks sit under the rail.\n * RTL needs Base UI's <DirectionProvider direction=\"rtl\">, not only dir=\"rtl\",\n * or dragging and arrow keys run backwards.\n */\n\nconst THUMB = 16\n\ntype Mark = number | { value: number; label?: React.ReactNode }\n\ntype SliderProps = Omit<SliderPrimitive.Root.Props, \"format\"> & {\n  /** Formats the value in the bubble and for screen readers. */\n  format?: Intl.NumberFormatOptions | ((value: number) => string)\n  /** Ticks under the rail. Pass `{ value, label }` to caption them. */\n  marks?: Mark[]\n  /** Show the value above the thumb while dragging or focused. */\n  showValue?: boolean\n}\n\nconst toArray = (v: number | readonly number[] | undefined) =>\n  v === undefined ? undefined : Array.isArray(v) ? [...v] : [v as number]\n\n/* With edge alignment the thumb centre travels from THUMB/2 to 100% - THUMB/2. */\nconst along = (p: number) => `calc(${THUMB / 2}px + (100% - ${THUMB}px) * ${p})`\n\nfunction Slider({\n  className,\n  defaultValue,\n  value,\n  onValueChange,\n  min = 0,\n  max = 100,\n  format,\n  locale,\n  marks,\n  showValue = true,\n  orientation = \"horizontal\",\n  \"aria-label\": ariaLabelProp,\n  \"aria-labelledby\": ariaLabelledBy,\n  ...props\n}: SliderProps) {\n  const [inner, setInner] = React.useState<number[]>(() => toArray(defaultValue) ?? [min])\n  const values = toArray(value) ?? inner\n  const uid = React.useId()\n  const ariaLabel = ariaLabelProp ?? (ariaLabelledBy ? undefined : \"Value\")\n  const suffix = (i: number) => (values.length === 2 ? (i === 0 ? \"minimum\" : \"maximum\") : values.length > 2 ? `thumb ${i + 1}` : null)\n\n  const fmt = React.useMemo(() => {\n    if (typeof format === \"function\") return format\n    const nf = new Intl.NumberFormat(locale as Intl.LocalesArgument, format)\n    return (n: number) => nf.format(n)\n  }, [format, locale])\n\n  const pct = (v: number) => (max === min ? 0 : (Math.min(max, Math.max(min, v)) - min) / (max - min))\n  const lo = values.length > 1 ? Math.min(...values) : min\n  const hi = Math.max(...values)\n  const labelled = marks?.some((m) => typeof m === \"object\" && m.label != null)\n\n  return (\n    <SliderPrimitive.Root\n      data-slot=\"slider\"\n      className={cn(\"group/slider data-horizontal:w-full data-vertical:h-full\", className)}\n      defaultValue={defaultValue}\n      value={value}\n      onValueChange={(next, details) => {\n        if (value === undefined) setInner(toArray(next) ?? [min])\n        onValueChange?.(next, details)\n      }}\n      min={min}\n      max={max}\n      format={typeof format === \"object\" ? format : undefined}\n      locale={locale}\n      orientation={orientation}\n      thumbAlignment=\"edge\"\n      aria-labelledby={ariaLabelledBy}\n      {...props}\n    >\n      <SliderPrimitive.Control className=\"relative flex w-full cursor-pointer touch-none items-center py-1 select-none data-disabled:cursor-not-allowed data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col data-vertical:px-1 data-vertical:py-0\">\n        <SliderPrimitive.Track\n          data-slot=\"slider-track\"\n          className=\"relative grow overflow-hidden rounded-full bg-sunken shadow-[inset_0_0_0_1px_var(--border)] select-none data-horizontal:h-1.5 data-horizontal:w-full data-vertical:h-full data-vertical:w-1.5\"\n        >\n          <SliderPrimitive.Indicator\n            data-slot=\"slider-range\"\n            className=\"rounded-full bg-accent select-none group-aria-invalid/slider:bg-danger group-data-invalid/slider:bg-danger data-horizontal:h-full data-vertical:w-full\"\n          />\n        </SliderPrimitive.Track>\n        {values.map((v, index) => (\n          <SliderPrimitive.Thumb\n            data-slot=\"slider-thumb\"\n            key={index}\n            index={index}\n            getAriaLabel={ariaLabel ? () => (suffix(index) ? `${ariaLabel}, ${suffix(index)}` : ariaLabel) : undefined}\n            aria-labelledby={!ariaLabel && suffix(index) ? `${ariaLabelledBy} ${uid}-t${index}` : undefined}\n            getAriaValueText={typeof format === \"function\" ? (_s, n) => format(n) : undefined}\n            className={cn(\n              \"group/thumb relative block size-4 shrink-0 cursor-grab rounded-full border border-border-strong bg-raised shadow-key select-none\",\n              \"after:absolute after:-inset-3.5 after:rounded-full\",\n              \"transition-[box-shadow,border-color] duration-[140ms] ease-hairline\",\n              \"not-data-disabled:hover:ring-4 not-data-disabled:hover:ring-fg/[0.06]\",\n              \"data-dragging:cursor-grabbing data-dragging:border-accent-line data-dragging:ring-4 data-dragging:ring-accent-soft\",\n              \"has-[:focus-visible]:border-accent has-[:focus-visible]:ring-4 has-[:focus-visible]:ring-accent-line\",\n              \"data-disabled:cursor-not-allowed\"\n            )}\n          >\n            {!ariaLabel && suffix(index) ? (\n              <span id={`${uid}-t${index}`} className=\"sr-only\">\n                {suffix(index)}\n              </span>\n            ) : null}\n            {showValue ? (\n              <span\n                aria-hidden\n                data-slot=\"slider-value\"\n                className={cn(\n                  \"pointer-events-none absolute z-10 hidden rounded-md bg-ink px-1.5 py-0.5 text-xs font-medium whitespace-nowrap text-on-ink tabular-nums shadow-overlay\",\n                  \"animate-[glyph-in_180ms_var(--ease-hairline)_both]\",\n                  \"bottom-full left-1/2 mb-2 -translate-x-1/2\",\n                  \"group-data-vertical/thumb:bottom-auto group-data-vertical/thumb:left-full group-data-vertical/thumb:top-1/2 group-data-vertical/thumb:mb-0 group-data-vertical/thumb:ml-3 group-data-vertical/thumb:translate-x-0 group-data-vertical/thumb:-translate-y-1/2\",\n                  \"group-data-dragging/thumb:block group-has-[:focus-visible]/thumb:block\"\n                )}\n              >\n                {fmt(v)}\n              </span>\n            ) : null}\n          </SliderPrimitive.Thumb>\n        ))}\n      </SliderPrimitive.Control>\n      {marks?.length && orientation === \"horizontal\" ? (\n        <div aria-hidden data-slot=\"slider-marks\" className={cn(\"relative mt-1\", labelled ? \"h-6\" : \"h-1.5\")}>\n          {marks.map((m) => {\n            const mv = typeof m === \"number\" ? m : m.value\n            const label = typeof m === \"number\" ? null : m.label\n            const inRange = mv >= lo && mv <= hi\n            return (\n              <span key={mv} className=\"absolute top-0 flex -translate-x-1/2 flex-col items-center gap-1 rtl:translate-x-1/2\" style={{ insetInlineStart: along(pct(mv)) }}>\n                <span className={cn(\"h-1.5 w-px rounded-full transition-colors duration-[70ms]\", inRange ? \"bg-accent\" : \"bg-border-strong\")} />\n                {label != null ? (\n                  <span className={cn(\"text-2xs whitespace-nowrap tabular-nums transition-colors duration-[70ms]\", inRange ? \"text-fg-muted\" : \"text-fg-subtle\")}>{label}</span>\n                ) : null}\n              </span>\n            )\n          })}\n        </div>\n      ) : null}\n    </SliderPrimitive.Root>\n  )\n}\n\nexport { Slider }\nexport type { SliderProps }\n"
    }
  ]
}
