Content & marketing
Carousel code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/carousel.blade.php
@props([
'autoplay' => false,
'interval' => 5000,
'loop' => true,
'controls' => true,
'indicators' => true,
])
@php
/*
| Carousel / slider. Each child is one slide — drop in any markup (images,
| cards, quotes). Navigate with the arrow controls, the dot indicators or
| the left/right arrow keys when focused. Optionally autoplays.
|
| Usage:
| <x-ui.carousel autoplay :interval="4000">
| <img src="/img/1.jpg" alt="">
| <img src="/img/2.jpg" alt="">
| <div class="flex h-full items-center justify-center">Any slide content</div>
| </x-ui.carousel>
*/
@endphp
<div
x-data="{
active: 0,
count: 0,
loop: @js((bool) $loop),
autoplay: @js((bool) $autoplay),
interval: @js((int) $interval),
timer: null,
init() {
this.count = this.$refs.track.children.length;
if (this.autoplay) { this.start(); }
},
start() {
if (! this.autoplay || this.count <= 1) { return; }
this.stop();
this.timer = setInterval(() => this.next(), this.interval);
},
stop() {
if (this.timer) { clearInterval(this.timer); this.timer = null; }
},
go(i) {
this.active = (i + this.count) % this.count;
},
next() {
if (! this.loop && this.active === this.count - 1) { return; }
this.go(this.active + 1);
},
prev() {
if (! this.loop && this.active === 0) { return; }
this.go(this.active - 1);
},
}"
@mouseenter="stop()"
@mouseleave="start()"
@keydown.arrow-right.prevent="next()"
@keydown.arrow-left.prevent="prev()"
tabindex="0"
role="region"
aria-roledescription="carousel"
{{ $attributes->merge(['class' => 'group/carousel relative overflow-hidden rounded-2xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500']) }}
>
{{-- Track --}}
<div
x-ref="track"
class="flex transition-transform duration-500 ease-out [&>*]:w-full [&>*]:shrink-0"
:style="`transform: translateX(-${active * 100}%)`"
>
{{ $slot }}
</div>
@if ($controls)
<button
type="button"
@click="prev()"
x-show="loop || active > 0"
aria-label="Previous slide"
class="absolute left-3 top-1/2 -translate-y-1/2 flex h-9 w-9 items-center justify-center rounded-full bg-white/90 dark:bg-gray-900/90 text-gray-700 dark:text-gray-200 shadow-md ring-1 ring-black/5 transition hover:bg-white dark:hover:bg-gray-800 cursor-pointer opacity-0 group-hover/carousel:opacity-100 focus-visible:opacity-100"
>
<x-ui.icon name="caret-left" weight="bold" size="lg" />
</button>
<button
type="button"
@click="next()"
x-show="loop || active < count - 1"
aria-label="Next slide"
class="absolute right-3 top-1/2 -translate-y-1/2 flex h-9 w-9 items-center justify-center rounded-full bg-white/90 dark:bg-gray-900/90 text-gray-700 dark:text-gray-200 shadow-md ring-1 ring-black/5 transition hover:bg-white dark:hover:bg-gray-800 cursor-pointer opacity-0 group-hover/carousel:opacity-100 focus-visible:opacity-100"
>
<x-ui.icon name="caret-right" weight="bold" size="lg" />
</button>
@endif
@if ($indicators)
<div class="pointer-events-none absolute inset-x-0 bottom-3 flex items-center justify-center gap-2">
<template x-for="i in count" :key="i">
<button
type="button"
@click="go(i - 1)"
:aria-label="`Go to slide ${i}`"
:aria-current="active === i - 1"
:class="active === i - 1 ? 'w-6 bg-brand-500' : 'w-2 bg-white/70 dark:bg-gray-600 hover:bg-white dark:hover:bg-gray-500'"
class="pointer-events-auto h-2 rounded-full shadow-sm ring-1 ring-black/5 transition-all cursor-pointer"
></button>
</template>
</div>
@endif
</div>