componentes / carousel
Carousel
Slides com setas, indicadores, teclado, autoplay opcional e loop.
Como usar
Cada filho é um slide. É uma `region` com `aria-roledescription="carousel"`; os slides são `group`s e os fora da vista ficam ocultos para tecnologias assistivas e sem foco. ←/→/Home/End navegam. O `autoplay` pausa com o mouse e o foco e nunca roda com `prefers-reduced-motion`. `index` pode ser controlado; `slidesPerView` mostra vários slides de uma vez.
preview
Carousel.tsx
import { Carousel } from "vaiperui"; <Carousel aria-label="Slides em destaque" loop autoplay={4000}> <img src="/photos/1.jpg" alt="One" /> <img src="/photos/2.jpg" alt="Two" /> <img src="/photos/3.jpg" alt="Three" /></Carousel>Vários por vez
preview
por-vez.tsx
<Carousel aria-label="..." slidesPerView={2}>...</Carousel>Autoplay e loop
Pausa com mouse e foco; não roda com prefers-reduced-motion.
preview
autoplay.tsx
<Carousel aria-label="..." loop autoplay={3000}>...</Carousel>Controlado
preview
Slide: 1
controlado.tsx
const [index, setIndex] = useState(0); <Carousel aria-label="..." index={index} onIndexChange={setIndex}>...</Carousel>Cores
preview
cores.tsx
<Carousel variantColor="primary" ...><Carousel variantColor="success" ...><Carousel variantColor="warning" ...><Carousel variantColor="danger" ...>Cor personalizada
preview
cor-personalizada.tsx
<Carousel customColor="#db2777" ...>Props
| Prop | Valores | Padrão | Descrição |
|---|---|---|---|
| children | ReactNode | — | Um slide por filho |
| aria-label | string | — | Nome acessível do carrossel |
| index / defaultIndex | number | 0 | Primeiro slide visível controlado / inicial |
| onIndexChange | (index: number) => void | nenhum | Chamado quando a posição muda |
| slidesPerView | number | 1 | Slides visíveis ao mesmo tempo |
| loop | boolean | false | Voltar do fim ao início |
| autoplay | boolean | number | false | Avança a cada N ms (true = 5000). Pausa com mouse/foco |
| showArrows / showDots | boolean | true | Mostrar as setas / indicadores |
| previousLabel / nextLabel | string | Previous slide / Next slide | Nomes acessíveis das setas |
| dotLabel | (index) => string | Go to slide N | Nome acessível de cada indicador |
| slideLabel | (index, total) => string | N of total | Nome acessível de cada slide |
| variantColor | primary | success | warning | danger | primary | Cor de destaque (botão play, progresso, indicadores) |
| customColor | string | { light, dark } | nenhum | Qualquer cor CSS (ou um par por tema). Substitui o variantColor |
| size | sm | md | lg | md | Tamanho dos controles e do texto |
| radius | none | sm | md | lg | full | lg | Arredondamento dos cantos |
| animation | none | smooth | fast | bounce | smooth | Transição dos controles |
Acessibilidade
- A raiz é uma `<section>` com `aria-roledescription="carousel"` e o `aria-label` que você passa.
- Cada slide é um `role="group"` com `aria-roledescription="slide"` e um nome vindo de `slideLabel` (padrão `1 of 5`).
- Slides fora de vista ficam `aria-hidden` e `inert`, então seu conteúdo não é lido nem focável.
- As setas são botões nomeados por `previousLabel` / `nextLabel` (desabilitados nas pontas, exceto com `loop`), e os pontos são botões nomeados por `dotLabel` com `aria-current="true"` no atual.
- O autoplay pausa enquanto o ponteiro está sobre o carrossel ou o foco está dentro dele, e não roda sob `prefers-reduced-motion: reduce`; a transição entre slides também é removida.
- A área dos slides é `aria-live="off"` enquanto o autoplay roda e `polite` caso contrário, então trocas manuais são anunciadas.
Teclado
| Tecla | Ação |
|---|---|
| ArrowLeft / ArrowRight | Slide anterior / próximo (dá a volta com `loop`) |
| Home / End | Primeiro / último slide |
| Tab | Percorre setas, pontos e conteúdo focável dos slides visíveis |
Tenha em mente
- O autoplay não tem botão de pausa; hover ou foco o pausam. Prefira sem autoplay, ou um intervalo longo, para conteúdo que precisa ser lido.
- O texto alternativo das imagens e os rótulos (`aria-label`, `slideLabel`, `dotLabel`, setas) no idioma da página são responsabilidade do seu app.