MD3
Expressive
MATERIAL DESIGN 3 EXPRESSIVE

Sliders

Sliders allow users to select a value or a range of values by sliding a "handle" along a track. The MD3 Expressive Slider features a signature pill-shaped handle, gap spacing, asymmetric corner radii, and interactive compression effects.

Sliders are ideal controls when users need to select from a continuous or discrete range of values. The MD3 Expressive edition stands out with its 4px pill handle that compresses to 2px when pressed, a 6px gap between the track and handle, and asymmetric corner radii (outer=full/custom, inner=2px).

Introduction

The MD3 Expressive Slider is a high-fidelity input component designed for precision and tactile feedback. It features a unique "pill-shaped" thumb that responds to touch by compressing, mimicking the physical sensation of pressing a button. It supports various configurations, including centered tracks for neutral-point adjustments, range sliders for selecting intervals, discrete snapping steps, and track inset icons with automatic state transitions.

Anatomy

  • Track: The horizontal (or vertical) bar representing the range of values.
  • Thumb (Handle): The interactive element the user slides. Features a 4dp pill shape that compresses to 2dp on press.
  • Active Track: The highlighted portion of the track indicating the selected value. Uses var(--md-sys-color-[variant]).
  • Inactive Track: The remaining portion of the track. Uses var(--md-sys-color-[variant]-container).
  • Value Indicator (Optional): A tooltip that displays the current numerical value during interaction.
  • Ticks (Optional): Visual dot marks indicating discrete steps on the track. Ticks on the active track adapt to var(--md-sys-color-on-[variant]) for optimal visual contrast.

Variants

Basic

The standard interactive slider component with value indicator tooltip.

Loading demo...

Continuous vs Discrete

  • Continuous: Allows selection of any value within the range.
  • Discrete: Snaps to specific intervals defined by the step prop. Set showTicks to true to visualize step marks along the track.
Loading demo...

Track Sizes

Supports 5 Material 3 track sizes ranging from xs (4dp) to xl (32dp).

Loading demo...

Color Variants

Supports 4 standard Material 3 color schemes: primary, secondary, tertiary, and error.

Loading demo...

Centered Track

Ideal for settings that have a neutral starting point (e.g., exposure, pan, or balance), where the active track grows from the center (50%) outwards.

Loading demo...

Range Slider

Allows users to select a range of values using two independent thumbs. Prevents thumbs from crossing each other and automatically handles z-index ordering for the last-dragged thumb.

Loading demo...

Features

Inset Icons

For track sizes of md (40dp) or larger, you can place icons directly inside the track. Inset icons dynamically transition between the active and inactive track segments depending on thumb position.

  • insetIcon: Leading icon (rendered inside track).
  • insetIconAtMin: Alternate icon displayed when value === min (e.g., swapping a speaker icon for a mute icon at 0%).
  • insetIconTrailing: Trailing icon rendered at the far end.
  • insetIconAtMax: Alternate icon displayed when value === max.
Loading demo...

External Icons

In addition to track inset icons, leading and trailing icons can be placed alongside the slider using standard container flex layouts.

Loading demo...

Custom Track Shapes

The trackShape prop configures outer track corner rounding:

  • "md3" (default): MD3 specific border radius per size.
  • "full": Fully rounded pill shape (size / 2).
  • number: Custom pixel radius (e.g., 4 for subtle rounded corners).
Loading demo...

Disabled State

When disabled is set to true, interaction is disabled and standard 38% opacity is applied.

Loading demo...

Thumb Compression

On press or touch, the thumb width reduces from 4dp to 2dp, providing a subtle but powerful tactile feedback loop driven by Motion spring animations.

Orientation

Supports both horizontal and vertical layouts. Vertical sliders are useful for volume or intensity controls in dense interfaces.

Loading demo...

Usage

Basic Usage

import { Slider } from "@bug-on/m3-expressive";
import { useState } from "react";

const [value, setValue] = useState(50);

<Slider 
  value={value} 
  onValueChange={setValue} 
  min={0} 
  max={100} 
/>

Range Selection

import { RangeSlider } from "@bug-on/m3-expressive";
import { useState } from "react";

const [range, setRange] = useState([20, 80]);

<RangeSlider 
  value={range} 
  onValueChange={setRange} 
  min={0} 
  max={100} 
/>

Inset Icon with Swapping at Min

import { Slider } from "@bug-on/m3-expressive";
import { Volume2, VolumeX } from "lucide-react";
import { useState } from "react";

const [volume, setVolume] = useState(0);

<Slider
  value={volume}
  onValueChange={setVolume}
  size="lg"
  insetIcon={<Volume2 className="size-5" />}
  insetIconAtMin={<VolumeX className="size-5" />}
/>

Best Practices

Do

  • Use sliders for settings where the relative value change matters more than exact digits (e.g., volume, brightness, zoom).
  • Provide immediate feedback as the user slides the thumb (onValueChange).
  • Use onValueChangeEnd when triggering expensive operations (like API queries) on release.
  • Use isCentered for adjustments with a logical zero or middle point.
  • Ensure the slider has an aria-label or is linked via aria-labelledby.

Don't

  • Don't use a slider for entering precise data where a Text Field would be more efficient.
  • Don't crowd too many sliders into a single view.
  • Avoid using Inset Icons for small track sizes (xs, sm) as they are disabled by spec to prevent clutter.

Accessibility

  • Keyboard: Navigate with Arrow keys (fine-tuning) and Home/End (min/max ends).
  • Roles: Correctly implements role="slider".
  • States: Communicates current value via aria-valuenow, aria-valuemin, aria-valuemax.
  • Touch: Provides a minimum 48x48dp touch target for interactive handles.
  • Motion: Respects prefers-reduced-motion user preferences.

API Reference

Slider

PropTypeDefaultDescription
valuenumberControlled value.
defaultValuenumber(min+max)/2Initial value for uncontrolled usage.
onValueChange(value: number) => voidCallback fired during dragging or keyboard adjustments.
onValueChangeEnd(value: number) => voidCallback fired when user releases thumb or commits keyboard input.
minnumber0Minimum allowed value.
maxnumber100Maximum allowed value.
stepnumber0Snap step increment. 0 disables snapping (continuous mode).
orientation"horizontal" | "vertical""horizontal"Layout direction.
size"xs" | "sm" | "md" | "lg" | "xl""md"Physical track thickness per MD3 spec.
variant"primary" | "secondary" | "tertiary" | "error""primary"MD3 color scheme.
trackShape"md3" | "full" | number"md3"Corner radius style for outer track ends.
isCenteredbooleanfalseWhen true, active track grows from center (50%) outward.
showValueIndicatorbooleanfalseShows floating value tooltip on interaction.
showTicksbooleanfalseShows tick dot markers for discrete mode (step > 0).
disabledbooleanfalseDisables interaction and applies 38% opacity.
insetIconReactNodeLeading icon rendered inside track (track size >= 40dp).
insetIconAtMinReactNodeAlternate leading icon displayed when value === min.
insetIconTrailingReactNodeTrailing icon rendered inside track.
insetIconAtMaxReactNodeAlternate trailing icon displayed when value === max.
formatValue(value: number) => stringStringFormatter function for displayed tooltip value.
classNamestringAdditional CSS classes applied to outer container.
aria-labelstringAccessible label for screen readers.
aria-labelledbystringID of element labeling the slider.

RangeSlider

Extends SliderProps (excluding value, defaultValue, onValueChange, onValueChangeEnd, and isCentered).

PropTypeDefaultDescription
value[number, number]Controlled [start, end] tuple.
defaultValue[number, number][min, max]Initial [start, end] tuple for uncontrolled usage.
onValueChange(value: [number, number]) => voidCallback fired during range drag. Prevents thumb crossover.
onValueChangeEnd(value: [number, number]) => voidCallback fired when range drag ends.