FairGarden Design

Slider

A Base UI Slider for approximate values where relative position matters: an outlined track, an ink indicator and a tall thumb, with the value always shown in text.

import { Slider } from '@fairgarden-private/design/components/Slider'

The label and value sit above a full-width track; formatValue adds a unit or a derived rate ("1M (≈24 WPM)"). Arrow keys step, Page Up and Page Down step by ten, Home and End jump to the ends. Never make a slider the only way to enter a precise value; exact values belong in a Number Field.

States

States

Hover gives the thumb a soft --primary3 face; dragging steps its edge to 3 px, and a drag chip in the overlay face shows the value above the thumb. stepped adds the tick scale: 12 px major ticks with labels (marks, by default the ends and midpoint) and 6 px minor ticks at each step; labels thin to the ends and midpoint below 768 px and to the ends below 360 px. A range passes an array value and names each thumb with thumbLabels. Disabled draws dotted track and thumb edges and removes the indicator, but the value stays.

Walking Distance
6 km
Group Size
8
Price Range
$40 – $120
Elevation Gain
300 m
SliderStates.tsx
'use client'

import * as React from 'react'
import { Slider } from '@fairgarden-private/design/components/Slider'
import styles from './states.module.css'

/**
 * Continuous with a unit, stepped with a labelled tick scale, a two-thumb
 * range, and disabled (the indicator goes, the value stays).
 */
export function SliderStates() {
  return (
    <div className={styles.stack}>
      <Slider
        label="Walking Distance"
        defaultValue={6}
        max={20}
        step={0.5}
        formatValue={(formatted) => `${formatted[0]} km`}
      />
      <Slider
        label="Group Size"
        stepped
        defaultValue={8}
        min={0}
        max={20}
        marks={[
          { value: 0 },
          { value: 5 },
          { value: 10 },
          { value: 15 },
          { value: 20 },
        ]}
      />
      <Slider
        label="Price Range"
        defaultValue={[40, 120]}
        max={200}
        step={5}
        minStepsBetweenValues={2}
        format={{ style: 'currency', currency: 'USD', maximumFractionDigits: 0 }}
        thumbLabels={['Minimum price', 'Maximum price']}
      />
      <Slider label="Elevation Gain" defaultValue={300} max={1000} disabled formatValue={(formatted) => `${formatted[0]} m`} />
    </div>
  )
}

Primary scale

Primary scale

primary drives every part: the track edge, indicator, thumb edge, ticks and value. secondary is unused.

Scope Colors
40
Primary Plum
60
Primary Indigo
80
SliderColor.tsx
import { Slider } from '@fairgarden-private/design/components/Slider'
import styles from './color.module.css'

/** `primary` drives every part; `secondary` is unused, since the indicator is structure, not a selection. */
export function SliderColor() {
  return (
    <div className={styles.grid}>
      <Slider label="Scope Colors" defaultValue={40} />
      <Slider label="Primary Plum" primary="plum" defaultValue={60} />
      <Slider label="Primary Indigo" primary="indigo" defaultValue={80} />
    </div>
  )
}

On grounds

On paper and forest

The thumb face is --role-halo, the scope's step 1 on every ground and in both modes, ringed by its --primary12 edge. In print the control is hidden and the slider prints as "Label: value".

paper
Shade Cover
45%
Trail Grade
3
forest
Shade Cover
45%
Trail Grade
3
SliderGrounds.tsx
'use client'

import { Slider } from '@fairgarden-private/design/components/Slider'
import { PresetGround } from '@/components/PresetGround'
import styles from './grounds.module.css'

const presets = ['paper', 'forest'] as const

/** The same roles on two grounds; the thumb face is --role-halo, the scope's step 1, everywhere. */
export function SliderGrounds() {
  return (
    <div className={styles.row}>
      {presets.map((preset) => (
        <PresetGround key={preset} preset={preset} className={styles.face}>
          <span className={styles.name}>{preset}</span>
          <Slider label="Shade Cover" defaultValue={45} formatValue={(formatted) => `${formatted[0]}%`} />
          <Slider label="Trail Grade" stepped defaultValue={3} max={10} />
        </PresetGround>
      ))}
    </div>
  )
}

API Reference

A Base UI Slider for approximate values where relative position matters: an outlined track, a --primary12 indicator and a tall --role-halo thumb, with the value always shown in text. Exact values use a Number Field. A range passes an array value / defaultValue.

PropTypeDescription
label
React.ReactNode | undefined

The field label, above the track; title case up to about four words [D160].

formatValue
| ((
    formattedValues: string[],
    values: number[],
  ) => React.ReactNode)
| undefined

Formats the value readout at the right end of the label row, e.g. a unit or a derived rate in parentheses (“1M (≈24 WPM)"). Default: the formatted values joined by “ — “.

marks
SliderMark[] | undefined

stepped only: the major ticks. Default: min, the midpoint and max.

minorStep
number | undefined

stepped only: the minor tick spacing. Default step.

primary
| 'ruby'
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| null
| undefined

Primary Radix scale: every part (track edge, indicator, thumb edge, ticks, value). Never defaulted; omitted, it inherits the scope [D133].

secondary
| 'ruby'
| 'olive'
| 'sage'
| 'slate'
| 'sand'
| 'gray'
| 'mauve'
| 'brown'
| 'bronze'
| 'gold'
| 'red'
| 'crimson'
| 'tomato'
| 'pink'
| 'plum'
| 'indigo'
| 'iris'
| 'violet'
| 'purple'
| 'amber'
| 'blue'
| 'cyan'
| 'grass'
| 'green'
| 'jade'
| 'lime'
| 'mint'
| 'orange'
| 'sky'
| 'teal'
| 'yellow'
| null
| undefined

Secondary Radix scale: unused by the slider itself. Never defaulted.

stepped
boolean | null | undefined

Shows the tick scale under the track: minor and major ticks, major labels. Default false.

thumbLabels
string[] | undefined

Accessible names per thumb, needed for a range (“Minimum price”, “Maximum price”).

Additional types

slider
type slider = slider
SliderMark

A major tick on a stepped slider: its value and optional label.

type SliderMark = {
  /** The value the tick marks, between `min` and `max`. */
  value: number;
  /** The label under the tick; defaults to the formatted value. */
  label?: React.ReactNode;
}
SliderProps

Props for Slider: Base UI Slider.Root props plus the stepped and color axes, the label, the value readout and the tick scale.

type SliderProps<Value extends number | number[] = number | number[]> = {
  /** Shows the tick scale under the track: minor and major ticks, major labels. Default `false`. */
  stepped?: boolean | null;
  /**
   * Primary Radix scale: every part (track edge, indicator, thumb edge,
   * ticks, value). Never defaulted; omitted, it inherits the scope [D133].
   */
  primary?:
    | 'ruby'
    | 'olive'
    | 'sage'
    | 'slate'
    | 'sand'
    | 'gray'
    | 'mauve'
    | 'brown'
    | 'bronze'
    | 'gold'
    | 'red'
    | 'crimson'
    | 'tomato'
    | 'pink'
    | 'plum'
    | 'indigo'
    | 'iris'
    | 'violet'
    | 'purple'
    | null;
  /** Secondary Radix scale: unused by the slider itself. Never defaulted. */
  secondary?:
    | 'ruby'
    | 'olive'
    | 'sage'
    | 'slate'
    | 'sand'
    | 'gray'
    | 'mauve'
    | 'brown'
    | 'bronze'
    | 'gold'
    | 'red'
    | 'crimson'
    | 'tomato'
    | 'pink'
    | 'plum'
    | 'indigo'
    | 'iris'
    | 'violet'
    | 'purple'
    | 'amber'
    | 'blue'
    | 'cyan'
    | 'grass'
    | 'green'
    | 'jade'
    | 'lime'
    | 'mint'
    | 'orange'
    | 'sky'
    | 'teal'
    | 'yellow'
    | null;
  /** The field label, above the track; title case up to about four words [D160]. */
  label?: React.ReactNode;
  /**
   * Formats the value readout at the right end of the label row, e.g. a
   * unit or a derived rate in parentheses ("1M (≈24 WPM)"). Default: the
   * formatted values joined by " – ".
   */
  formatValue?: (formattedValues: string[], values: number[]) => React.ReactNode;
  /** `stepped` only: the major ticks. Default: `min`, the midpoint and `max`. */
  marks?: SliderMark[];
  /** `stepped` only: the minor tick spacing. Default `step`. */
  minorStep?: number;
  /** Accessible names per thumb, needed for a range ("Minimum price", "Maximum price"). */
  thumbLabels?: string[];
}

Specification: DESIGN-SYSTEM.md §10.10 (slider).