For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to documentation URLs. Homepage markdown is available at /llms.md/content.md. An agent skill is available at /.well-known/agent-skills/site-skill.md.

Slider

The Slider component from this design system, built on Base UI.

Installation

$ pnpm dlx shadcn@latest add @cobalt/slider

Source

import { Slider as SliderPrimitive } from "@base-ui/react/slider";
import { cn } from "cn";
import type * as React from "react";

function Slider({
  className,
  defaultValue,
  value,
  min = 0,
  max = 100,
  label,
  showTooltip = true,
  formatValue,
  thumbLabels,
  variant = "default",
  trackContent,
  ...props
}: SliderPrimitive.Root.Props & {
  /**
   * `stepped` is a 28px recessed track with a tick for every step and a
   * lifted block thumb, for choosing one of a few levels. Pass `step`.
   */
  variant?: "default" | "stepped";
  /** `stepped` only: drawn over the track, under the thumb. */
  trackContent?: React.ReactNode;
  label?: React.ReactNode;
  showTooltip?: boolean;
  formatValue?: (value: number, index: number) => React.ReactNode;
  thumbLabels?: string[];
}) {
  const current = value ?? defaultValue;
  const count = Array.isArray(current) ? current.length : 1;

  if (variant === "stepped") {
    const steps = Math.max(
      1,
      Math.round((max - min) / (Number(props.step) || 1))
    );
    return (
      <SliderPrimitive.Root
        data-slot="slider"
        data-variant="stepped"
        className={cn(
          "group flex min-w-0 flex-col gap-2 data-horizontal:w-full data-disabled:cursor-not-allowed data-disabled:opacity-50",
          className
        )}
        defaultValue={defaultValue}
        value={value}
        min={min}
        max={max}
        thumbAlignment="edge"
        {...props}
      >
        {label != null && (
          <SliderPrimitive.Label className="text-sm leading-5 font-medium text-foreground">
            {label}
          </SliderPrimitive.Label>
        )}
        <SliderPrimitive.Control className="relative flex h-7 w-full touch-none items-center select-none">
          <SliderPrimitive.Track
            data-slot="slider-track"
            className="relative h-full grow overflow-hidden rounded-[8px] bg-[var(--surface-sunken,var(--muted))]"
          >
            <SliderPrimitive.Indicator
              data-slot="slider-range"
              className="rounded-[8px] bg-[var(--border-strong,var(--accent))]"
            />
            {Array.from({ length: steps + 1 }, (_, index) => (
              <span
                aria-hidden
                key={index}
                className="pointer-events-none absolute top-1/2 h-3 w-0.5 -translate-x-1/2 -translate-y-1/2 rounded-full bg-[var(--foreground-subtle,var(--muted-foreground))]/50"
                style={{
                  left: `calc(10px + (100% - 20px) * ${index / steps})`,
                }}
              />
            ))}
            {trackContent}
          </SliderPrimitive.Track>
          {Array.from({ length: count }, (_, index) => (
            <SliderPrimitive.Thumb
              key={index}
              index={index}
              data-slot="slider-thumb"
              getAriaLabel={() => thumbLabels?.[index] ?? "Value"}
              className="h-7 w-5 cursor-grab rounded-[8px] border border-[var(--border-hover,var(--border))] bg-[var(--surface-raised,var(--background))] shadow-[0_1px_2px_0_rgb(0_0_0/0.18)] outline-none transition-[box-shadow] duration-150 focus-visible:ring-2 focus-visible:ring-ring active:cursor-grabbing motion-reduce:transition-none"
            />
          ))}
        </SliderPrimitive.Control>
      </SliderPrimitive.Root>
    );
  }

  return (
    <SliderPrimitive.Root
      data-slot="slider"
      className={cn(
        "group flex min-w-0 flex-col gap-2 data-horizontal:w-full data-vertical:h-full data-disabled:cursor-not-allowed data-disabled:opacity-50",
        className
      )}
      defaultValue={defaultValue}
      value={value}
      min={min}
      max={max}
      thumbAlignment="center"
      {...props}
    >
      {label != null && (
        <SliderPrimitive.Label className="text-sm leading-5 font-medium text-foreground">
          {label}
        </SliderPrimitive.Label>
      )}
      <SliderPrimitive.Control
        className={cn(
          "relative flex touch-none items-center select-none data-horizontal:h-8 data-horizontal:w-full data-vertical:h-full data-vertical:min-h-40 data-vertical:w-8 data-vertical:flex-col",
          showTooltip && "data-horizontal:mt-8"
        )}
      >
        <SliderPrimitive.Track
          data-slot="slider-track"
          className="relative grow rounded-full bg-[var(--surface-strong,var(--input))] shadow-[inset_0_1px_1px_rgb(0_0_0/0.06)] transition-colors duration-150 ease-[ease] group-hover:bg-[var(--border-strong,var(--border))] motion-reduce:transition-none data-horizontal:h-1.5 data-horizontal:w-full data-vertical:h-full data-vertical:w-1.5"
        >
          <SliderPrimitive.Indicator
            data-slot="slider-range"
            className="rounded-full bg-primary bg-[image:var(--slider-range-gradient,var(--gradient-button-primary-default,linear-gradient(90deg,var(--primary),var(--primary))))] shadow-[inset_0_1px_0_rgb(255_255_255/0.24),0_1px_2px_rgb(4_80_226/0.16)] data-horizontal:h-full data-vertical:w-full"
          />
        </SliderPrimitive.Track>
        {Array.from({ length: count }, (_, index) => (
          <SliderPrimitive.Thumb
            key={index}
            index={index}
            data-slot="slider-thumb"
            getAriaLabel={() =>
              thumbLabels?.[index] ??
              (count > 1 ? `Value ${index + 1}` : "Value")
            }
            className="group/thumb flex size-5 cursor-grab items-center justify-center rounded-full border border-border bg-[var(--slider-thumb-surface,var(--background))] shadow-[0_2px_4px_rgb(0_0_0/0.10),inset_0_1px_0_rgb(255_255_255/0.8)] outline-none transition-[scale,box-shadow,border-color] duration-150 ease-[ease] hover:scale-105 hover:border-[var(--border-hover,var(--border))] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background data-dragging:z-20 data-dragging:scale-110 data-dragging:border-ring data-dragging:shadow-[0_3px_8px_color-mix(in_srgb,var(--primary)_22%,transparent)] data-disabled:cursor-not-allowed motion-reduce:transition-none"
          >
            {showTooltip && (
              <span
                aria-hidden
                className="pointer-events-none absolute bottom-[calc(100%+9px)] left-1/2 flex h-7 min-w-8 -translate-x-1/2 items-center justify-center rounded-[8px] border border-border bg-popover px-2 text-xs leading-4 font-medium tracking-[0.15px] whitespace-nowrap text-foreground shadow-[shadow:var(--shadow-control,0_1px_2px_0_rgb(0_0_0/0.05))] transition-[translate,box-shadow] duration-150 ease-[ease] group-focus-within/thumb:-translate-y-0.5 group-focus-within/thumb:shadow-[shadow:var(--shadow-dropdown,0_1px_1px_0_rgb(0_0_0/0.04),0_4px_4px_0_rgb(0_0_0/0.02))] group-data-dragging/thumb:-translate-y-0.5 motion-reduce:transition-none"
              >
                <SliderPrimitive.Value>
                  {(formatted, values) =>
                    formatValue?.(values[index], index) ?? formatted[index]
                  }
                </SliderPrimitive.Value>
                <span
                  aria-hidden
                  className="absolute -bottom-[4.5px] left-1/2 size-2 -translate-x-1/2 rotate-45 border-r border-b border-border bg-popover"
                />
              </span>
            )}
            <span
              aria-hidden
              className="size-2 rounded-full bg-primary bg-[image:var(--gradient-button-primary-default,linear-gradient(180deg,var(--primary),var(--primary)))] shadow-[inset_0_1px_0_rgb(255_255_255/0.24)] transition-[filter] duration-150 ease-[ease] group-focus-within/thumb:brightness-110 group-data-dragging/thumb:brightness-110 motion-reduce:transition-none"
            />
          </SliderPrimitive.Thumb>
        ))}
      </SliderPrimitive.Control>
    </SliderPrimitive.Root>
  );
}

export { Slider };