Rask
Rask
v0.23.1-alpha.0.440

Components

Carousel

Slides that scroll sideways and come to rest on one. The browser scrolls and snaps, so a wheel, a swipe and the keyboard all move it, and the arrows, the indicators and autoplay follow in the browser with nothing sent to the page. A slide's width is its class.

Drawn and behaving as Flux UI's carousel does, measured against its live docs.

Carousel

A slide at a time, with arrows over it.

CarouselExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CarouselExample : Component
{
    // Placeholders where Flux's page shows photographs.
    private static readonly string[] Scenes = ["Seascape", "Woodscape", "Landscape"];

    protected override Component? Render() =>
        Div.Class("mx-auto w-full max-w-sm")[
            Ui.Carousel.TrackClass("rounded-lg")[
                Scenes.Select(scene => Ui.CarouselSlide.Key(scene).Class("w-full")[
                    Div.Class("flex aspect-16/9 items-center justify-center rounded-lg bg-zinc-100 text-sm font-medium text-zinc-500 dark:bg-white/10 dark:text-white/50")[scene]
                ])
            ]
        ];
}
Live result
Seascape
Woodscape
Landscape

Indicators

One button per slide under the carousel, and no arrows.

CarouselIndicatorsExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CarouselIndicatorsExample : Component
{
    private static readonly (string Said, string Name, string Role)[] Quotes =
    [
        ("“We shipped a polished UI in days, not weeks.”", "Graham Whitaker", " · Founder, Northwind"),
        ("“When I reach for a component, it just works.”", "Elise Rowan", " · Engineering Lead, Southline Studio"),
        ("“I stopped writing UI primitives entirely.”", "Jonah Vale", " · CTO, Alder & Co."),
    ];

    protected override Component? Render() =>
        Div.Class("mx-auto w-full max-w-sm")[
            Ui.Carousel.Class("w-full").Indicators().Arrows(false)[
                Quotes.Select(quote => Ui.CarouselSlide.Key(quote.Name).Class("w-full")[
                    Div.Class("flex flex-col items-center gap-4 text-center")[
                        Blockquote.Class("text-balance text-zinc-700 dark:text-zinc-300")[quote.Said],
                        Ui.Text.Sm[Ui.Text.Inline().Variant(Ui.TextVariant.Strong)[quote.Name], quote.Role]
                    ]
                ])
            ]
        ];
}
Live result
“We shipped a polished UI in days, not weeks.”

Graham Whitaker · Founder, Northwind

“When I reach for a component, it just works.”

Elise Rowan · Engineering Lead, Southline Studio

“I stopped writing UI primitives entirely.”

Jonah Vale · CTO, Alder & Co.

Autoplay

Moves on every 3.5 seconds, and holds under the pointer.

CarouselAutoplayExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CarouselAutoplayExample : Component
{
    private static readonly string[] Scenes = ["Seascape", "Woodscape", "Landscape"];

    protected override Component? Render() =>
        Div.Class("mx-auto w-full max-w-sm")[
            Ui.Carousel.Autoplay().AutoplayInterval(3500).Indicators().TrackClass("rounded-lg")[
                Scenes.Select(scene => Ui.CarouselSlide.Key(scene).Class("w-full")[
                    Div.Class("flex aspect-16/9 items-center justify-center rounded-lg bg-zinc-100 text-sm font-medium text-zinc-500 dark:bg-white/10 dark:text-white/50")[scene]
                ])
            ]
        ];
}
Live result
Seascape
Woodscape
Landscape

External controls

Ui.CarouselControls anywhere on the page move the carousel that carries their name.

CarouselExternalControlsExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CarouselExternalControlsExample : Component
{
    private static readonly (string Name, string Price, string Rating)[] Stays =
    [
        ("Cottage in Mendocino", "$680", "5.0"), ("Lodge in the Thousand Islands", "$370", "4.96"),
        ("House in the Adirondacks", "$550", "4.88"), ("House in Cape Cod", "$840", "5"),
    ];

    protected override Component? Render() =>
        Div.Class("px-6")[
            Div.Class("mb-4 flex items-center justify-between")[
                Div[Ui.Heading.Lg["Popular stays"], Ui.Text["Handpicked homes in beautiful places"]],
                Div[Ui.CarouselControls.Name("popular-stays")]
            ],
            Ui.Carousel.Name("popular-stays").Bleed().Arrows(false)[
                Stays.Select(stay => Ui.CarouselSlide.Key(stay.Name).Class("w-4/5 sm:w-1/2 md:w-1/3")[
                    Div.Class("aspect-2/1 rounded-lg bg-zinc-100 dark:bg-white/10"),
                    Ui.Heading.Class("mt-2")[stay.Name],
                    Div.Class("mt-1 flex items-center gap-0.5")[
                        Ui.Text.Class("text-xs")[stay.Price, " for 2 nights ・"],
                        Ui.Icon.Name(Ui.IconName.Star).Variant(Ui.IconVariant.Micro).Class("size-3"),
                        Ui.Text.Class("text-xs text-zinc-800 dark:text-white")[stay.Rating]
                    ]
                ])
            ]
        ];
}
Live result
Popular stays

Handpicked homes in beautiful places

Cottage in Mendocino

$680 for 2 nights ・

5.0

Lodge in the Thousand Islands

$370 for 2 nights ・

4.96

House in the Adirondacks

$550 for 2 nights ・

4.88

House in Cape Cod

$840 for 2 nights ・

5

Arrow position

Inside, the default, or half over each edge.

CarouselArrowPositionExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CarouselArrowPositionExample : Component
{
    private static readonly string[] Slides = ["one", "two"];

    protected override Component? Render() =>
        Div.Class("mx-auto max-w-xs space-y-6")[
            Ui.Carousel.Key("inside").TrackClass("rounded-lg")[Labelled("Inside")],
            Ui.Carousel.Key("overlap").Overlap.TrackClass("rounded-lg")[Labelled("Overlap")]
        ];

    private static IEnumerable<Component> Labelled(string label) =>
        Slides.Select(slide => Ui.CarouselSlide.Key(label + "-" + slide).Class("w-full")[
            Div.Class("flex h-40 w-full items-center justify-center bg-zinc-100 text-sm font-medium text-zinc-500 dark:bg-white/10 dark:text-white/50")[label]
        ]);
}
Live result
Inside
Inside
Overlap
Overlap

Fade

The edge with more slides behind it fades.

CarouselFadeExample.cs
using System.Globalization;

namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CarouselFadeExample : Component
{
    protected override Component? Render() =>
        Div.Class("px-6")[
            Ui.Carousel.Fade().Bleed()[
                Enumerable.Range(1, 5).Select(number => Ui.CarouselSlide.Key(number).Class("w-4/5 sm:w-1/2 md:w-1/3")[
                    Div.Class("flex aspect-2/1 items-center justify-center rounded-lg bg-zinc-100 text-2xl font-medium text-zinc-400 dark:bg-white/10 dark:text-white/40")[
                        number.ToString(CultureInfo.InvariantCulture)
                    ]
                ])
            ]
        ];
}
Live result
1
2
3
4
5

Page advance

An arrow moves by the slides in view.

CarouselPageExample.cs
using System.Globalization;

namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CarouselPageExample : Component
{
    protected override Component? Render() =>
        Div.Class("px-6")[
            Ui.Carousel.Page.Bleed()[
                Enumerable.Range(1, 10).Select(number => Ui.CarouselSlide.Key(number).Class("w-4/5 sm:w-1/2 md:w-1/3")[
                    Div.Class("flex aspect-2/1 items-center justify-center rounded-lg bg-zinc-100 text-2xl font-medium text-zinc-400 dark:bg-white/10 dark:text-white/40")[
                        number.ToString(CultureInfo.InvariantCulture)
                    ]
                ])
            ]
        ];
}
Live result
1
2
3
4
5
6
7
8
9
10

Arrows outside

CarouselOutsideExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CarouselOutsideExample : Component
{
    private static readonly string[] Slides = ["one", "two"];

    protected override Component? Render() =>
        Div.Class("mx-auto max-w-xs")[
            Ui.Carousel.Outside.TrackClass("rounded-lg")[
                Slides.Select(slide => Ui.CarouselSlide.Key(slide).Class("w-full")[
                    Div.Class("flex h-40 w-full items-center justify-center bg-zinc-100 text-sm font-medium text-zinc-500 dark:bg-white/10 dark:text-white/50")["Outside"]
                ])
            ]
        ];
}
Live result
Outside
Outside

Rewind

At the end the next arrow stays, and goes back to the first slide.

CarouselRewindExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CarouselRewindExample : Component
{
    private static readonly string[] Scenes = ["Seascape", "Woodscape", "Landscape"];

    protected override Component? Render() =>
        Div.Class("mx-auto w-full max-w-sm")[
            Ui.Carousel.Rewind.Mandatory.TrackClass("rounded-lg")[
                Scenes.Select(scene => Ui.CarouselSlide.Key(scene).Class("w-full")[
                    Div.Class("flex aspect-16/9 items-center justify-center rounded-lg bg-zinc-100 text-sm font-medium text-zinc-500 dark:bg-white/10 dark:text-white/50")[scene]
                ])
            ]
        ];
}
Live result
Seascape
Woodscape
Landscape

Disabled

The arrows and the indicators are out of use; the slides still scroll.

CarouselDisabledExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class CarouselDisabledExample : Component
{
    private static readonly string[] Scenes = ["Seascape", "Woodscape", "Landscape"];

    protected override Component? Render() =>
        Div.Class("mx-auto w-full max-w-sm")[
            Ui.Carousel.Disabled().Indicators().TrackClass("rounded-lg")[
                Scenes.Select(scene => Ui.CarouselSlide.Key(scene).Class("w-full")[
                    Div.Class("flex aspect-16/9 items-center justify-center rounded-lg bg-zinc-100 text-sm font-medium text-zinc-500 dark:bg-white/10 dark:text-white/50")[scene]
                ])
            ]
        ];
}
Live result
Seascape
Woodscape
Landscape

Behaviour

Nothing is sent to the page

The scrolling is the browser's: scroll-snap, so a wheel, a swipe and the keyboard move it as they move any scroller. What a stylesheet cannot do, Rask's runtime does from the markup (data-rask-carousel): it marks the root and each arrow data-at-start and data-at-end, disables the arrow with nowhere to go, marks the slide in view data-selected and its indicator aria-current, moves the track when an arrow or an indicator is pressed, and runs autoplay. There is no OnChange, as Flux has none.

A slide's width is its class, and decides how many are in view. daisyUI's carousel wrapper, which Ui.Carousel used to draw around carousel-item children, is gone.

Autoplay

Autoplay() advances on its interval, 5000ms unless AutoplayInterval says otherwise, and goes back to the first slide after the last. It holds under the pointer and while focus is inside, and never starts for a reader who asked for less motion.

What a screen reader hears

A group called "carousel" holding "slide, 2 of 5". Those words, and the buttons' names, are in the translation catalog.

Reference

Ui.Carousel

flux:carousel

Slides that scroll sideways and come to rest on one. The browser scrolls; the runtime moves the rest.

Step
Takes
What it does
.Arrows()boolWhether it has its own previous and next arrows. On unless false.
.ArrowsPosition(…)
Ui.CarouselArrowsPosition
Inside
Overlap
Outside

Each is a step of its own too, unless an HTML tag has its name.

Where those arrows sit. Inside when unset.
.ArrowsClass(…)stringClasses added to each of those arrows.
.Autoplay()boolAdvances by itself, holding while it is hovered, focused or moved by hand, and never for a reader who asked for less motion.
.AutoplayInterval(…)intMilliseconds between two advances. 5000 when unset.
.Indicators()boolOne button per slide under the carousel, each going to its slide.
.Disabled()boolTakes the arrows and the indicators out of use. The slides still scroll.
.Fade()boolFades the edge of the track that has more slides behind it.
.Bleed()boolRuns the track through the padding around it, by --ui-bleed (a card sets it) or 1.5rem.
.Snap(…)
Ui.CarouselSnap
Proximity
Mandatory

Each is a step of its own too, unless an HTML tag has its name.

How firmly it rests on a slide. Proximity when unset.
.Scroll(…)
Ui.CarouselScroll
Smooth
Instant

Each is a step of its own too, unless an HTML tag has its name.

How an arrow or an indicator moves it. Smooth when unset.
.Advance(…)
Ui.CarouselAdvance
Slide
Page

Each is a step of its own too, unless an HTML tag has its name.

How far an arrow moves it: a slide, or the slides in view. Slide when unset.
.Wrap(…)
Ui.CarouselWrap
None
Rewind

Each is a step of its own too, unless an HTML tag has its name.

What advancing past the last slide does. None when unset; Rewind goes back to the first.
.Name(…)stringA name unique on the page, for a Ui.CarouselControls elsewhere to move it by.
.TrackClass(…)stringClasses added to the scrolling track.

Ui.CarouselSlide

flux:carousel.slide

One slide. Its class is its width, and so how many are in view.

No steps of its own: what goes inside it is its content.

Ui.CarouselControls

flux:carousel.controls

Previous and next buttons anywhere on the page, for the carousel of that name.

Step
Takes
What it does
.Name(…)stringThe Name of the carousel they move.