Carousel
A headless Blazor carousel that owns state, navigation, timing, and ARIA — nothing else. Consumers compose controls and indicators using unstyled behavior components and style everything with Tailwind or their own classes.
Overview
MCarousel manages which slide is active and exposes navigation through
cascaded context. It renders no built-in controls, indicators, or CSS classes.
Slides always render in the DOM; visibility is controlled through the
data-state="active|inactive" attribute so consumers can drive it with
Tailwind's data-[state=inactive]:hidden or any other utility.
Three unstyled behavior components are available for composition inside a carousel:
MCarouselPrevious, MCarouselNext, and
MCarouselIndicator. Each wires up to the carousel context automatically
and adds no default styling.
Usage
Add the namespace import to use the component.
@using Marai.UI.Components.MCarouselBasic Usage
A carousel with three slides and composed prev/next controls. Inactive slides are hidden
via data-[state=inactive]:hidden on the slide's Class.
<MCarousel Class="relative w-full overflow-hidden" AriaLabel="Basic carousel">
<MCarouselSlide Class="data-[state=inactive]:hidden">
<div class="flex h-48 items-center justify-center rounded-lg bg-muted text-muted-foreground text-sm font-medium">
Slide 1
</div>
</MCarouselSlide>
<MCarouselSlide Class="data-[state=inactive]:hidden">
<div class="flex h-48 items-center justify-center rounded-lg bg-secondary text-secondary-foreground text-sm font-medium">
Slide 2
</div>
</MCarouselSlide>
<MCarouselSlide Class="data-[state=inactive]:hidden">
<div class="flex h-48 items-center justify-center rounded-lg bg-accent text-accent-foreground text-sm font-medium">
Slide 3
</div>
</MCarouselSlide>
<MCarouselPrevious aria-label="Previous slide"
Class="absolute left-2 top-1/2 -translate-y-1/2 inline-flex h-8 w-8 items-center justify-center rounded-full border border-border bg-background/80 text-foreground hover:bg-background">
‹
</MCarouselPrevious>
<MCarouselNext aria-label="Next slide"
Class="absolute right-2 top-1/2 -translate-y-1/2 inline-flex h-8 w-8 items-center justify-center rounded-full border border-border bg-background/80 text-foreground hover:bg-background">
›
</MCarouselNext>
</MCarousel>
Examples
Indicator Navigation
Use MCarouselIndicator with a required Index to add dot
navigation. Each indicator exposes data-state="active|inactive", so
active state styling is driven by data-[state=active]: utilities.
<MCarousel Class="relative w-full overflow-hidden" AriaLabel="Carousel with indicators">
<MCarouselSlide Class="data-[state=inactive]:hidden">
<div class="flex h-48 items-center justify-center rounded-lg bg-muted text-muted-foreground text-sm font-medium">
Slide 1 of 4
</div>
</MCarouselSlide>
<MCarouselSlide Class="data-[state=inactive]:hidden">
<div class="flex h-48 items-center justify-center rounded-lg bg-secondary text-secondary-foreground text-sm font-medium">
Slide 2 of 4
</div>
</MCarouselSlide>
<MCarouselSlide Class="data-[state=inactive]:hidden">
<div class="flex h-48 items-center justify-center rounded-lg bg-accent text-accent-foreground text-sm font-medium">
Slide 3 of 4
</div>
</MCarouselSlide>
<MCarouselSlide Class="data-[state=inactive]:hidden">
<div class="flex h-48 items-center justify-center rounded-lg bg-muted text-muted-foreground text-sm font-medium">
Slide 4 of 4
</div>
</MCarouselSlide>
<div class="absolute bottom-3 left-1/2 -translate-x-1/2 flex items-center gap-1.5" role="tablist" aria-label="Slide navigation">
<MCarouselIndicator Index="0" Class="h-2 w-2 rounded-full transition-all duration-300 bg-primary/30 data-[state=active]:w-6 data-[state=active]:bg-primary/80" />
<MCarouselIndicator Index="1" Class="h-2 w-2 rounded-full transition-all duration-300 bg-primary/30 data-[state=active]:w-6 data-[state=active]:bg-primary/80" />
<MCarouselIndicator Index="2" Class="h-2 w-2 rounded-full transition-all duration-300 bg-primary/30 data-[state=active]:w-6 data-[state=active]:bg-primary/80" />
<MCarouselIndicator Index="3" Class="h-2 w-2 rounded-full transition-all duration-300 bg-primary/30 data-[state=active]:w-6 data-[state=active]:bg-primary/80" />
</div>
<MCarouselPrevious aria-label="Previous slide"
Class="absolute left-2 top-1/2 -translate-y-1/2 inline-flex h-8 w-8 items-center justify-center rounded-full border border-border bg-background/80 text-foreground hover:bg-background">
‹
</MCarouselPrevious>
<MCarouselNext aria-label="Next slide"
Class="absolute right-2 top-1/2 -translate-y-1/2 inline-flex h-8 w-8 items-center justify-center rounded-full border border-border bg-background/80 text-foreground hover:bg-background">
›
</MCarouselNext>
</MCarousel>
Auto-Advance
Set AutoAdvance="true" to cycle through slides automatically.
Use Interval (in milliseconds) to control the delay between advances.
Auto-advance pauses when the user hovers over or focuses within the carousel, and resumes
when they leave.
<MCarousel AutoAdvance="true" Interval="3000" Class="relative w-full overflow-hidden" AriaLabel="Auto-advancing carousel">
<MCarouselSlide Class="data-[state=inactive]:hidden">
<div class="flex h-48 items-center justify-center rounded-lg bg-muted text-muted-foreground text-sm font-medium">
Auto-advances every 3 s — pauses on hover or focus
</div>
</MCarouselSlide>
<MCarouselSlide Class="data-[state=inactive]:hidden">
<div class="flex h-48 items-center justify-center rounded-lg bg-secondary text-secondary-foreground text-sm font-medium">
Slide 2
</div>
</MCarouselSlide>
<MCarouselSlide Class="data-[state=inactive]:hidden">
<div class="flex h-48 items-center justify-center rounded-lg bg-accent text-accent-foreground text-sm font-medium">
Slide 3
</div>
</MCarouselSlide>
<div class="absolute bottom-3 left-1/2 -translate-x-1/2 flex items-center gap-1.5">
<MCarouselIndicator Index="0" Class="h-2 w-2 rounded-full transition-all duration-300 bg-primary/30 data-[state=active]:w-6 data-[state=active]:bg-primary/80" />
<MCarouselIndicator Index="1" Class="h-2 w-2 rounded-full transition-all duration-300 bg-primary/30 data-[state=active]:w-6 data-[state=active]:bg-primary/80" />
<MCarouselIndicator Index="2" Class="h-2 w-2 rounded-full transition-all duration-300 bg-primary/30 data-[state=active]:w-6 data-[state=active]:bg-primary/80" />
</div>
<MCarouselPrevious aria-label="Previous slide"
Class="absolute left-2 top-1/2 -translate-y-1/2 inline-flex h-8 w-8 items-center justify-center rounded-full border border-border bg-background/80 text-foreground hover:bg-background">
‹
</MCarouselPrevious>
<MCarouselNext aria-label="Next slide"
Class="absolute right-2 top-1/2 -translate-y-1/2 inline-flex h-8 w-8 items-center justify-center rounded-full border border-border bg-background/80 text-foreground hover:bg-background">
›
</MCarouselNext>
</MCarousel>
Custom Controls
MCarouselPrevious and MCarouselNext render a native
<button> with no default styling. Pass any content — text, icons,
or SVGs — as ChildContent and style with Class.
<MCarousel Class="relative w-full overflow-hidden" AriaLabel="Carousel with custom controls">
<MCarouselSlide Class="data-[state=inactive]:hidden">
<div class="flex h-48 items-center justify-center rounded-lg bg-primary text-primary-foreground text-sm font-medium">
Controls styled with your own classes
</div>
</MCarouselSlide>
<MCarouselSlide Class="data-[state=inactive]:hidden">
<div class="flex h-48 items-center justify-center rounded-lg bg-secondary text-secondary-foreground text-sm font-medium">
Slide 2
</div>
</MCarouselSlide>
<MCarouselSlide Class="data-[state=inactive]:hidden">
<div class="flex h-48 items-center justify-center rounded-lg bg-muted text-muted-foreground text-sm font-medium">
Slide 3
</div>
</MCarouselSlide>
<MCarouselPrevious aria-label="Previous slide"
Class="absolute left-2 top-1/2 -translate-y-1/2 rounded px-3 py-1 text-xs font-medium bg-primary text-primary-foreground hover:bg-primary/90">
← Prev
</MCarouselPrevious>
<MCarouselNext aria-label="Next slide"
Class="absolute right-2 top-1/2 -translate-y-1/2 rounded px-3 py-1 text-xs font-medium bg-primary text-primary-foreground hover:bg-primary/90">
Next →
</MCarouselNext>
</MCarousel>
Fully Styled
A complete example using only Tailwind utilities — gradient slides, pill indicators, and frosted-glass prev/next buttons.
Slide One
Fully styled with Tailwind utilities
<MCarousel Class="relative w-full overflow-hidden rounded-xl shadow-lg" AriaLabel="Fully styled carousel">
<MCarouselSlide Class="data-[state=inactive]:hidden">
<div class="flex h-64 items-center justify-center bg-gradient-to-br from-primary to-primary/60 text-primary-foreground">
<div class="text-center">
<p class="text-2xl font-bold">Slide One</p>
<p class="mt-1 text-sm opacity-80">Fully styled with Tailwind utilities</p>
</div>
</div>
</MCarouselSlide>
<MCarouselSlide Class="data-[state=inactive]:hidden">
<div class="flex h-64 items-center justify-center bg-gradient-to-br from-secondary to-secondary/60 text-secondary-foreground">
<div class="text-center">
<p class="text-2xl font-bold">Slide Two</p>
<p class="mt-1 text-sm opacity-80">Style controlled entirely by the consumer</p>
</div>
</div>
</MCarouselSlide>
<MCarouselSlide Class="data-[state=inactive]:hidden">
<div class="flex h-64 items-center justify-center bg-gradient-to-br from-accent to-accent/60 text-accent-foreground">
<div class="text-center">
<p class="text-2xl font-bold">Slide Three</p>
<p class="mt-1 text-sm opacity-80">No default classes from the component</p>
</div>
</div>
</MCarouselSlide>
<div class="absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-2">
<MCarouselIndicator Index="0" Class="h-2 w-2 rounded-full transition-all duration-300 bg-white/40 data-[state=active]:w-6 data-[state=active]:bg-white" />
<MCarouselIndicator Index="1" Class="h-2 w-2 rounded-full transition-all duration-300 bg-white/40 data-[state=active]:w-6 data-[state=active]:bg-white" />
<MCarouselIndicator Index="2" Class="h-2 w-2 rounded-full transition-all duration-300 bg-white/40 data-[state=active]:w-6 data-[state=active]:bg-white" />
</div>
<MCarouselPrevious aria-label="Previous slide"
Class="absolute left-3 top-1/2 -translate-y-1/2 inline-flex h-9 w-9 items-center justify-center rounded-full bg-black/20 text-white backdrop-blur-sm hover:bg-black/40 transition-colors">
‹
</MCarouselPrevious>
<MCarouselNext aria-label="Next slide"
Class="absolute right-3 top-1/2 -translate-y-1/2 inline-flex h-9 w-9 items-center justify-center rounded-full bg-black/20 text-white backdrop-blur-sm hover:bg-black/40 transition-colors">
›
</MCarouselNext>
</MCarousel>
Accessibility
-
The carousel root is a
<section>withrole="region"and an accessible label set viaAriaLabel(defaults to"Carousel"). -
Every
MCarouselSlideis always in the DOM withrole="group",aria-roledescription="slide", andaria-hidden="true|false"to communicate visibility to assistive technologies. MCarouselIndicatorbuttons have a generatedaria-label("Go to slide N") anddata-statefor active state. Wrap them in a<div role="tablist" aria-label="Slide navigation">when using them as tab-style navigation.MCarouselPreviousandMCarouselNextare native buttons. Addaria-labelvia the attribute — for example,aria-label="Previous slide".-
Use
AriaLabelto provide a meaningful name when the default"Carousel"is not descriptive enough.
API Reference
MCarousel
| Parameter | Type | Default | Description |
|---|---|---|---|
| ChildContent | RenderFragment? | null |
Slides and composed controls placed inside the carousel. |
| AutoAdvance | bool | false |
When true, slides advance automatically on the configured Interval. |
| Interval | int | 5000 |
Milliseconds between auto-advance steps. Only used when AutoAdvance is true. |
| AriaLabel | string? | "Carousel" |
Accessible label for the carousel region. |
| Class | string? | null |
CSS classes applied to the root <section>. |
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes forwarded to the root element. |
MCarouselSlide
| Parameter | Type | Default | Description |
|---|---|---|---|
| ChildContent | RenderFragment? | null |
The content of this slide. |
| Class | string? | null |
CSS classes applied to the slide wrapper. Use
data-[state=inactive]:hidden to hide inactive slides.
|
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes forwarded to the slide element. |
MCarouselPrevious / MCarouselNext
| Parameter | Type | Default | Description |
|---|---|---|---|
| ChildContent | RenderFragment? | null |
Button content — icon, text, or any markup. |
| Class | string? | null |
CSS classes applied to the button element. |
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes forwarded to the button — use for aria-label. |
MCarouselIndicator
| Parameter | Type | Default | Description |
|---|---|---|---|
| Index | int | required | Zero-based slide index this indicator navigates to. |
| ChildContent | RenderFragment? | null |
Optional button content. Leave empty for a pure CSS indicator. |
| Class | string? | null |
CSS classes applied to the button. Use data-[state=active]: and
data-[state=inactive]: modifiers for state-driven styling.
|
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes forwarded to the button. |