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.
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")
]
];
}Text
Lines for a paragraph, each as wide as a class makes it.
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")
];
}Animation
Still, a shimmer, or a pulse.
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]
];
}Table
A loading placeholder for a data table.
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]
];
}| Customer | Date | Status | Amount |
|---|---|---|---|
Chart
A loading placeholder for a chart card.
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")
]
];
}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
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.SkeletonAnimateNone 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(…) | string | Inline CSS for the block: a width known only at run time. |
Ui.SkeletonLine
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.SkeletonLineSizeBase 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.SkeletonAnimateNone 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(…) | string | Inline CSS for the line: a width known only at run time. |
Ui.SkeletonGroup
A <div> that draws nothing and animates every skeleton inside it, however deep.
Step | Takes | What it does |
|---|---|---|
.Animate(…) | Ui.SkeletonAnimateNone 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(…) | string | Inline CSS for the group. |