Skip to content

Commit a9aefee

Browse files
carousel docs
1 parent b66a4fb commit a9aefee

File tree

3 files changed

+164
-19
lines changed

3 files changed

+164
-19
lines changed

apps/website/src/routes/docs/headless/carousel/examples/initial.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export default component$(() => {
77
const colors = ['red', 'green', 'blue', 'yellow', 'purple', 'orange', 'pink'];
88

99
return (
10-
<Carousel.Root class="carousel-root" gap={30} initialIndex={4}>
10+
<Carousel.Root class="carousel-root" gap={30} startIndex={4}>
1111
<div class="carousel-buttons">
1212
<Carousel.Previous>Prev</Carousel.Previous>
1313
<Carousel.Next>Next</Carousel.Next>

apps/website/src/routes/docs/headless/carousel/index.mdx

Lines changed: 159 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -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';

packages/kit-headless/src/components/carousel/root.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export type CarouselRootProps = PropsOf<'div'> & {
3131
'bind:selectedIndex'?: Signal<number>;
3232

3333
/** change the initial index of the carousel on render */
34-
initialIndex?: number;
34+
startIndex?: number;
3535

3636
/**
3737
* @deprecated Use bind:selectedIndex instead
@@ -58,7 +58,7 @@ export const CarouselBase = component$(
5858
'bind:selectedIndex': givenSlideIndexSig,
5959
'bind:autoplay': givenAutoplaySig,
6060
_isTitle: isTitle,
61-
initialIndex,
61+
startIndex,
6262
...props
6363
}: CarouselRootProps) => {
6464
// core state
@@ -72,7 +72,7 @@ export const CarouselBase = component$(
7272
const bulletRefsArray = useSignal<Array<Signal>>([]);
7373
const currentIndexSig = useBoundSignal(
7474
givenSlideIndexSig ?? givenOldSlideIndexSig,
75-
initialIndex ?? 0,
75+
startIndex ?? 0,
7676
);
7777
const isScrollerSig = useSignal(false);
7878
const isAutoplaySig = useBoundSignal(givenAutoplaySig, false);
@@ -107,7 +107,7 @@ export const CarouselBase = component$(
107107
alignSig,
108108
isLoopSig,
109109
autoPlayIntervalMsSig,
110-
initialIndex,
110+
initialIndex: startIndex,
111111
};
112112

113113
useContextProvider(carouselContextId, context);

0 commit comments

Comments
 (0)