Slider

Material Design 3 slider component for value selection with support for ranges, steps, and marks

Slider

Sliders allow users to make selections from a range of values. @duskmoon-dev/core provides a complete set of Material Design 3 slider variants with support for single values, ranges, discrete steps, and visual marks.

Basic Usage

A basic slider with default styling:

Basic Slider

50

Color Variants

Primary (Default)

The default primary color variant:

Primary Slider

60

Secondary

Secondary color variant for different emphasis:

Secondary Slider

40

Tertiary

Tertiary color variant:

Tertiary Slider

75

Sizes

Sliders come in three sizes to fit different use cases:

Small

Compact slider for space-constrained interfaces:

Small Slider

30

Medium (Default)

Standard size for most use cases:

Medium Slider

50

Large

Larger slider for emphasis or better touch targets:

Large Slider

70

Range Slider

Range sliders allow users to select a range of values using two thumbs:

Range Slider

20
80

Discrete Slider with Steps

Sliders can snap to discrete values using marks:

Discrete Slider with Steps

50

Marks with Labels

Add labels to marks for better context:

Marks with Labels

0
25
50
75
100
25

Min/Max Labels

Display minimum and maximum values:

Slider with Min/Max Labels

60
0 100

Always Show Labels

Show value labels permanently instead of only on hover/active:

Slider with Always-Visible Labels

45

Vertical Slider

Vertical orientation for space-constrained layouts:

Vertical Slider

40

Disabled State

Disabled sliders are non-interactive:

Disabled Slider

50

Interactive Examples

Volume Control

Volume Control Slider

70%
🔇 Mute 🔊 Max

Temperature Range

Temperature Range Slider

18°C
22°C
18°C
22°C

Price Range Filter

Price Range Filter

$15
$85
$0 $100

Best Practices

Accessibility

  • Always provide clear labels indicating the purpose of the slider
  • Include min/max labels when the range is not obvious
  • Ensure keyboard navigation support (arrow keys for adjustment)
  • Use aria-valuenow, aria-valuemin, aria-valuemax, and aria-label attributes
  • Consider always showing value labels for better accessibility

Accessible Slider

50

Value Feedback

Provide immediate visual feedback when values change:

  • Use value labels to show current selection
  • Consider always showing labels for critical values
  • Update labels in real-time as users drag
  • For ranges, clearly indicate both min and max values

Touch Targets

Ensure thumb controls are large enough for touch interfaces:

Responsive Touch Target

50

Step Granularity

Choose appropriate step sizes:

  • Continuous: For precise control (no marks)
  • Coarse steps: For quick selection (few marks, e.g., 0, 25, 50, 75, 100)
  • Fine steps: For balanced control (many marks, e.g., every 5 or 10)

Framework Examples

React

import { useState } from 'react';

interface SliderProps {
  min?: number;
  max?: number;
  value?: number;
  onChange?: (value: number) => void;
  color?: 'primary' | 'secondary' | 'tertiary';
  size?: 'sm' | 'md' | 'lg';
  showLabels?: boolean;
}

export function Slider({
  min = 0,
  max = 100,
  value = 50,
  onChange,
  color = 'primary',
  size = 'md',
  showLabels = false
}: SliderProps) {
  const [currentValue, setCurrentValue] = useState(value);

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const newValue = Number(e.target.value);
    setCurrentValue(newValue);
    onChange?.(newValue);
  };

  const percentage = ((currentValue - min) / (max - min)) * 100;

  return (
    <div>
      <div className={`slider slider-${color} ${size !== 'md' ? `slider-${size}` : ''} ${showLabels ? 'slider-labels-always' : ''}`}>
        <div className="slider-track">
          <div className="slider-track-filled" style={{ width: `${percentage}%` }}></div>
        </div>
        <div className="slider-thumb" style={{ left: `${percentage}%` }}>
          <div className="slider-thumb-label">{currentValue}</div>
        </div>
        <input
          type="range"
          min={min}
          max={max}
          value={currentValue}
          onChange={handleChange}
          className="absolute inset-0 opacity-0 cursor-pointer"
        />
      </div>
      {showLabels && (
        <div className="slider-labels">
          <span>{min}</span>
          <span>{max}</span>
        </div>
      )}
    </div>
  );
}

// Usage
<Slider
  min={0}
  max={100}
  value={50}
  onChange={(value) => console.log(value)}
  color="secondary"
  showLabels
/>

Vue

<template>
  <div>
    <div
      :class="[
        'slider',
        `slider-${color}`,
        size !== 'md' && `slider-${size}`,
        showLabels && 'slider-labels-always'
      ]"
    >
      <div class="slider-track">
        <div class="slider-track-filled" :style="{ width: `${percentage}%` }"></div>
      </div>
      <div class="slider-thumb" :style="{ left: `${percentage}%` }">
        <div class="slider-thumb-label">{{ modelValue }}</div>
      </div>
      <input
        type="range"
        :min="min"
        :max="max"
        :value="modelValue"
        @input="handleInput"
        class="absolute inset-0 opacity-0 cursor-pointer"
      />
    </div>
    <div v-if="showLabels" class="slider-labels">
      <span>{{ min }}</span>
      <span>{{ max }}</span>
    </div>
  </div>
</template>

<script setup>
import { computed } from 'vue';

const props = defineProps({
  min: { type: Number, default: 0 },
  max: { type: Number, default: 100 },
  modelValue: { type: Number, default: 50 },
  color: { type: String, default: 'primary' },
  size: { type: String, default: 'md' },
  showLabels: { type: Boolean, default: false }
});

const emit = defineEmits(['update:modelValue']);

const percentage = computed(() => {
  return ((props.modelValue - props.min) / (props.max - props.min)) * 100;
});

const handleInput = (e) => {
  emit('update:modelValue', Number(e.target.value));
};
</script>

<!-- Usage -->
<Slider
  v-model="volume"
  :min="0"
  :max="100"
  color="secondary"
  :show-labels="true"
/>

API Reference

Class Names

ClassDescription
.sliderBase slider container (required)
.slider-trackBackground track element
.slider-track-filledFilled portion of track
.slider-thumbDraggable thumb control
.slider-thumb-labelValue label on thumb
.slider-primaryPrimary color (default)
.slider-secondarySecondary color variant
.slider-tertiaryTertiary color variant
.slider-smSmall size (2rem height, 1rem thumb)
.slider-mdMedium size (2.5rem height, 1.25rem thumb, default)
.slider-lgLarge size (3rem height, 1.5rem thumb)
.slider-rangeRange slider with two thumbs
.slider-marksContainer for step marks
.slider-markIndividual step mark
.slider-mark-activeActive/filled step mark
.slider-mark-labelLabel for step mark
.slider-labelsContainer for min/max labels
.slider-labels-alwaysAlways show value labels
.slider-verticalVertical orientation
.slider-disabledDisabled state

Combinations

You can combine classes for different effects:

Large Secondary Slider with Always-Visible Labels

60

Small Vertical Tertiary Slider with Marks

40
  • Input - Text and number inputs
  • Select - Dropdown selections
  • Switch - Toggle controls

See Also