Carousel
Carousel
<section class="carousel" aria-label="Featured updates">
<div class="card p-0">
<div class="scrollbar-sm" data-carousel-viewport tabindex="0">
<div data-carousel-track>
<article data-carousel-item class="p-6">
<span class="badge" data-variant="secondary">Release</span>
<h3 class="mt-4 text-xl font-semibold">Ship faster with native patterns</h3>
<p class="mt-2 text-sm text-muted-foreground">Carousel uses CSS scroll snap and a small controller instead of a heavy drag dependency.</p>
</article>
<article data-carousel-item class="p-6">
<span class="badge" data-variant="secondary">Docs</span>
<h3 class="mt-4 text-xl font-semibold">Keep examples close to real HTML</h3>
<p class="mt-2 text-sm text-muted-foreground">Each slide can contain arbitrary semantic content, not only cards or images.</p>
</article>
<article data-carousel-item class="p-6">
<span class="badge" data-variant="secondary">Style packs</span>
<h3 class="mt-4 text-xl font-semibold">Works across every bundle</h3>
<p class="mt-2 text-sm text-muted-foreground">Radius, borders, muted surfaces, and control styling all come from the active style pack.</p>
</article>
</div>
</div>
</div>
<div data-carousel-controls>
<div data-carousel-nav>
<button type="button" class="btn" data-variant="outline" data-size="icon-sm" data-carousel-prev aria-label="Previous slide"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6" /></svg></button>
<button type="button" class="btn" data-variant="outline" data-size="icon-sm" data-carousel-next aria-label="Next slide"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 18 6-6-6-6" /></svg></button>
</div>
<div data-carousel-dots>
<button type="button" data-carousel-dot aria-label="Go to slide 1"></button>
<button type="button" data-carousel-dot aria-label="Go to slide 2"></button>
<button type="button" data-carousel-dot aria-label="Go to slide 3"></button>
</div>
</div>
</section> Usage
5h3ll-ui Carousel intentionally maps the upstream shadcn/ui idea onto native browser scrolling instead of porting Embla internals. The result is a smaller public API built from scroll snapping, semantic content, and a lightweight controller script.
Include CSS
@import "tailwindcss";
@import "5h3ll-ui/vega.css";Or import the base CSS plus Carousel and optional Scrollbar component CSS.
@import "tailwindcss";
@import "5h3ll-ui/base.css";
@import "5h3ll-ui/components/carousel.css";
@import "5h3ll-ui/components/scrollbar.css";
@import "5h3ll-ui/styles/vega.css"; Include JavaScript
<script src="/assets/js/all.min.js" defer></script>Or copy the runtime and Carousel script separately.
<script src="/assets/js/5h3ll-ui.min.js" defer></script>
<script src="/assets/js/carousel.min.js" defer></script> Add your carousel HTML
<section class="carousel" aria-label="Featured updates">
<div data-carousel-viewport tabindex="0">
<div data-carousel-track>
<article data-carousel-item>...</article>
<article data-carousel-item>...</article>
<article data-carousel-item>...</article>
</div>
</div>
<div data-carousel-controls>
<div data-carousel-nav>
<button type="button" data-carousel-prev>Previous</button>
<button type="button" data-carousel-next>Next</button>
</div>
<div data-carousel-dots>
<button type="button" data-carousel-dot></button>
<button type="button" data-carousel-dot></button>
<button type="button" data-carousel-dot></button>
</div>
</div>
</section> HTML structure
<section class=“carousel” aria-label=”…”>- Carousel root. Use
aria-labeloraria-labelledbyto give the region an accessible name. data-orientation=“horizontal|vertical”Optional- Switches the track to vertical scrolling. Horizontal is the default.
<div data-carousel-viewport tabindex=“0”>- Scrollable viewport. The script listens here for scroll state and keyboard navigation.
<div data-carousel-track>- Track wrapper holding the slides in order.
<article data-carousel-item>- One slide. Use any semantic element that fits the content.
data-carousel-prev,data-carousel-nextOptional- Previous and next controls.
data-carousel-dotsanddata-carousel-dotOptional- Direct slide navigation. Provide one dot per slide.
JavaScript API
Examples
Image cards
<section class="carousel" aria-label="Project gallery">
<div class="card p-0">
<div class="scrollbar-sm" data-carousel-viewport tabindex="0">
<div data-carousel-track>
<figure data-carousel-item class="p-4">
<div class="aspect-[16/9] rounded-xl bg-muted"></div>
<figcaption class="pt-4 text-sm">
<strong class="block">Dashboard overview</strong>
<span class="text-muted-foreground">Summary cards, quick actions, and recent activity.</span>
</figcaption>
</figure>
<figure data-carousel-item class="p-4">
<div class="aspect-[16/9] rounded-xl bg-muted"></div>
<figcaption class="pt-4 text-sm">
<strong class="block">Settings workspace</strong>
<span class="text-muted-foreground">Field groups composed from existing 5h3ll-ui primitives.</span>
</figcaption>
</figure>
<figure data-carousel-item class="p-4">
<div class="aspect-[16/9] rounded-xl bg-muted"></div>
<figcaption class="pt-4 text-sm">
<strong class="block">Team activity</strong>
<span class="text-muted-foreground">A scrollable layout that still keeps each frame isolated.</span>
</figcaption>
</figure>
</div>
</div>
</div>
<div data-carousel-controls>
<div data-carousel-nav>
<button type="button" class="btn" data-variant="outline" data-size="icon-sm" data-carousel-prev aria-label="Previous slide"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6" /></svg></button>
<button type="button" class="btn" data-variant="outline" data-size="icon-sm" data-carousel-next aria-label="Next slide"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 18 6-6-6-6" /></svg></button>
</div>
<div data-carousel-dots>
<button type="button" data-carousel-dot aria-label="Go to slide 1"></button>
<button type="button" data-carousel-dot aria-label="Go to slide 2"></button>
<button type="button" data-carousel-dot aria-label="Go to slide 3"></button>
</div>
</div>
</section> Vertical
<section class="carousel" data-orientation="vertical" aria-label="Onboarding steps">
<div class="card h-80 p-0">
<div class="scrollbar-sm h-full" data-carousel-viewport tabindex="0">
<div data-carousel-track>
<article data-carousel-item class="p-6">
<h3 class="text-base font-semibold">Create workspace</h3>
<p class="mt-2 text-sm text-muted-foreground">Set your workspace name, domain, and visual theme.</p>
</article>
<article data-carousel-item class="p-6">
<h3 class="text-base font-semibold">Invite your team</h3>
<p class="mt-2 text-sm text-muted-foreground">Add teammates and choose which notifications they receive.</p>
</article>
<article data-carousel-item class="p-6">
<h3 class="text-base font-semibold">Ship the first page</h3>
<p class="mt-2 text-sm text-muted-foreground">Start from blocks and adjust the markup to fit your app.</p>
</article>
</div>
</div>
</div>
<div data-carousel-controls>
<div data-carousel-nav>
<button type="button" class="btn" data-variant="outline" data-size="sm" data-carousel-prev>Previous</button>
<button type="button" class="btn" data-size="sm" data-carousel-next>Next</button>
</div>
<div data-carousel-dots>
<button type="button" data-carousel-dot aria-label="Go to slide 1"></button>
<button type="button" data-carousel-dot aria-label="Go to slide 2"></button>
<button type="button" data-carousel-dot aria-label="Go to slide 3"></button>
</div>
</div>
</section> Notes
Intentional divergence from upstream
Upstream shadcn/ui Carousel is built on Embla. 5h3ll-ui uses native scrolling and CSS scroll snap instead, so Embla-specific plugins and APIs are intentionally not part of this component.