@@ -18,57 +18,112 @@ Displays multiple content items in one space, rotating through them.
1818 features = { [
1919 ' Follows WAI-ARIA design pattern' ,
2020 ' Full keyboard navigation' ,
21- ' Dynamic Slide Offsetting' ,
22- ' Transition Duration Control' ,
23- ' Pagination' ,
24- ' Navigate via Prev/Next buttons' ,
21+ ' Dynamic slide offsetting' ,
22+ ' Customizable alignment (start, center, end)' ,
23+ ' Pagination with bullet navigation' ,
24+ ' Navigate via Previous/Next buttons' ,
25+ ' Autoplay functionality' ,
26+ ' Looping option' ,
27+ ' Support for multiple slides per view' ,
28+ ' Reactive slide updates' ,
29+ ' Initial slide selection' ,
30+ ' Customizable accessible names' ,
31+ ' Supports scroller and conditional carousels' ,
2532 ]}
2633 roadmap = { [
27- ' Tests' ,
28- ' Documentation' ,
29- ' Fix for the "snapping" behavior when going pass the last slide' ,
30- ' A refactor of the API (for example, conventions like the select)' ,
31- ' Remove additional styles to the headless component' ,
34+ ' Improve test coverage' ,
35+ ' Enhance documentation' ,
36+ ' Refine API for consistency with other components' ,
37+ ' Add support for vertical carousels' ,
3238 ]}
3339/>
3440
3541## CSS Scroll snapping
3642
43+ Qwik UI combines CSS scroll snapping and flexbox for the carousel:
44+
45+ - Scroll snapping: Used on mobile for smooth touch interactions and initial snap position.
46+ - Flexbox: Provides a simple layout system for variable widths, gaps, and columns.
47+
48+ > Styles are in an @layer for easy customization:
49+
3750``` css
3851@layer qwik-ui {
52+ [data-qui-carousel-scroller ] {
53+ overflow : hidden ;
54+ display : flex ;
55+ gap : var (--gap );
56+ /* for mobile & scroll-snap-start */
57+ scroll-snap-type : x mandatory ;
58+ }
59+
60+ [data-qui-carousel-slide ] {
61+ /* default, feel free to override */
62+ --total-gap-width : calc (var (--gap ) * (var (--slides-per-view ) - 1 ));
63+ --available-slide-width : calc (100% - var (--total-gap-width ));
64+ --slide-width : calc (var (--available-slide-width ) / var (--slides-per-view ));
65+
66+ flex-basis : var (--slide-width );
67+ flex-shrink : 0 ;
68+ }
69+
3970 @media (pointer: coarse) {
4071 [data-qui-carousel-scroller ][data-draggable ] {
41- scroll-snap-type : x mandatory ;
4272 overflow-x : scroll ;
4373 }
4474
45- [data-draggable ] [data-qui-carousel-slide ] {
75+ /* make sure snap align is added after initial index animation */
76+ [data-draggable ][data-initial-touch ] [data-qui-carousel-slide ] {
4677 scroll-snap-align : start ;
4778 }
79+
80+ [data-draggable ][data-align = ' center' ][data-initial-touch ] [data-qui-carousel-slide ] {
81+ scroll-snap-align : center ;
82+ }
83+
84+ [data-draggable ][data-align = ' end' ][data-initial-touch ] [data-qui-carousel-slide ] {
85+ scroll-snap-align : end ;
86+ }
4887 }
4988}
5089```
5190
5291## Pagination
5392
93+ Use ` <Carousel.Pagination /> ` and ` <Carousel.Bullet /> ` components to add pagination.
94+
5495<Showcase name = " pagination" />
5596
97+ > These are exposed to assistive technologies as tabs for screen readers.
98+
5699## Multiple Slides
57100
101+ Set the ` slidesPerView ` prop for multiple slides.
102+
58103<Showcase name = " multiple-slides" />
59104
60105## Non-draggable
61106
107+ Opt-out of the draggable behavior by setting the ` draggable ` prop to ` false ` .
108+
62109<Showcase name = " non-draggable" />
63110
64111## Different widths
65112
113+ By default, the slides will take up the full width of the carousel.
114+
115+ To change this, use the ` flex-basis ` CSS property on the ` <Carousel.Slide /> ` component.
116+
66117<Showcase name = " different-widths" />
67118
68119## Without Scroller
69120
121+ Qwik UI supports carousels without a scroller, which can be useful for conditional slide carousels.
122+
70123<Showcase name = " without-scroller" />
71124
125+ Remove the ` <Carousel.Scroller /> ` component to remove the scroller.
126+
72127## Animations
73128
74129### Conditional Slides
@@ -97,34 +152,124 @@ Displays multiple content items in one space, rotating through them.
97152
98153## CSR
99154
155+ Both SSR and CSR are supported. In this example, we conditionally render the carousel based on an interaction.
156+
100157<Showcase name = " csr" />
101158
102- ## Align
159+ ## Center
103160
104- ### Center
161+ Align slides to the center of the carousel by setting the ` align ` prop to ` center ` .
105162
106163<Showcase name = " center" />
107164
108- ### End
165+ ## End
166+
167+ Align slides to the end of the carousel by setting the ` align ` prop to ` end ` .
109168
110169<Showcase name = " end" />
111170
112171## Loop
113172
173+ Loop the carousel by setting the ` loop ` prop to ` true ` .
174+
114175<Showcase name = " loop" />
115176
177+ > When looping, navigation buttons are never disabled.
178+
116179## Accessible Name
117180
181+ Add an accessible name to the carousel by adding the ` <Carousel.Title /> ` component.
182+
118183<Showcase name = " title" />
119184
185+ To hide the title from screen readers, use the ` <VisuallyHidden /> ` component.
186+
187+ > The title is automatically added to the carousel's ` aria-labelledby ` attribute.
188+
120189## Autoplay
121190
191+ To use autoplay, use the ` bind:autoplay ` prop.
192+
122193<Showcase name = " player" />
123194
195+ ### What if I want to autoplay on initial render?
196+
197+ Use a visible task to change the signal passed to ` bind:autoplay ` to ` true ` when the component is visible.
198+
199+ ``` tsx
200+ {/* inside your component */ }
201+ useVisibleTask$ (() => {
202+ isAutoplaySig .value = true ;
203+ })
204+
205+ {/* the carousel */ }
206+ <Carousel.Root bind :autoplay = { isAutoplaySig } >
207+ ```
208+
124209## Initial
125210
211+ To set an initial slide position, use the `startIndex` prop.
212+
126213<Showcase name = " initial" />
127214
128215## Reactive
129216
217+ Reactively control the selected slide index by using the `bind:selectedIndex` prop.
218+
130219<Showcase name = " reactive" />
220+
221+ ## API
222+
223+ ### Carousel.Root
224+
225+ <APITable
226+ propDescriptors = { [
227+ {
228+ name: ' gap' ,
229+ type: ' number' ,
230+ description: ' The gap between slides.' ,
231+ },
232+ {
233+ name: ' slidesPerView' ,
234+ type: ' number' ,
235+ description: ' Number of slides to show at once.' ,
236+ },
237+ {
238+ name: ' draggable' ,
239+ type: ' boolean' ,
240+ description: ' Whether the carousel is draggable.' ,
241+ },
242+ {
243+ name: ' align' ,
244+ type: ' union' ,
245+ description: ' Alignment of slides within the viewport.' ,
246+ info: ' "start" | "center" | "end"' ,
247+ },
248+ {
249+ name: ' loop' ,
250+ type: ' boolean' ,
251+ description: ' Whether the carousel should loop.' ,
252+ },
253+ {
254+ name: ' bind:selectedIndex' ,
255+ type: ' Signal<number>' ,
256+ description: ' Bind the selected index to a signal.' ,
257+ },
258+ {
259+ name: ' startIndex' ,
260+ type: ' number' ,
261+ description: ' Change the initial index of the carousel on render.' ,
262+ },
263+ {
264+ name: ' bind:autoplay' ,
265+ type: ' Signal<boolean>' ,
266+ description: ' Whether the carousel should autoplay.' ,
267+ },
268+ {
269+ name: ' autoPlayIntervalMs' ,
270+ type: ' number' ,
271+ description: ' Time in milliseconds before the next slide plays during autoplay.' ,
272+ },
273+ ]}
274+ />
275+ import { info } from 'console';
0 commit comments