Skip to content
ShineUI
Install

Skeleton

Shape before substance.

<sui-skeleton><sui-skeleton-card>FeedbackStable

About

Shimmer placeholder that holds the shape of content while it loads, in four shapes: text, title, rect and circle. The host itself is the shimmering block, so size it with width and height and lay it out like any element. Compose a region from several, or drop in sui-skeleton-card for the common card shape.

Usage

<sui-skeleton shape="title"></sui-skeleton> <script type="module"> import { defineCustomElements } from "@shineui/core/loader"; import "@shineui/core/shineui.css"; defineCustomElements(); </script>
import { SuiSkeleton } from "@shineui/react"; import "@shineui/core/shineui.css"; export function Example() { return <SuiSkeleton shape="title" />; }
<script setup> import { SuiSkeleton } from "@shineui/vue"; import "@shineui/core/shineui.css"; </script> <template> <SuiSkeleton shape="title" /> </template>
import { Component } from "@angular/core"; import { SuiSkeleton } from "@shineui/angular"; @Component({ standalone: true, imports: [SuiSkeleton], template: `<sui-skeleton shape="title"></sui-skeleton>`, }) export class Example { }

Basic

A title line and two text lines. Every skeleton is aria-hidden, so a loading region announces nothing on its own: put aria-busy="true" on the container holding them and take it off when the real content lands.

<div aria-busy="true" style="display:flex;flex-direction:column;gap:12px;max-width:420px;"> <sui-skeleton shape="title"></sui-skeleton> <sui-skeleton></sui-skeleton> <sui-skeleton width="70%"></sui-skeleton> </div>

Shapes and sizes

Four shapes: text, title, rect and circle, each with its own default size. width and height take a CSS length or a bare number read as px, and a circle takes its height from its width.

<div style="display:flex;flex-direction:column;gap:12px;max-width:420px;"> <sui-skeleton shape="title" width="220px"></sui-skeleton> <sui-skeleton width="60%"></sui-skeleton> <sui-skeleton height="20"></sui-skeleton> <div style="display:flex;gap:10px;align-items:center;"> <sui-skeleton shape="circle" width="48"></sui-skeleton> <sui-skeleton shape="rect" height="48" style="flex:1;"></sui-skeleton> </div> </div>

Card

sui-skeleton-card is the ready-made card: a circle avatar, a title and caption line, then three text lines in the card frame. It is aria-hidden like its parts, so the same aria-busy rule applies to the container.

<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px;"> <sui-skeleton-card></sui-skeleton-card> <sui-skeleton-card></sui-skeleton-card> </div>

Animation off

animation="none" drops the shimmer and leaves the flat sunken block, so the placeholder keeps its shape and weight. A long list is the case for it, since a 40 by 5 table skeleton is 200 gradients repainting every frame; on sui-skeleton-card one value reaches all six parts.

<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px;"> <div style="display:flex;flex-direction:column;gap:10px;"> <sui-skeleton shape="title" animation="none"></sui-skeleton> <sui-skeleton animation="none"></sui-skeleton> <sui-skeleton width="70%" animation="none"></sui-skeleton> <sui-skeleton width="46%" animation="none"></sui-skeleton> </div> <sui-skeleton-card animation="none"></sui-skeleton-card> </div>

A loading table

The anatomy is composed, not configured: rows of sui-skeleton in the real table geometry. aria-busy on the region is what tells a screen reader anything is happening.

<div aria-busy="true" style="background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:14px 16px;display:flex;flex-direction:column;gap:12px;max-width:520px;"> <div style="display:grid;grid-template-columns:2fr 1fr 1fr;gap:14px;"> <sui-skeleton shape="title" width="70%" animation="none"></sui-skeleton> <sui-skeleton shape="title" width="60%" animation="none"></sui-skeleton> <sui-skeleton shape="title" width="50%" animation="none"></sui-skeleton> </div> <div style="display:grid;grid-template-columns:2fr 1fr 1fr;gap:14px;"> <sui-skeleton animation="none"></sui-skeleton> <sui-skeleton width="70%" animation="none"></sui-skeleton> <sui-skeleton width="55%" animation="none"></sui-skeleton> </div> <div style="display:grid;grid-template-columns:2fr 1fr 1fr;gap:14px;"> <sui-skeleton width="82%" animation="none"></sui-skeleton> <sui-skeleton width="64%" animation="none"></sui-skeleton> <sui-skeleton width="48%" animation="none"></sui-skeleton> </div> <div style="display:grid;grid-template-columns:2fr 1fr 1fr;gap:14px;"> <sui-skeleton width="90%" animation="none"></sui-skeleton> <sui-skeleton width="58%" animation="none"></sui-skeleton> <sui-skeleton width="62%" animation="none"></sui-skeleton> </div> </div>

Card groupsui-skeleton-card

The ready-made card composed from sui-skeleton.

<div style="max-width:420px;"><sui-skeleton-card></sui-skeleton-card></div>

API

<sui-skeleton>

Properties

PropertyAttributeTypeDefaultDescription
animationanimation"none" | "wave"'wave'none drops the shimmer and leaves the flat sunken block. A long list is the case for it: a 40 x 5 table skeleton is 200 gradients repainting every frame.
heightheightnumber | string | undefinedCSS length or px number. Defaults per shape (circle follows width).
shapeshape"circle" | "rect" | "text" | "title"'text'Placeholder shape and its default size: text a 12px line, title 17px at 45% width, rect an 84px block, circle 36px. Every shape is aria-hidden, so a loading region says nothing on its own; put aria-busy="true" on the container while it loads.
widthwidthnumber | string | undefinedCSS length or px number. Defaults per shape.

<sui-skeleton-card>

Properties

PropertyAttributeTypeDefaultDescription
animationanimation"none" | "wave"'wave'Passed to every part, so the whole card shimmers or none of it does; a card whose avatar shimmered over flat lines would read as half loaded. none stops the shimmer on a long list of cards; see sui-skeleton.

Theming

Every design token this element can render with, read from its own stylesheets and the ones it imports. Set a token on :root or on any ancestor and every use below follows it. The values shown are the ones in the theme you are viewing. The list is read per stylesheet, so an element that imports a shared drawing module lists that module's tokens too, whether or not it paints with all of them.

See also