Carousel
Carrossel deslizante construído sobre Embla com navegação por teclado
Carousel é um wrapper sobre embla-carousel-react que traz navegação por teclado, botões de passo e acessibilidade pronta. Use para galerias, onboarding em páginas internas e recomendações horizontais. Evite usá-lo como container principal de uma landing — usuários frequentemente ignoram slides além do primeiro.
DesignTokens, primitivas e padrões visuais.
EngenhariaComponentes tipados com Motion e Radix.
AcessibilidadeTestado com jest-axe e addon-a11y.
DocumentaçãoExemplos vivos direto do código-fonte.
'use client';
import {
Card,
CardContent,
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from '@uranus-workspace/design-system';
const slides = [
{ title: 'Design', description: 'Tokens, primitivas e padrões visuais.' },
{ title: 'Engenharia', description: 'Componentes tipados com Motion e Radix.' },
{ title: 'Acessibilidade', description: 'Testado com jest-axe e addon-a11y.' },
{ title: 'Documentação', description: 'Exemplos vivos direto do código-fonte.' },
];
export default function CarouselDefault() {
return (
<Carousel className="w-full max-w-xs">
<CarouselContent>
{slides.map((slide) => (
<CarouselItem key={slide.title}>
<Card>
<CardContent className="flex aspect-square flex-col items-center justify-center gap-2 p-6 text-center">
<span className="text-lg font-medium">{slide.title}</span>
<span className="text-sm text-muted-foreground">{slide.description}</span>
</CardContent>
</Card>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
);
}
Anatomia
- Carousel — container raiz. Aceita
orientation="horizontal"(padrão) ou"vertical", além deopts(passados para o Embla) eplugins. - CarouselContent — wrapper flex dos slides. Deve conter apenas
CarouselItem. - CarouselItem — slide individual. Por padrão, ocupa
basis-full; usebasis-1/2,basis-1/3para mostrar múltiplos por vez. - CarouselPrevious / CarouselNext — botões flutuantes com ícone de seta. Automaticamente ficam desabilitados quando não há mais slides.
Uso
'use client';
import {
Card,
CardContent,
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from '@uranus-workspace/design-system';
<Carousel className="w-full max-w-xs">
<CarouselContent>
{items.map((item) => (
<CarouselItem key={item.id}>
<Card>
<CardContent>{item.title}</CardContent>
</Card>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>Múltiplos slides visíveis
<CarouselItem className="md:basis-1/2 lg:basis-1/3">
{/* ... */}
</CarouselItem>Acessar a API do Embla
const [api, setApi] = useState<CarouselApi>();
<Carousel setApi={setApi}>
{/* ... */}
</Carousel>
// api.scrollTo(index), api.scrollNext(), api.on('select', ...)Faça
- Mostre claramente que há mais slides — mantenha
CarouselPrevious/CarouselNextvisíveis, ou use indicadores. - Pause loops automáticos quando o usuário passa o mouse ou dá foco em um slide.
- Use
basis-*para exibir parcialmente o próximo slide — o "espia" convida o usuário a arrastar.
Não faça
- Não use carrossel para conteúdo crítico da página. Se o usuário precisa ler tudo, use uma lista.
- Não esconda os botões de navegação em desktop — eles são o principal affordance de teclado e mouse.
- Não coloque carrossel dentro de carrossel. O foco e a rolagem ficam ambíguos.
Acessibilidade
- O container emite
role="region"comaria-roledescription="carousel", e cadaCarouselItemexpõerole="group"comaria-roledescription="slide". - Teclado:
←e→navegam entre slides quando o carrossel ou seus filhos estão focados. - Os botões
CarouselPrevious/CarouselNexttêm textosr-only("Previous slide" / "Next slide") — sobrescreva comaria-labelpara localizar em pt-BR. - Respeita
prefers-reduced-motionvia animação do Embla.