Rask
Rask
v0.23.1-alpha.0.440

Components

Skeleton

The shape of what is coming, so the layout does not jump when it arrives. A skeleton is a block the call site sizes and rounds, a line keeps the height of the text it stands in for, and a group animates everything inside it.

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

Skeleton

An avatar and two lines, shimmering together.

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

public sealed partial class SkeletonExample : Component
{
    protected override Component? Render() =>
        Ui.SkeletonGroup.Shimmer.Class("flex max-w-64 items-center gap-4")[
            Ui.Skeleton.Class("size-10 rounded-full"),
            Div.Class("flex-1")[
                Ui.SkeletonLine,
                Ui.SkeletonLine.Class("w-1/2")
            ]
        ];
}
Live result

Text

Lines for a paragraph, each as wide as a class makes it.

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

public sealed partial class SkeletonLinesExample : Component
{
    protected override Component? Render() =>
        Ui.SkeletonGroup.Shimmer.Class("max-w-sm")[
            Ui.SkeletonLine.Class("mb-2 w-1/4"),
            Ui.SkeletonLine,
            Ui.SkeletonLine,
            Ui.SkeletonLine.Class("w-3/4")
        ];
}
Live result

Animation

Still, a shimmer, or a pulse.

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

public sealed partial class SkeletonAnimationExample : Component
{
    protected override Component? Render() =>
        Div.Class("flex max-w-sm flex-col gap-6")[
            Div[Div.Class("mb-2 text-sm font-medium")["None"], Ui.Skeleton],
            Div[Div.Class("mb-2 text-sm font-medium")["Shimmer"], Ui.Skeleton.Shimmer],
            Div[Div.Class("mb-2 text-sm font-medium")["Pulse"], Ui.Skeleton.Pulse]
        ];
}
Live result
None
Shimmer
Pulse

Table

A loading placeholder for a data table.

SkeletonTableExample.cs
using System.Globalization;

namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class SkeletonTableExample : Component
{
    protected override Component? Render() =>
        Ui.SkeletonGroup.Shimmer[
            Ui.Table.Class("mb-0")[
                Thead[Tr[Th["Customer"], Th["Date"], Th["Status"], Th["Amount"]]],
                Tbody[Enumerable.Range(1, 5).Select(Row)]
            ]
        ];

    // The first line of each row a different width, as a column of real names would be.
    private static Component Row(int order) =>
        Tr.Key(order)[
            Td[
                Div.Class("flex items-center gap-2")[
                    Ui.Skeleton.Class("size-5 rounded-full"),
                    Div.Class("flex-1")[
                        Ui.SkeletonLine.Style(string.Create(CultureInfo.InvariantCulture, $"width:{50 + ((order * 37) % 51)}%"))
                    ]
                ]
            ],
            Td[Ui.SkeletonLine],
            Td[Ui.SkeletonLine],
            Td[Ui.SkeletonLine]
        ];
}
Live result
CustomerDateStatusAmount

Chart

A loading placeholder for a chart card.

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

public sealed partial class SkeletonChartExample : Component
{
    protected override Component? Render() =>
        Ui.Card[
            Div.Class("flex flex-col gap-6")[
                Div.Class("flex gap-12")[
                    Div[
                        Ui.Text["Today"],
                        Ui.Heading.Xl.Class("mt-2 tabular-nums")["$---"],
                        Ui.Text.Class("mt-2 tabular-nums")["-:-- PM"]
                    ],
                    Div[
                        Ui.Text["Yesterday"],
                        Ui.Heading.Lg.Class("mt-2 tabular-nums")["$---"]
                    ]
                ],
                Ui.Skeleton.Shimmer.Class("aspect-[4/1] size-full rounded-lg")
            ]
        ];
}
Live result

Today

$---

-:-- PM

Yesterday

$---

Behaviour

Sizes

Ui.Skeleton on its own is a 16px bar the width of its container; .Class("size-10 rounded-full") makes it an avatar, .Class("aspect-[4/1] rounded-lg") a chart. Its height and radius are written at zero specificity, so the call site's always win.

Ui.SkeletonLine takes up the whole line height of the text it stands in for and draws a bar the height of the letters, so a page keeps its rhythm when the words land: Base, a 14px bar in a 20px line, or Lg, a 16px bar in a 24px line. Its width is the container's; .Class("w-1/2") shortens it.

Animation

Shimmer carries a band of light across, left to right, every two seconds; Pulse fades to half and back, every two seconds. A skeleton is still unless it, or a group around it, says otherwise.

Ui.SkeletonGroup is a <div> that draws nothing. Its Animate reaches every skeleton and line inside it, however deep, that does not state its own: Ui.Skeleton.Pulse or .Animate(Ui.SkeletonAnimate.None) keeps its own.

The shimmer's light is --ui-shimmer-color: white, and zinc-900 in dark. On a surface that is neither, set it to that surface's colour. As in Flux, neither animation stops under prefers-reduced-motion.

What it says

Nothing: a skeleton carries no ARIA of its own. Say that the region is loading on the region (aria-busy). The roots are marked data-ui-skeleton, data-ui-skeleton-line and data-ui-skeleton-group.

Reference

Ui.Skeleton

flux:skeleton

A block in the shape of what is coming: 16px tall and as wide as its container until Class sizes and rounds it.

Step
Takes
What it does
.Animate(…)
Ui.SkeletonAnimate
None
Shimmer
Pulse

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

How it shows something is on its way. Its group's when unset, and still without one.
.Style(…)stringInline CSS for the block: a width known only at run time.

Ui.SkeletonLine

flux:skeleton.line

A line of text that has not arrived: the line's full height, a bar the height of its letters.

Step
Takes
What it does
.Size(…)
Ui.SkeletonLineSize
Base
Lg

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

The size of text it stands in for. Base when unset.
.Animate(…)
Ui.SkeletonAnimate
None
Shimmer
Pulse

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

How it shows something is on its way. Its group's when unset, and still without one.
.Style(…)stringInline CSS for the line: a width known only at run time.

Ui.SkeletonGroup

flux:skeleton.group

A <div> that draws nothing and animates every skeleton inside it, however deep.

Step
Takes
What it does
.Animate(…)
Ui.SkeletonAnimate
None
Shimmer
Pulse

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

How every skeleton inside animates, unless it states its own.
.Style(…)stringInline CSS for the group.