Carousel
A touch-friendly carousel powered by Embla Carousel, featuring smooth physics-based swipe inertia, suitable for image galleries, product showcases, feature introductions, and more.
Demo
🔥 新粗野主义
大胆边框 + 硬朗阴影
💡 简洁至上
去掉多余的装饰
⚡ 高对比度
清晰的视觉层次感
程序化控制
1 / 3
🔥 新粗野主义
2 / 3
💡 简洁至上
3 / 3
⚡ 高对比度
提示:本组件尊重系统的「减少动态效果」设置。启用后将停止自动播放并禁用幻灯片切换的过渡动画。
Installation
pnpm dlx brutx-vue@latest add carouselUsage
<script setup>
import { Carousel, CarouselItem } from 'brutx-ui-vue/carousel'
</script>
<template>
<Carousel :loop="true" :autoplay="true" :autoplay-delay="3000" size="md">
<CarouselItem>
<div class="w-full h-full flex items-center justify-center bg-brutal-primary border-3 border-brutal">
Slide 1
</div>
</CarouselItem>
<CarouselItem>
<div class="w-full h-full flex items-center justify-center bg-brutal-secondary border-3 border-brutal">
Slide 2
</div>
</CarouselItem>
</Carousel>
</template>Sizes
| Size | Description |
|---|---|
sm | Small size |
md | Medium size |
lg | Large size |
full | Full-screen height |
default | Default size |
Sub-components
| Component | Description |
|---|---|
Carousel | Carousel container, manages scroll logic and navigation controls |
CarouselItem | Single slide container |
Composables
The internal logic of the Carousel component has been extracted into a standalone useCarousel composable, which can be used independently to build fully custom carousel UIs. It wraps embla-carousel-vue with built-in autoplay, loop control, selected index tracking, and automatically respects the prefers-reduced-motion system preference (stops autoplay and disables transition animations when reduced motion is enabled).
import { useCarousel } from 'brutx-ui-vue/useCarousel'
import type { UseCarouselOptions } from 'brutx-ui-vue/useCarousel'
const options: UseCarouselOptions = {
loop: true,
autoplay: true,
autoplayDelay: 3000,
}
const {
emblaRef, // Ref to bind to the carousel container
selectedIndex, // Currently selected slide index
scrollSnaps, // All scroll snap points
canScrollPrev, // Whether scrolling backward is possible
canScrollNext, // Whether scrolling forward is possible
scrollPrev, // Scroll to the previous slide
scrollNext, // Scroll to the next slide
scrollTo, // Scroll to a specific index
startAutoplay, // Start autoplay
stopAutoplay, // Stop autoplay
} = useCarousel(options)Options
| Prop | Type | Default | Description |
|---|---|---|---|
loop | MaybeRefOrGetter<boolean | undefined> | false | Whether to enable loop scrolling |
autoplay | MaybeRefOrGetter<boolean | undefined> | false | Whether to enable autoplay |
autoplayDelay | MaybeRefOrGetter<number | undefined> | 3000 | Autoplay interval in milliseconds |
Return Values
| Prop | Type | Description |
|---|---|---|
emblaRef | Ref<HTMLElement | undefined> | Ref to bind to the carousel container (pass to the container element in <template>) |
selectedIndex | Ref<number> | Currently selected slide index |
scrollSnaps | Ref<number[]> | All scrollable snap positions |
canScrollPrev | ComputedRef<boolean> | Whether scrolling backward is possible (in non-loop mode, false when at the first slide) |
canScrollNext | ComputedRef<boolean> | Whether scrolling forward is possible (in non-loop mode, false when at the last slide) |
scrollPrev() | () => void | Scroll to the previous slide |
scrollNext() | () => void | Scroll to the next slide |
scrollTo(index) | (index: number) => void | Scroll to the specified index |
startAutoplay() | () => void | Start autoplay (controlled by autoplay option and reduced motion preference) |
stopAutoplay() | () => void | Stop autoplay |
Note:
useCarouselregisters Embla events internally inonMountedand cleans up listeners and timers inonUnmounted. It must be called at the top level ofsetup.
Programmatic Control
Carousel exposes a set of methods and state via defineExpose, allowing you to actively control the carousel's scrolling and read its current state from the parent component through a component ref, without needing to take over the Embla instance yourself.
<script setup>
import { ref } from 'vue'
import { Carousel, CarouselItem } from 'brutx-ui-vue/carousel'
const carouselRef = ref()
</script>
<template>
<Carousel ref="carouselRef" :loop="true">
<CarouselItem v-for="i in 5" :key="i">
<div class="w-full h-full flex items-center justify-center">Slide {{ i }}</div>
</CarouselItem>
</Carousel>
<div class="flex gap-2 mt-4">
<button @click="carouselRef?.scrollPrev()">Previous</button>
<button @click="carouselRef?.scrollNext()">Next</button>
<button @click="carouselRef?.scrollTo(0)">Go to first</button>
</div>
</template>API Reference
Carousel
| Name | Type | Default | Description |
|---|---|---|---|
loop | boolean | undefined | false | Enables looping from the last slide to the first; previous and next navigation remain available. |
DetailsDeclared typeboolean | undefined Literal values
Default value details
Sourcepackages/ui/src/components/carousel/types.ts | |||
autoplay | boolean | undefined | false | Enables automatic slide playback; autoplay stops while reduced motion is preferred. |
DetailsDeclared typeboolean | undefined Literal values
Default value details
Sourcepackages/ui/src/components/carousel/types.ts | |||
autoplayDelay | number | undefined | 3000 | Sets the autoplay interval between slides, in milliseconds. |
DetailsDeclared typenumber | undefined Default value details
Sourcepackages/ui/src/components/carousel/types.ts | |||
showArrows | boolean | undefined | true | Controls whether previous and next navigation buttons are shown. |
DetailsDeclared typeboolean | undefined Literal values
Default value details
Sourcepackages/ui/src/components/carousel/types.ts | |||
showDots | boolean | undefined | true | Controls whether bottom dot navigation is shown; dots are hidden when thumbnails are displayed. |
DetailsDeclared typeboolean | undefined Literal values
Default value details
Sourcepackages/ui/src/components/carousel/types.ts | |||
size | "default" | "lg" | "sm" | "md" | "full" | undefined | "default" | Sets the height preset for the carousel viewport. |
DetailsDeclared type"default" | "lg" | "sm" | "md" | "full" | undefined Literal values
Default value details
Sourcepackages/ui/src/components/carousel/types.ts | |||
thumbnails | CarouselThumbnails | undefined | { "gap": 8, "highlightCurrent": true, "position": "bottom", "show": false, "size": "sm" }Factory evaluated per instance | Configures thumbnail navigation visibility, position, size, spacing, and current-item highlighting. |
DetailsDeclared typeCarouselThumbnails | undefined Referenced types CarouselThumbnails Type definition — CarouselThumbnailsexport interface CarouselThumbnails {
show?: boolean
position?: 'bottom' | 'left' | 'right'
size?: 'sm' | 'md' | 'lg'
gap?: number
highlightCurrent?: boolean
}Default value details
Sourcepackages/ui/src/components/carousel/types.ts | |||
autoplayIndicator | AutoplayIndicator | undefined | undefined | Configures the autoplay indicator type, position, and pause-on-hover behavior. |
DetailsDeclared typeAutoplayIndicator | undefined Referenced types AutoplayIndicator Type definition — AutoplayIndicatorexport interface AutoplayIndicator {
type: 'progress' | 'dots' | 'fraction'
position?: 'top' | 'bottom'
pauseOnHover?: boolean
}Default value details
Sourcepackages/ui/src/components/carousel/types.ts | |||
parallax | ParallaxEffect | undefined | undefined | Configures the slide parallax effect, including scale, opacity, and animation duration. |
DetailsDeclared typeParallaxEffect | undefined Referenced types ParallaxEffect Type definition — ParallaxEffectexport interface ParallaxEffect {
enabled?: boolean
scale?: number
opacity?: boolean
duration?: number
easing?: string
}Default value details
Sourcepackages/ui/src/components/carousel/types.ts | |||
class | string | undefined | undefined | CSS classes added to the carousel root element. |
DetailsDeclared typestring | undefined Default value details
Sourcepackages/ui/src/components/carousel/types.ts | |||
| Name | Signature or type | Description |
|---|---|---|
#default | {} | Contains CarouselItem slide components. |
DetailsDeclared type{}Sourcepackages/ui/src/components/carousel/Carousel.vue | ||
#thumbnail | { index: number; scrollTo: (index: number) => void; } | Customizes thumbnail content; the slot scope provides the thumbnail index and a scrollTo function for navigating to a slide. |
DetailsDeclared type{ index: number; scrollTo: (index: number) => void; }Sourcepackages/ui/src/components/carousel/Carousel.vue | ||
| Name | Signature or type | Description |
|---|---|---|
scrollPrev | () => void | Scrolls to the previous slide; in loop mode it wraps from the first slide to the last. |
DetailsDeclared type() => void Sourcepackages/ui/src/components/carousel/Carousel.vue | ||
scrollNext | () => void | Scrolls to the next slide; in loop mode it wraps from the last slide to the first. |
DetailsDeclared type() => void Sourcepackages/ui/src/components/carousel/Carousel.vue | ||
scrollTo | (index: number) => void | Scrolls to the slide at the specified zero-based index. |
DetailsDeclared type(index: number) => void Sourcepackages/ui/src/components/carousel/Carousel.vue | ||
selectedIndex | number | The zero-based index of the selected slide as read-only reactive state. |
DetailsDeclared typenumber Sourcepackages/ui/src/components/carousel/Carousel.vue | ||
canScrollPrev | boolean | Indicates whether scrolling backward is possible; it is always true in loop mode. |
DetailsDeclared typeboolean Literal values
Sourcepackages/ui/src/components/carousel/Carousel.vue | ||
canScrollNext | boolean | Indicates whether scrolling forward is possible; it is always true in loop mode. |
DetailsDeclared typeboolean Literal values
Sourcepackages/ui/src/components/carousel/Carousel.vue | ||
startAutoplay | () => void | Starts autoplay; a reduced-motion preference still prevents playback. |
DetailsDeclared type() => void Sourcepackages/ui/src/components/carousel/Carousel.vue | ||
stopAutoplay | () => void | Stops autoplay. |
DetailsDeclared type() => void Sourcepackages/ui/src/components/carousel/Carousel.vue | ||
CarouselItem
| Name | Type | Default | Description |
|---|---|---|---|
class | string | undefined | undefined | CSS classes added to the individual slide container. |
DetailsDeclared typestring | undefined Default value details
Sourcepackages/ui/src/components/carousel/CarouselItem.vue | |||
ariaHidden | boolean | undefined | undefined | Marks the slide and its descendants as hidden from assistive technology; set it to true for slides outside the viewport. |
DetailsDeclared typeboolean | undefined Literal values
Default value details
Sourcepackages/ui/src/components/carousel/CarouselItem.vue | |||
| Name | Signature or type | Description |
|---|---|---|
#default | {} | Custom content displayed inside the slide. |
DetailsDeclared type{}Sourcepackages/ui/src/components/carousel/CarouselItem.vue | ||
Carousel Enhanced Features
Carousel includes thumbnail navigation, autoplay indicators (progress bar / dots / fraction), and parallax animation effects, suitable for image galleries, product showcases, and other scenarios requiring richer interaction.
Usage
<script setup>
import { Carousel, CarouselItem } from 'brutx-ui-vue/carousel'
</script>
<template>
<Carousel
:loop="true"
:autoplay="true"
:autoplay-delay="3000"
size="md"
:thumbnails="{ show: true, position: 'bottom', size: 'sm', gap: 8, highlightCurrent: true }"
:autoplay-indicator="{ type: 'progress', position: 'top', pauseOnHover: true }"
:parallax="{ enabled: true, scale: 1.05, opacity: true, duration: 400 }"
>
<CarouselItem v-for="slide in slides" :key="slide.label">
<div class="w-full h-full flex items-center justify-center">{{ slide.label }}</div>
</CarouselItem>
</Carousel>
</template>CarouselThumbnails
| Prop | Type | Default | Description |
|---|---|---|---|
show | boolean | false | Whether to show thumbnails |
position | 'bottom' | 'left' | 'right' | 'bottom' | Thumbnail position |
size | 'sm' | 'md' | 'lg' | 'sm' | Thumbnail size |
gap | number | 8 | Thumbnail gap in pixels |
highlightCurrent | boolean | true | Whether to highlight the current thumbnail |
AutoplayIndicator
| Prop | Type | Default | Description |
|---|---|---|---|
type | 'progress' | 'dots' | 'fraction' | —(required) | Indicator type: progress bar / dots / fraction |
position | 'top' | 'bottom' | — | Indicator position |
pauseOnHover | boolean | — | Pause autoplay on mouse hover |
ParallaxEffect
| Prop | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Whether to enable parallax animation |
scale | number | 1.1 | Slide scale ratio |
opacity | boolean | false | Whether to enable opacity transition |
duration | number | 300 | Animation duration in milliseconds |
easing | string | 'ease-out' | CSS easing function |
Accessibility
- ARIA attributes: Left/right arrow buttons have
aria-label; dot navigation buttons havearia-label - Focus management: Keyboard focus management is natively supported by Embla
- Motion degradation: The component respects the
prefers-reduced-motionsystem setting. When the user enables "reduce motion" (monitored by the internaluseCarouselcomposable viauseReducedMotion):- Stops autoplay:
autoplayno longer triggers, and existing timers are cleared - Disables transition animations: Via
emblaApi.reInit({ duration: 0 }), slide transitions jump directly without swipe inertia - Real-time response: Changes take effect immediately without remounting the component; restoring default settings restores transition duration and resumes playback when
autoplayis enabled
- Stops autoplay:
The hover-to-pause / leave-to-resume interaction logic is preserved in motion degradation mode, but since autoplay is already stopped, the hover behavior has no additional side effects.
Enhanced Feature Notes
- Thumbnail buttons: Each thumbnail is a
<button type="button">witharia-label(viat('carousel.goToSlide', { index })internationalization); the current thumbnail is identified by visual highlight - Navigation deduplication: When thumbnails are shown, the default bottom dot navigation is automatically hidden (
showDotsandthumbnails.showare mutually exclusive) to avoid duplicate navigation - Autoplay indicators: Progress bar / fraction / dots are decorative visual elements (
<div>), non-interactive and do not steal focus - Autoplay pause: Keyboard focus within the carousel pauses autoplay and resumes it after focus leaves only if it was running before and has not been manually stopped. Pointer hover follows
autoplayIndicator.pauseOnHover; it defaults to pausing when no autoplay indicator is configured. When hover pause is enabled, autoplay stays paused while either focus or hover remains inside - Parallax animation: Driven by CSS variables, inherits
useCarousel'sprefers-reduced-motionhandling — parallax scale and opacity transitions are also disabled when reduced motion is enabled
FAQ
Q: Why doesn't the arrow button state change after enabling loop?
A: When loop is set to true, canScrollPrev and canScrollNext are always true because you can always continue scrolling in loop mode. This is expected behavior, and the arrow buttons remain always enabled.
Q: Autoplay doesn't work on certain devices. What's the cause?
A: The component automatically respects the system's "reduce motion" preference. If the user has enabled "reduce motion" in their system settings, autoplay will be disabled. Additionally, autoplay pauses when the page is not visible and resumes when the page becomes active again.
Q: How do I execute custom logic when the carousel transitions?
A: After obtaining the component instance via ref, you can watch for changes to selectedIndex. You can also use the useCarousel composable to build your own carousel UI, which exposes the full Embla event interface for registering select, scroll, and other event callbacks.