Skip to content

RangeSlider ​

Select numeric values within a defined range using an intuitive slider interface. Perfect for volume controls, price filters, settings adjustments, and any numeric input. Built for Pulse Framework with full reactivity support.

Import ​

ts
import { RangeSlider } from '@odyssee-software/components';

Basic Usage ​

Code Éditable
Résultat

With Value Display ​

Code Éditable
Résultat

Custom Range ​

Define custom minimum, maximum, and step values.

Code Éditable
Résultat

Custom Value Format ​

Format the displayed value with a custom function.

Code Éditable
Résultat

With Hints ​

Code Éditable
Résultat

Validation State ​

Code Éditable
Résultat

Disabled State ​

Code Éditable
Résultat

Reactive Slider ​

Control slider value with Pulse signals.

tsx
import { RangeSlider, Button, Pulse } from '@odyssee-software/components';

const VolumeControl = () => {
  const volume = Pulse.signal(50);
  const isMuted = Pulse.signal(false);

  const displayVolume = Pulse.computed(() => {
    return isMuted() ? 0 : volume();
  });

  const toggleMute = () => {
    isMuted(!isMuted());
  };

  const increaseVolume = () => {
    const current = volume();
    if (current < 100) {
      volume(Math.min(100, current + 10));
      isMuted(false);
    }
  };

  const decreaseVolume = () => {
    const current = volume();
    if (current > 0) {
      volume(Math.max(0, current - 10));
    }
  };

  return (
    <div class='space-y-4'>
      <RangeSlider
        label='Volume Control'
        value={volume}
        min={0}
        max={100}
        showValue={true}
        valueFormat={(val) => `${val}%`}
        onChange={(val) => {
          volume(val);
          isMuted(false);
        }}
        disabled={isMuted()}
      />

      <div class='flex gap-2'>
        <Button onClick={decreaseVolume} size='sm' variant='outline'>
          Volume Down
        </Button>
        <Button onClick={toggleMute} size='sm' variant='outline'>
          {isMuted() ? 'Unmute' : 'Mute'}
        </Button>
        <Button onClick={increaseVolume} size='sm' variant='outline'>
          Volume Up
        </Button>
      </div>

      <div class='p-4 bg-gray-50 rounded-lg dark:bg-neutral-800'>
        <p class='text-sm text-gray-700 dark:text-gray-300'>
          Current Volume: {displayVolume()}%
        </p>
        <p class='text-xs text-gray-500 dark:text-gray-400'>
          Status: {isMuted() ? 'Muted' : 'Playing'}
        </p>
      </div>
    </div>
  );
};

Props ​

PropTypeDefaultDescription
valuenumber | Signal<number>50Current slider value
minnumber0Minimum value
maxnumber100Maximum value
stepnumber1Step increment
labelstring-Slider label text
hintstring-Helper text below slider
errorstring-Error message
disabledbooleanfalseDisable slider
showValuebooleanfalseDisplay current value
valueFormat(value: number) => string-Custom value formatter
onChange(value: number) => void-Value change callback
namestring-Input name attribute
classNamestring-Additional CSS classes
idstringAuto-generatedHTML id attribute

Accessibility ​

The RangeSlider component follows accessibility best practices:

  • ✅ Semantic HTML with native <input type="range">
  • ✅ Proper label associations
  • ✅ Keyboard navigation (arrow keys, home, end)
  • ✅ ARIA attributes for screen readers
  • ✅ Focus indicators
  • ✅ Disabled state properly communicated
tsx
// Accessibility features are built-in
const accessibleSlider = (
  <RangeSlider
    label='Volume'
    value={50}
    showValue={true}
    // All ARIA and keyboard navigation handled automatically
  />
);

Best Practices ​

✅ Do ​

  • Use appropriate min/max ranges for the context
  • Provide clear labels describing what the slider controls
  • Show current value for important adjustments
  • Use reasonable step increments
  • Format values meaningfully (%, $, °C, etc.)
tsx
// Good: Clear context and formatting
const goodSlider = (
  <RangeSlider
    label='Product Price Filter'
    min={0}
    max={1000}
    step={10}
    value={500}
    showValue={true}
    valueFormat={(val) => `$${val}`}
    hint='Filter products by price range'
  />
);

❌ Don't ​

  • Don't use very large ranges with small steps (poor UX)
  • Don't omit labels or value displays for important controls
  • Don't use steps that are too large for precision
  • Don't forget to validate against min/max
  • Don't hide the current value for critical settings
tsx
// Bad: Unclear purpose, no formatting
const badSlider = (
  <RangeSlider
    value={50}
    // No label
    // No value display
    // No formatting
  />
);

// Better: Clear and formatted
const betterSlider = (
  <RangeSlider
    label='Temperature'
    value={20}
    showValue={true}
    valueFormat={(val) => `${val}°C`}
  />
);

Use Cases ​

Volume Control ​

tsx
const AudioPlayer = () => {
  const volume = Pulse.signal(75);

  return (
    <div class='p-4 bg-white rounded-lg shadow dark:bg-neutral-800'>
      <RangeSlider
        label='Volume'
        value={volume}
        min={0}
        max={100}
        showValue={true}
        valueFormat={(val) => `${val}%`}
        onChange={(val) => {
          volume(val);
          // Update audio element volume
          document.querySelector('audio')?.volume = val / 100;
        }}
      />
    </div>
  );
};

Price Filter ​

tsx
const PriceFilter = () => {
  const minPrice = Pulse.signal(0);
  const maxPrice = Pulse.signal(1000);

  return (
    <div class='space-y-4'>
      <h3 class='font-semibold'>Price Range</h3>
      
      <RangeSlider
        label='Minimum Price'
        value={minPrice}
        min={0}
        max={maxPrice()}
        step={10}
        showValue={true}
        valueFormat={(val) => `$${val}`}
        onChange={(val) => minPrice(val)}
      />

      <RangeSlider
        label='Maximum Price'
        value={maxPrice}
        min={minPrice()}
        max={2000}
        step={10}
        showValue={true}
        valueFormat={(val) => `$${val}`}
        onChange={(val) => maxPrice(val)}
      />

      <p class='text-sm text-gray-600'>
        Showing items from ${minPrice()} to ${maxPrice()}
      </p>
    </div>
  );
};

Settings Panel ​

tsx
const NotificationSettings = () => {
  const frequency = Pulse.signal(3);

  const frequencyLabels = {
    1: 'Every hour',
    2: 'Every 2 hours',
    3: 'Every 4 hours',
    4: 'Every 8 hours',
    5: 'Once daily'
  };

  return (
    <Card title='Notification Settings'>
      <RangeSlider
        label='Notification Frequency'
        value={frequency}
        min={1}
        max={5}
        step={1}
        showValue={true}
        valueFormat={(val) => frequencyLabels[val] || String(val)}
        hint='How often would you like to receive notifications?'
      />
    </Card>
  );
};

Styling & Theming ​

All range slider styles use Tailwind CSS and support dark mode automatically.

Custom Styling ​

tsx
// Custom track color (via CSS)
const customSlider = (
  <div class='[&_input[type=range]]:bg-purple-200 dark:[&_input[type=range]]:bg-purple-900'>
    <RangeSlider
      label='Custom styled'
      value={50}
    />
  </div>
);

Dark Mode ​

tsx
// Dark mode support is automatic
const darkModeSlider = (
  <RangeSlider
    label='Volume'
    value={75}
    showValue={true}
    // Automatically uses dark:bg-gray-700, dark:text-gray-300, etc.
  />
);

TypeScript ​

Full TypeScript support with complete type definitions.

tsx
import type { RangeSliderProps } from '@odyssee-software/components';
import { Pulse } from '@odyssee-software/components';

// Type-safe props
const props: RangeSliderProps = {
  label: 'Volume',
  value: 50,
  min: 0,
  max: 100,
  step: 1,
  showValue: true,
  valueFormat: (val: number): string => `${val}%`,
  onChange: (value: number): void => {
    console.log(value);
  }
};

const slider = <RangeSlider {...props} />;

// Type-safe with signals
const volume = Pulse.signal<number>(75);
const typedSlider = (
  <RangeSlider
    label='Volume'
    value={volume}
    onChange={(val: number) => volume(val)}
  />
);

Version: 1.0.0
Last Updated: January 2025

Released under the MIT License.