Carousel
Diapositivas nativas con scroll-snap, con inercia en pantallas táctiles, arrastre con el ratón, teclas de flecha, puntos, miniaturas y una reproducción automática que se pausa cuando debe.
pnpm dlx shadcn@latest add https://hextaui.com/r/carousel.jsonAñade el componente, los tokens del tema de HextaUI y los componentes de HextaUI de los que depende.
Añade los tokens del tema a tu CSS global, si aún no lo has hecho.
Instala las dependencias.
pnpm add @base-ui/react @tabler/icons-react class-variance-authority cnCopia y pega el siguiente código en tu proyecto.
components/ui/carousel.tsx components/ui/button.tsx components/ui/number-flow.tsx lib/motion.ts Actualiza las rutas de importación para que coincidan con la configuración de tu proyecto.
Las diapositivas se desplazan de forma nativa con CSS scroll snap, así que el impulso táctil y el desplazamiento del trackpad se sienten como los de la plataforma. Un ratón puede arrastrar, y las teclas de flecha mueven una diapositiva cada vez.
API
Pasa setApi para obtener la API del carousel y escucha select para mostrar tu propia posición. Next se deshabilita en la última diapositiva pero conserva el foco.
Varias por vista
Los elementos definen su propio basis. Los márgenes vienen de la prop spacing, así que se mantienen exactos con cualquier basis.
Puntos y contador
El punto activo se estira a medida que se desplazan las diapositivas y sus vecinos le hacen sitio. El contador solo gira el dígito que cambió.
Autoplay
autoplay está desactivado por defecto y sigue desactivado con movimiento reducido. Se pausa al pasar el cursor, con el foco del teclado, al tocar, al arrastrar, con una pestaña oculta o cuando sale de la vista, y el punto activo se llena mientras corre el temporizador.
Miniaturas
<CarouselThumbnails /> sigue al carousel principal y se desplaza para mantener visible la miniatura activa.
Vertical
orientation="vertical" necesita una altura en <CarouselContent />. Los botones pasan arriba y abajo.
Controlado
Pasa index y onIndexChange. Deslizar actualiza tu estado y tu estado desplaza el carousel.
Rebobinado e índice inicial
rewind envía Next en la última diapositiva de vuelta a la primera. defaultIndex abre en una diapositiva sin animación de desplazamiento.
Enlaces y contenido enfocable
Arrastrar un enlace con el ratón desplaza sin abrirlo. Tabular hasta una diapositiva fuera de pantalla la desplaza hasta hacerla visible.
Añadir y quitar diapositivas
Los puntos, el contador y los botones se actualizan a medida que las diapositivas entran y salen.
Anidado
Las teclas de flecha, el arrastre y los puntos solo mueven el carousel en el que estás.
Contenido largo y una sola diapositiva
El texto sin espacios se ajusta dentro de su diapositiva. Con una sola diapositiva los puntos se ocultan y los botones permanecen deshabilitados.
De derecha a izquierda
Las diapositivas empiezan a la derecha, las flechas se invierten, la tecla de flecha izquierda avanza y los puntos se llenan desde la derecha.
Las teclas funcionan mientras el foco esté en cualquier punto del carousel, excepto en campos de texto y carousels anidados.
| Key | Acción |
|---|---|
| → | Diapositiva siguiente. Anterior en diseños de derecha a izquierda. ↓ en carousels verticales. |
| ← | Diapositiva anterior. Siguiente en diseños de derecha a izquierda. ↑ en carousels verticales. |
| Tab | Recorre los botones, el punto activo y el contenido de las diapositivas, desplazando a la vista las diapositivas fuera de pantalla. |
| EnterSpace | Activa el botón, punto o miniatura con foco. |
- La raíz es una
regiondescrita como carousel. Dale unaria-label. - Cada elemento es un
groupdescrito como diapositiva y etiquetado con su posición, como “3 of 5”. - Una región activa polite anuncia la nueva diapositiva tras navegar con el teclado o los botones, y permanece en silencio mientras funciona el autoplay.
- Los puntos y las miniaturas usan una sola parada del tabulador, y el foco sigue al activo.
- Previous y Next siguen pudiendo recibir foco cuando están deshabilitados, así que el foco nunca se pierde en ninguno de los extremos.
| Prop | Tipo | Predeterminado |
|---|---|---|
orientation | "horizontal" | "vertical" | "horizontal" |
spacingSeparación entre diapositivas. | "none" | "sm" | "default" | "lg" | "default" |
index | number | – |
defaultIndex | number | 0 |
onIndexChange | (index: number) => void | – |
rewindVuelve de la última diapositiva a la primera. | boolean | false |
mouseDragPermite que un ratón arrastre las diapositivas. | boolean | true |
autoplayAvanza con un temporizador. delay vale 5000 ms por defecto, con un mínimo de 1000 ms. | boolean | { delay?: number } | false |
setApi | (api: CarouselApi) => void | – |
render | ReactElement | (props, state) => ReactElement | <div> |
| Atributo | Descripción |
|---|---|
data-slot="carousel" | Apunta a la raíz en CSS. |
data-orientation | La orientación. |
--carousel-spacing | La separación entre diapositivas, definida por spacing. |
| Prop | Tipo | Predeterminado |
|---|---|---|
classNameSe aplica a la pista que contiene las diapositivas. | string | – |
viewportClassNameSe aplica al viewport de desplazamiento. | string | – |
| Atributo | Descripción |
|---|---|
data-slot="carousel-content" | El viewport de desplazamiento. |
data-slot="carousel-container" | La pista que contiene. |
data-scrollable | Presente cuando hay más de una posición. |
data-dragging | Presente mientras hay un arrastre con el ratón en curso. |
| Prop | Tipo | Predeterminado |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <div> |
| Atributo | Descripción |
|---|---|
data-slot="carousel-item" | Define basis-* para mostrar varias por vista. |
Ambos renderizan un <Button /> y aceptan sus props. Van fuera del contenido, así que deja espacio alrededor del carousel.
| Prop | Tipo | Predeterminado |
|---|---|---|
variant | ButtonProps["variant"] | "outline" |
size | ButtonProps["size"] | "icon-sm" |
childrenSigue la orientación y la dirección. | ReactNode | Arrow icon |
| Atributo | Descripción |
|---|---|
data-slot="carousel-previous" | Etiquetado como “Previous slide”. |
data-slot="carousel-next" | Etiquetado como “Next slide”. |
data-disabled | Presente en cualquiera de los extremos. El botón sigue pudiendo recibir foco. |
| Prop | Tipo | Predeterminado |
|---|---|---|
aria-label | string | "Choose slide" |
| Atributo | Descripción |
|---|---|
data-slot="carousel-dots" | El grupo de puntos. Oculto con una sola posición. |
data-slot="carousel-dot" | Cada punto. El activo tiene aria-current. |
--dot-active | De 0 a 1, qué tan activo está un punto al desplazarse. |
| Atributo | Descripción |
|---|---|
data-slot="carousel-counter" | Muestra la posición actual sobre el total con un dígito que rueda. |
| Prop | Tipo | Predeterminado |
|---|---|---|
variant | ButtonProps["variant"] | "ghost" |
size | ButtonProps["size"] | "icon-sm" |
| Atributo | Descripción |
|---|---|
data-slot="carousel-autoplay-toggle" | Etiquetado como “Pause slideshow” o “Play slideshow”. |
| Prop | Tipo | Predeterminado |
|---|---|---|
aria-label | string | "Slides" |
| Atributo | Descripción |
|---|---|
data-slot="carousel-thumbnails" | La tira de desplazamiento. |
| Prop | Tipo | Predeterminado |
|---|---|---|
indexLa diapositiva que abre. Por defecto es su posición en la tira. | number | – |
render | ReactElement | (props, state) => ReactElement | <button> |
| Atributo | Descripción |
|---|---|
data-slot="carousel-thumbnail" | Selecciona las miniaturas en CSS. |
data-active | Presente mientras su diapositiva está a la vista. |
Se devuelve mediante setApi y useCarousel(). Pasa jump: true para moverte sin animación.
| Prop | Tipo | Predeterminado |
|---|---|---|
scrollPrev | (jump?: boolean) => void | – |
scrollNext | (jump?: boolean) => void | – |
scrollToSe desplaza a una posición de ajuste. | (index: number, jump?: boolean) => void | – |
scrollToSlideSe desplaza a la posición que muestra una diapositiva. | (slideIndex: number, jump?: boolean) => void | – |
canScrollPrev | () => boolean | – |
canScrollNext | () => boolean | – |
selectedScrollSnap | () => number | – |
scrollSnapList | () => number[] | – |
slidesInView | () => number[] | – |
slideNodes | () => HTMLElement[] | – |
viewportNode | () => HTMLElement | null | – |
play | () => void | – |
stop | () => void | – |
isPlaying | () => boolean | – |
on / off | (event: "select" | "scroll" | "settle" | "reInit", listener) => CarouselApi | – |
Úsalo dentro de <Carousel /> para construir tus propios controles. Devuelve api, orientation, selectedIndex, snapCount, slideCount, slidesInView, canScrollPrev, canScrollNext, isPlaying y los métodos de desplazamiento y reproducción.
- ButtonBotones en todas las variantes y tamaños, con un flujo integrado de carga, éxito y error que omite el spinner en las peticiones rápidas.
- Number flowNúmeros animados donde solo giran los dígitos que cambian, con cualquier formato Intl y configuración regional.
- AccordionEncabezados apilados que revelan cada uno un panel, con un movimiento de altura que puedes invertir a mitad de camino y paneles que siguen siendo buscables mientras están cerrados.
- Aspect ratioUn contenedor que mantiene su forma antes de que cargue el contenido multimedia, muestra un brillo mientras carga, hace aparecer el contenido con un fundido y muestra una alternativa si falla.
- CollapsibleUn panel que se muestra y se oculta con un movimiento de altura que puedes invertir a mitad de camino, sin saltos en el diseño.
- ResizablePaneles que puedes separar arrastrando, con un divisor discreto que despierta al pasar el cursor, tamaños que se deslizan al restablecer o colapsar, y diseños que persisten.