Slider

Pronto

Controle deslizante para escolher valor numérico em range contínuo. Single ou range (2 thumbs). Suporta steps.

Usar quando

Volume, brilho, percentual, range de preço/data, qualquer numero contínuo onde preview visual ajuda.

Não usar quando

Valor preciso (use Number Input). Lista de opções (use Select). Mais de 2 thumbs (UI complexa demais).

Variantes

Volume60
Faixa de honoráriosR$ 15.000 – R$ 75.000
Prioridade3 de 5
Confiança85%

Microinterações

MicrointeraçãoDisparada porComportamentoTiming
Thumb dragmousedown + mousemoveThumb segue cursor, valor atualiza inlinefollows pointer
Thumb scale on grabmousedownThumb scale 1→1.15100ms ease
Track fillvalue mudaCor preenche da esquerda até thumb150ms ease
Tick snapdrag perto de tickSnap visual + sutil haptic feedbackinstant
Keyboard step↑↓ ou ←→Move pelo step (instant)instant
Tooltip with valuedragMostra value flutuante acima do thumb150ms fade

Acessibilidade

Acessibilidade — checklist

Teclado
TabFoco no thumb
← / ↓Diminui pelo step
→ / ↑Aumenta pelo step
PgUp / PgDnStep × 10
Home / EndMin / Max
ARIA esperado
  • role="slider"
  • aria-valuenow / valuemin / valuemax / valuetext
  • aria-label="Volume" ou aria-labelledby
  • aria-orientation="horizontal" (default)
Notas
  • Use aria-valuetext quando value precisa de unit ("15%", "R$ 250").
  • Range slider: 2 thumbs cada com role="slider" próprio.
  • Mostre valor sempre (label, tooltip ou ao lado).
  • Steps tornam keyboard previsível.

Código

'use client';
import { useState } from 'react';
import { T } from '@/lib/tokens';

export function Slider({ value, onChange, min = 0, max = 100, step = 1 }) {
  const pct = ((value - min) / (max - min)) * 100;
  return (
    <div style={{ position: 'relative', height: 24, width: '100%' }}>
      <div style={{ position: 'absolute', top: 11, left: 0, right: 0, height: 2, background: T.surface3, borderRadius: 980 }}>
        <div style={{ width: `${pct}%`, height: '100%', background: T.ink, borderRadius: 980 }} />
      </div>
      <input type="range" role="slider"
        min={min} max={max} step={step} value={value}
        onChange={(e) => onChange(Number(e.target.value))}
        aria-valuenow={value} aria-valuemin={min} aria-valuemax={max}
        style={{ position: 'absolute', inset: 0, opacity: 0 }} />
      <div style={{
        position: 'absolute', top: 4, left: `calc(${pct}% - 8px)`,
        width: 16, height: 16, borderRadius: 980, background: T.surface,
        border: `2px solid ${T.ink}`, boxShadow: T.shadowSm,
      }} />
    </div>
  );
}

Regras

Faça

  • Mostre valor visível (label ou tooltip).
  • Track 2-3px height; thumb 14-16px.
  • Cor preenchida ink à esquerda, surface3 à direita.
  • Steps com ticks visuais se discreto.
  • Keyboard ←→ ↑↓ obrigatório.

Não faça

  • Não use slider para valor preciso (use Number Input).
  • Não use mais de 2 thumbs (range OK, mais que isso vira ruído).
  • Não esconda valor (sem feedback de quanto está).
  • Não force thumb pequeno < 14px (touch difícil).
  • Não use cor brass na track (cor é sinal).

Tokens usados

TokenValorPapel
T.ink#0A0A0Aborda do thumb + fill da track
T.surface#FFFFFFbackground do thumb
T.surface3#F3F0E6track sem preenchimento
T.shadowSm0 1px 2px ...sombra do thumb
RADIUS.pill980thumb e track