Refined UI

An opinionated, customizable superset of shadcn/ui

AgentsPrimitivesSkillsSkins
Guides
  • Overview
  • Get started
  • Shadcn compatibility
  • Architecture
  • Agent surface
Primitives
  • Accordion
  • Alert
  • Alert dialog
  • Aspect ratio
  • Autocomplete
  • Avatar
  • Badge
  • Button
  • Button group
  • Calendar
  • Card
  • Checkbox
  • Checkbox Group
  • Code
  • Code Diff
  • Collapsible
  • Color Picker
  • Combobox
  • Command
  • Date Picker
  • Dialog
  • Drawer
  • Empty
  • Field
  • Form
  • Gradient Editor
  • Hover card
  • Input
  • Input group
  • Input OTP
  • Item
  • Kbd
  • Markdown
  • Menu
  • Menubar
  • Meter
  • Native select
  • Navigation menu
  • Number Field
  • Pagination
  • Popover
  • Progress
  • Radio group
  • Resizable
  • Scroll area
  • Select
  • Sidebar
  • Skeleton
  • Slider
  • Spinner
  • Switch
  • Table
  • Table of contents
  • Tabs
  • Textarea
  • Toast
  • Toggle
  • Toolbar
  • Tooltip
  • Tree
  • Trigger Menu
  • Typography
Hooks
  • Use Chat Message
  • Use Chat Input
  • Use User Ask
  • Use Audio Recorder
  • Use Environment Variables
  • Use Copy To Clipboard
  • Use Tool Call
  • Use Sidebar Resize
Utils
  • cn
  • Skin
  • Contracts
  • Serialize
Extensions
  • Control Effects
  • View Transition
  • Send Aurora
  1. Primitives
  2. Slider
Primitives

Slider

Single-value range control with branded and plain treatments.

Composition

The preferred shape for composing the installed primitive from its exported parts.

Root

Single-slot surface with no nested parts.

Slider

The refined source installs this primitive from src/registry/sources/refined/ui/slider.tsx. Install it on its own with the command above, or inspect the source below.

Registry command
npx shadcn@latest add http://127.0.0.1:3000/r/refined/slider.json
See registry manifest

Raw code

Primary installed primitive source

Base UI Slider slot
src/registry/sources/refined/ui/slider.tsx
"use client";
​
import { Slider as SliderPrimitive } from "@base-ui/react/slider";
import { cva } from "class-variance-authority";
import type { SliderProps } from "@/components/refined-ui/contracts";
import { cn } from "@/components/refined-ui/lib/cn";
import { skinSlot } from "@/components/refined-ui/skin";
​
const MAX_VISIBLE_STEP_TICKS = 50;
​
// Tick offsets (%) for stepped slider; skipped when steps too fine/coarse to read as marks (matches one-rec's threshold).
function tickPositions(min: number, max: number, step: number | undefined): number[] {
if (typeof step !== "number" || step <= 0 || !Number.isFinite(step)) return [];
const range = max - min;
if (range <= 0) return [];
const stepCount = Math.round(range / step);
if (stepCount <= 1 || stepCount > MAX_VISIBLE_STEP_TICKS) return [];
return Array.from({ length: stepCount - 1 }, (_, i) => ((i + 1) / stepCount) * 100);
}
​
// variant: "default" = branded (muted track/primary indicator/pill thumb). "plain" = neutral monochrome (one-rec precision slider), for unbranded contexts.
// plain + label/showValue grows into labeled bar: taller track, label+live value inside, step ticks.
const trackVariant = cva("relative w-full grow overflow-hidden transition-colors duration-[var(--duration-fast)]", {
variants: {
variant: {
default: "h-1.5 rounded-full bg-muted",
plain: "h-1.5 rounded-[3px] bg-foreground/6 data-[dragging]:bg-foreground/10",
},
},
defaultVariants: { variant: "default" },
});
​
const indicatorVariant = cva("transition-colors duration-[var(--duration-fast)]", {
variants: {
variant: {
default: "rounded-full bg-primary",
plain: "bg-foreground/14 data-[dragging]:bg-foreground/25",
},
},
defaultVariants: { variant: "default" },
});
​
const thumbVariant = cva(
"block outline-none transition-[transform,background-color,box-shadow,height,width] duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-foreground/30",
{
variants: {
variant: {
default:
"size-3.5 rounded-full border border-border bg-background shadow-sm hover:scale-110 data-[dragging]:scale-110 data-[dragging]:shadow-md",
plain:
"h-4 w-0.5 rounded-[1px] bg-foreground/30 after:absolute after:-inset-3 after:content-[''] hover:bg-foreground/50 data-[dragging]:h-5 data-[dragging]:bg-foreground/70",
},
},
defaultVariants: { variant: "default" },
},
);
​
// Bottom-anchored so grow-on-drag reads as tick rising from track, not expanding from middle.
const tick =
"pointer-events-none absolute bottom-0 h-1.5 w-px bg-foreground/15 transition-[height,background-color] duration-[var(--duration-fast)] ease-[var(--ease-standard)] group-data-[dragging]:h-3 group-data-[dragging]:bg-foreground/25";
​
export function Slider({
className,
variant = "default",
label,
showValue,
formatValue,
value,
defaultValue,
onValueChange,
min = 0,
max = 100,
step,
disabled,
...props
}: SliderProps) {
const showValueResolved = showValue ?? Boolean(label);
const labeled = variant === "plain" && (label !== undefined || showValue === true);
const ticks = labeled ? tickPositions(min, max, step) : [];
​
// value/defaultValue passed explicitly, not via spread: Base UI reads controlled-ness from `value !== undefined` on first render; React Compiler can spread `undefined` for a tick, tripping the uncontrolled→controlled warning.
return (
<SliderPrimitive.Root
data-ui="agent"
data-slot="slider"
data-variant={variant}
data-labeled={labeled ? "true" : "false"}
value={value}
defaultValue={defaultValue}
min={min}
max={max}
step={step}
disabled={disabled}
onValueChange={onValueChange ? (next) => onValueChange(Array.isArray(next) ? next[0] : next) : undefined}
className={cn(
"group relative flex w-full cursor-pointer touch-none select-none items-center data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50",
skinSlot("slider", { variant, labeled }),
className,
)}
{...props}
>
<SliderPrimitive.Control
data-ui="agent"
data-slot="slider-control"
className={cn("flex w-full items-center", labeled ? "h-[1.875rem]" : "py-1.5", skinSlot("slider-control", {}))}
>
<SliderPrimitive.Track
data-ui="agent"
data-slot="slider-track"
className={cn(trackVariant({ variant }), labeled && "h-full rounded-[var(--radius-control)]", skinSlot("slider-track", {}))}
>
<SliderPrimitive.Indicator
data-ui="agent"
data-slot="slider-indicator"
className={cn(indicatorVariant({ variant }), skinSlot("slider-indicator", {}))}
/>
{ticks.map((pct) => (
<span key={pct} aria-hidden className={tick} style={{ left: `${pct}%` }} />
))}
<SliderPrimitive.Thumb
data-ui="agent"
data-slot="slider-thumb"
className={cn(thumbVariant({ variant }), skinSlot("slider-thumb", {}))}
/>
</SliderPrimitive.Track>
</SliderPrimitive.Control>
{labeled && (
<div className="pointer-events-none absolute inset-0 z-10 flex items-center justify-between px-3">
{label ? (
<SliderPrimitive.Label
data-ui="agent"
data-slot="slider-label"
className={cn(
"select-none text-meta leading-3.5 tracking-tight text-muted-foreground transition-all duration-[var(--duration-fast)] ease-[var(--ease-standard)]",
"group-data-[dragging]:-translate-x-1 group-data-[dragging]:-translate-y-1.5 group-data-[dragging]:scale-90",
skinSlot("slider-label", {}),
)}
>
{label}
</SliderPrimitive.Label>
) : (
<span />
)}
{showValueResolved && (
<SliderPrimitive.Value
data-ui="agent"
data-slot="slider-value"
className={cn(
"select-none text-meta leading-3.5 tracking-tight text-muted-foreground tabular-nums transition-all duration-[var(--duration-fast)] ease-[var(--ease-standard)]",
"group-data-[dragging]:translate-x-0.5 group-data-[dragging]:-translate-y-1.5 group-data-[dragging]:text-foreground",
skinSlot("slider-value", {}),
)}
>
{(_, values) => (formatValue ? formatValue(values[0]) : Math.round(values[0]))}
</SliderPrimitive.Value>
)}
</div>
)}
</SliderPrimitive.Root>
);
}
​

On this page

  • Preview
  • Composition
  • Installation
  • Source
Default
Plain
Plain, labeled
Zoom
50%
Disabled