{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "slider-range",
  "title": "Slider Range",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "A two-thumb Radix slider with a labeled value span, keyboard operation, and minimum thumb spacing.",
  "dependencies": [
    "radix-ui"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.ballmac.com/r/direction.json",
    "https://ui.ballmac.com/r/i18n.json"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/slider-range.tsx",
      "content": "// Ballmac UI: Slider Range. https://ui.ballmac.com/components/slider-range\n\"use client\"\n\nimport * as React from \"react\"\nimport { Slider as SliderPrimitive } from \"radix-ui\"\nimport { useDirection } from \"@/lib/ballmac/direction\"\nimport { cn } from \"@/lib/utils\"\nimport { useMessages } from \"@/lib/ballmac/i18n\"\n\ntype SliderRangeProps = Omit<\n  React.ComponentProps<typeof SliderPrimitive.Root>,\n  \"value\" | \"defaultValue\" | \"onValueChange\"\n> & {\n  /** Visible label for the range. */\n  label: string\n  /** Controlled lower and upper values. */\n  value?: [number, number]\n  /** Initial lower and upper values. */\n  defaultValue?: [number, number]\n  /** Called when either thumb changes. */\n  onValueChange?: (value: [number, number]) => void\n  /** Formats both displayed values. */\n  formatValue?: (value: number) => string\n  /** Minimum number of steps between thumbs. */\n  minStepsBetweenThumbs?: number\n}\nfunction SliderRange({\n  className,\n  label,\n  value,\n  defaultValue = [25, 75],\n  onValueChange,\n  formatValue = (number) => String(number),\n  min = 0,\n  max = 100,\n  step = 1,\n  minStepsBetweenThumbs = 1,\n  disabled,\n  ...props\n}: SliderRangeProps) {\n  const msg = useMessages()\n  const [internal, setInternal] = React.useState<[number, number]>(defaultValue)\n  const current = value ?? internal\n  const dir = useDirection(props.dir)\n  return (\n    <div\n      data-slot=\"slider-range\"\n      className={cn(\"flex min-w-0 flex-col gap-4\", className)}\n    >\n      <div className=\"flex items-center justify-between gap-3 text-sm\">\n        <span className=\"font-medium\">{label}</span>\n        <span\n          data-slot=\"slider-range-value\"\n          className=\"text-muted-foreground shrink-0 font-mono text-xs tabular-nums\"\n        >\n          {formatValue(current[0])} – {formatValue(current[1])}\n        </span>\n      </div>\n      <SliderPrimitive.Root\n        dir={dir}\n        {...props}\n        value={current}\n        onValueChange={(next) => {\n          const pair: [number, number] = [next[0], next[1]]\n          if (value === undefined) setInternal(pair)\n          onValueChange?.(pair)\n        }}\n        min={min}\n        max={max}\n        step={step}\n        minStepsBetweenThumbs={minStepsBetweenThumbs}\n        disabled={disabled}\n        className=\"relative flex h-5 w-full touch-none select-none items-center data-[disabled]:opacity-50\"\n      >\n        <SliderPrimitive.Track\n          data-slot=\"slider-range-track\"\n          className=\"bg-secondary relative h-2 w-full grow overflow-hidden rounded-full\"\n        >\n          <SliderPrimitive.Range\n            data-slot=\"slider-range-fill\"\n            className=\"bg-primary absolute h-full\"\n          />\n        </SliderPrimitive.Track>\n        <SliderPrimitive.Thumb\n          data-slot=\"slider-range-min\"\n          aria-label={msg(\"slider-range.minimum\", \"{label} minimum\", { label })}\n          className=\"border-primary bg-background block size-5 rounded-full border-2 shadow-sm outline-none transition-transform duration-150 focus-visible:ring-[3px] focus-visible:ring-ring/50 motion-safe:hover:scale-110 motion-reduce:transition-none\"\n        />\n        <SliderPrimitive.Thumb\n          data-slot=\"slider-range-max\"\n          aria-label={msg(\"slider-range.maximum\", \"{label} maximum\", { label })}\n          className=\"border-primary bg-background block size-5 rounded-full border-2 shadow-sm outline-none transition-transform duration-150 focus-visible:ring-[3px] focus-visible:ring-ring/50 motion-safe:hover:scale-110 motion-reduce:transition-none\"\n        />\n      </SliderPrimitive.Root>\n      <div className=\"text-muted-foreground flex justify-between text-xs tabular-nums\">\n        <span>{formatValue(min)}</span>\n        <span>{formatValue(max)}</span>\n      </div>\n    </div>\n  )\n}\nexport { SliderRange, type SliderRangeProps }\n",
      "type": "registry:ui",
      "target": "@components/ballmac/slider-range.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "slider",
      "range",
      "filter"
    ],
    "version": "1.0.0",
    "updated": "2026-09-30",
    "ai": {
      "summary": "A two-thumb Radix slider with a labeled value span, keyboard operation, and minimum thumb spacing.",
      "whenToUse": [
        "Filter a bounded numeric interval"
      ],
      "whenNotToUse": [
        "Use Number Input for exact values"
      ],
      "composesWith": [
        "number-input"
      ],
      "a11y": [
        {
          "keys": "Arrow keys / Home / End / Page Up / Page Down",
          "action": "Adjusts the focused minimum or maximum thumb"
        }
      ],
      "customization": [
        "Controlled and uncontrolled value",
        "Tokens and state styling"
      ]
    },
    "examples": [
      "slider-range-demo",
      "slider-range-states"
    ],
    "url": "https://ui.ballmac.com/components/slider-range"
  },
  "categories": [
    "forms"
  ],
  "type": "registry:ui"
}