x
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<div class="flex items-center gap" data-controller="carousel" data-action="keydown.left->carousel#prev keydown.right->carousel#next" role="region" aria-roledescription="carousel">
<button class="btn btn--icon" data-action="carousel#prev">
<img aria-hidden="true" src="/assets/arrow-left-a842582d.svg" width="16" height="16" />
<span class="sr-only">Previous slide</span>
</button>
<div class="carousel__content" style="max-inline-size: 20rem" data-carousel-target="content" index="-1">
<div class="card flex items-center justify-center aspect-square" role="group" aria-roledescription="slide">
<span class="text-4xl leading-none font-semibold">1</span>
</div>
<div class="card flex items-center justify-center aspect-square" role="group" aria-roledescription="slide">
<span class="text-4xl leading-none font-semibold">2</span>
</div>
<div class="card flex items-center justify-center aspect-square" role="group" aria-roledescription="slide">
<span class="text-4xl leading-none font-semibold">3</span>
</div>
<div class="card flex items-center justify-center aspect-square" role="group" aria-roledescription="slide">
<span class="text-4xl leading-none font-semibold">4</span>
</div>
<div class="card flex items-center justify-center aspect-square" role="group" aria-roledescription="slide">
<span class="text-4xl leading-none font-semibold">5</span>
</div>
</div>
<button class="btn btn--icon" data-action="carousel#next">
<img aria-hidden="true" src="/assets/arrow-right-2e20af60.svg" width="16" height="16" />
<span class="sr-only">Next slide</span>
</button>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<div class="flex items-center gap" data-controller="carousel" data-action="keydown.left->carousel#prev keydown.right->carousel#next" role="region" aria-roledescription="carousel">
<button class="btn btn--icon" data-action="carousel#prev">
<%= image_tag "arrow-left.svg", size: 16, aria: { hidden: true } %>
<span class="sr-only">Previous slide</span>
</button>
<div class="carousel__content" style="max-inline-size: 20rem" data-carousel-target="content" index="-1">
<div class="card flex items-center justify-center aspect-square" role="group" aria-roledescription="slide">
<span class="text-4xl leading-none font-semibold">1</span>
</div>
<div class="card flex items-center justify-center aspect-square" role="group" aria-roledescription="slide">
<span class="text-4xl leading-none font-semibold">2</span>
</div>
<div class="card flex items-center justify-center aspect-square" role="group" aria-roledescription="slide">
<span class="text-4xl leading-none font-semibold">3</span>
</div>
<div class="card flex items-center justify-center aspect-square" role="group" aria-roledescription="slide">
<span class="text-4xl leading-none font-semibold">4</span>
</div>
<div class="card flex items-center justify-center aspect-square" role="group" aria-roledescription="slide">
<span class="text-4xl leading-none font-semibold">5</span>
</div>
</div>
<button class="btn btn--icon" data-action="carousel#next">
<%= image_tag "arrow-right.svg", size: 16, aria: { hidden: true } %>
<span class="sr-only">Next slide</span>
</button>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.carousel__content {
display: flex;
gap: var(--size-6);
inline-size: var(--size-full);
overflow: hidden;
padding: var(--size-1);
scroll-snap-type: x mandatory;
> * {
flex: 0 0 var(--carousel-item-size, 100%);
scroll-snap-stop: always;
scroll-snap-align: start;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "content" ]
next() {
this.contentTarget.scrollTo({ left: this.#nextPosition, behavior: "smooth" })
}
prev() {
this.contentTarget.scrollTo({ left: this.#prevPosition, behavior: "smooth" })
}
get #nextPosition() {
return this.#scrollLeft + this.#slideSize
}
get #prevPosition() {
return this.#scrollLeft - this.#slideSize
}
get #scrollLeft() {
return this.contentTarget.scrollLeft
}
get #slideSize() {
return this.contentTarget.clientWidth
}
}