Ir al contenido
Formularios

Slider

Elegir un número —o un rango— arrastrando, con el slider de Photos de iCloud: pista de 2 px y perilla de 14. Marcas opcionales y valor visible.

import { Slider } from "sebs7n-ui/slider"

Ejemplos

Un valor, con el número a la vista

El `label` visible queda asociado al thumb por Base UI: no hace falta `aria-label`. Se mueve con las flechas, de a `largeStep` con Shift, y a los extremos con Inicio y Fin.

import { Slider } from "sebs7n-ui/slider"
import { useState } from "react"

function Basico() {
  const [opacidad, setOpacidad] = useState(60)
  return (
    <div className="flex w-full max-w-sm flex-col gap-6">
      <Slider label="Opacidad de la marca de agua" onValueChange={setOpacidad} showValue value={opacidad} />
      <div
        className="h-16 rounded-control bg-label"
        style={{ opacity: opacidad / 100 }}
      />
    </div>
  )
}

Rango, con marcas

`defaultValue={[min, max]}` pinta los dos thumbs solo. `minStepsBetweenValues` evita que terminen encimados, y las marcas son referencias visuales (`aria-hidden`), no topes.

import { Slider } from "sebs7n-ui/slider"
import { useState } from "react"

function Rango() {
  const [precio, setPrecio] = useState<readonly number[]>([200_000, 700_000])
  const formato = new Intl.NumberFormat("es-AR", { style: "currency", currency: "ARS", maximumFractionDigits: 0 })
  return (
    <div className="flex w-full max-w-sm flex-col gap-2">
      <Slider
        label="Presupuesto"
        marks={[0, 250_000, 500_000, 750_000, 1_000_000]}
        max={1_000_000}
        min={0}
        minStepsBetweenValues={1}
        onValueChange={setPrecio}
        step={25_000}
        value={precio}
      />
      <p className="text-callout text-label-secondary">
        De {formato.format(precio[0])} a {formato.format(precio[1])}
      </p>
    </div>
  )
}

Tamaños, formato y deshabilitado

`sm` (32px de área arrastrable) para un panel denso; `md` (40px) suelto. `format` es el de `Intl.NumberFormat`: cambia lo que muestra `showValue` y también el `aria-valuetext` que lee el lector de pantalla.

import { Slider } from "sebs7n-ui/slider"

function TamanosYFormato() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-6">
      <Slider
        defaultValue={120_000}
        format={{ style: "currency", currency: "ARS", maximumFractionDigits: 0 }}
        label="Tope de gasto mensual"
        locale="es-AR"
        max={500_000}
        showValue
        size="sm"
        step={10_000}
      />
      <Slider defaultValue={8} label="Cuotas" marks={[1, 3, 6, 12]} max={12} min={1} showValue size="md" />
      <Slider defaultValue={50} disabled label="Solo lectura" showValue />
    </div>
  )
}

Props

Generadas del TypeScript del paquete. Las propias del componente, más las heredadas del primitivo que tienen algo que explicar —marcadas «heredada de Base UI»—. El resto está en la línea «hereda de».

Slider

Hereda las props de Slider.Root.

PropTipoPor defectoDescripción
aria-labelstring—Nombre accesible del elemento.
controlClassNamestring—Clases del área arrastrable, por si hay que cambiarle el ancho.
defaultValueValue—El valor inicial. La forma que le des acá decide si el slider es simple (40) o de rango ([20, 60]).
labelReact.ReactNode—Etiqueta visible. Base UI la asocia sola con los thumbs; sin ella hace falta aria-label.
marksreadonly number[]—Valores donde va un punto de referencia. Siguen a min y max, no al 0–100 fijo.
maxnumber100El extremo de la derecha. Por defecto, 100.
minnumber0El extremo de la izquierda. Por defecto, 0.
showValuebooleanfalseMuestra el valor (o "20 – 60" en un rango) a la derecha de la etiqueta.
size"sm" | "md""md"sm 24px · md 32px de área arrastrable. La pista (2 px) y la perilla (14) son las mismas en los dos.
valueValue—Un número, o un array de dos para un rango. Pasarlo lo vuelve controlado.
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.
disabledboolean—Heredada de Base UI. Apaga la interacción y lo marca con data-disabled, que es el atributo del que cuelgan los estilos de apagado.
namestring—Heredada de Base UI. El nombre con el que el valor viaja en el submit y en el objeto errors de Form.
onValueChange((value: Value extends number ? number : Value, eventDetails: SliderRoot.ChangeEventDetails) => void)—Heredada de Base UI. Se llama con el valor nuevo cada vez que cambia.
orientation"horizontal" | "vertical"—Heredada de Base UI. horizontal (default) o vertical. Define qué flechas mueven el foco.

Teclado

← ↓
Baja un step.
→ ↑
Sube un step.
Shift + flecha
Se mueve un largeStep (10 por defecto).
Re Pág · Av Pág
Igual que Shift + flecha.
Inicio · Fin
Va al mínimo y al máximo.
Tab
Entra y sale. En un rango, cada thumb es su propia parada.

Accesibilidad

  • Cada thumb es un <input type="range"> de verdad: el teclado y los lectores de pantalla lo tratan como el control nativo.
  • El label visible queda asociado a los thumbs por Base UI. Sin label, el aria-label que pases viaja al input, no solo al grupo.
  • En un rango, aria-valuetext distingue el thumb de inicio del de fin.
  • El anillo de foco va en el thumb, por fuera (como el del Switch: adentro de una perilla de 14 un anillo interior la taparía), porque el foco real vive en el input de adentro.
  • La perilla es lo que se agarra: su borde de 2 px en el label llega a 3:1 contra la página en los dos temas. El área de toque la agranda un ::after (34 px, 46 con el dedo).
  • Las marcas son aria-hidden: el valor lo canta el thumb, no un punto.

Reglas de uso

  • Si el número exacto importa, es un `Input`. El slider es para proporciones: volumen, opacidad, un presupuesto «de tanto a tanto».
  • Con showValue el número se lee mientras se arrastra; sin él, el valor solo existe para el lector de pantalla.
  • marks son referencias, no topes: el valor sigue siendo continuo salvo que subas el step.
  • En un rango, minStepsBetweenValues evita que los dos thumbs terminen encimados.
  • onValueCommitted para lo caro (pegarle a la API): onValueChange dispara en cada píxel del arrastre.

Relacionados