Lumenv0.2

Components/Forms

Slider

A value in a range. A hairline track, a filled segment, a single dot.

Default

With value display

60

Props

PropTypeDefaultDescription
minnumber0Lower bound
maxnumber100Upper bound
stepnumber1Increment
valuenumberCurrent value
onChange(value: number) => voidChange handler

Installation

Paste the source into components/slider.tsx. No dependencies required.

'use client';

interface SliderProps {
  min?: number;
  max?: number;
  step?: number;
  value: number;
  onChange?: (value: number) => void;
}

export function Slider({ min = 0, max = 100, step = 1, value, onChange }: SliderProps) {
  const pct = max === min ? 0 : ((value - min) / (max - min)) * 100;

  return (
    <div style={{ position: 'relative', width: '100%', height: '16px', display: 'flex', alignItems: 'center' }}>
      <div
        style={{
          position: 'absolute',
          left: 0,
          right: 0,
          height: '1px',
          borderTop: '0.5px solid var(--color-border-soft)',
        }}
      />
      <div
        style={{
          position: 'absolute',
          left: 0,
          width: pct + '%',
          height: '1px',
          borderTop: '0.5px solid var(--color-text)',
        }}
      />
      <span
        style={{
          position: 'absolute',
          left: pct + '%',
          transform: 'translateX(-50%)',
          width: '12px',
          height: '12px',
          borderRadius: '50%',
          backgroundColor: 'var(--color-text)',
          pointerEvents: 'none',
        }}
      />
      <input
        type="range"
        min={min}
        max={max}
        step={step}
        value={value}
        onChange={(e) => onChange?.(Number(e.target.value))}
        style={{
          position: 'absolute',
          left: 0,
          width: '100%',
          margin: 0,
          opacity: 0,
          cursor: 'pointer',
          height: '16px',
        }}
      />
    </div>
  );
}

Built from Lumen tokens. Edit the tokens