Connection lost. Reconnecting… attempt 1 of 8
Paused. Your work is held on the server.
Could not reconnect.
This session has expired on the server.
An unhandled error has occurred.
Sedna.UI
v0.17.0 · main

Skeletons

tier 2 — classes

A placeholder the shape of what is coming, so the page does not reflow when it arrives. One class shimmers; the rest are the shapes a page is made of — a line, a title, a control, a circle, a picture, a block — and a loading state is those shapes arranged as the real thing will be.

A skeleton is for a known shape A table of rows, a card of fields, a chart: the reader knows what is coming, so the placeholder can be its shape. A wait with a percentage is a .progress, a wait with nothing to report is a .spinner, and a panel that will stay empty is an empty state. Mark the container aria-busy="true" — the bars are decoration and must not be announced one by one — and give every placeholder the height of the real thing, because a height that is merely close reflows the page when the content lands.

Lines and a title

.skeleton-text is a line of text and .skeleton-title a heading; .skeleton-avatar is the small circle beside one. Vary the widths — identical bars read as a table rather than a paragraph.

<div class="card" style="max-width:420px">
    <div class="card-head">
        <div class="skeleton skeleton-avatar"></div>
        <div class="skeleton skeleton-title" style="width:40%"></div>
    </div>
    <div class="card-body">
        <div class="skeleton skeleton-text"></div>
        <div class="skeleton skeleton-text" style="width:88%"></div>
        <div class="skeleton skeleton-text" style="width:64%"></div>
    </div>
</div>

List rows

The real .list markup with placeholders where the content goes, so the rows are exactly the height the rows will be. .skeleton-badge is the pill a row ends with.

<div class="card" style="max-width:460px" aria-busy="true">
    <ul class="list">
        <li><span class="list-row">
            <span class="skeleton skeleton-avatar"></span>
            <span class="list-main">
                <span class="skeleton skeleton-text" style="width:55%; margin:0 0 6px"></span>
                <span class="skeleton skeleton-text" style="width:80%; margin:0"></span>
            </span>
            <span class="skeleton skeleton-badge"></span>
        </span></li>
        <li><span class="list-row">
            <span class="skeleton skeleton-avatar"></span>
            <span class="list-main">
                <span class="skeleton skeleton-text" style="width:40%; margin:0 0 6px"></span>
                <span class="skeleton skeleton-text" style="width:70%; margin:0"></span>
            </span>
            <span class="skeleton skeleton-badge"></span>
        </span></li>
        <li><span class="list-row">
            <span class="skeleton skeleton-avatar"></span>
            <span class="list-main">
                <span class="skeleton skeleton-text" style="width:62%; margin:0 0 6px"></span>
                <span class="skeleton skeleton-text" style="width:48%; margin:0"></span>
            </span>
            <span class="skeleton skeleton-badge"></span>
        </span></li>
    </ul>
</div>

Table rows

The header is real — the reader knows the columns before the rows — and each cell holds one line sized to what the column usually holds. Three rows are enough to say “a table”; a screen of them says “broken”.

OrderCustomerReservationValue
<div class="card" style="max-width:560px">
    <table class="table" aria-busy="true">
        <thead>
            <tr><th>Order</th><th>Customer</th><th>Reservation</th><th class="col-num">Value</th></tr>
        </thead>
        <tbody>
            <tr>
                <td><span class="skeleton skeleton-text" style="width:64px"></span></td>
                <td><span class="skeleton skeleton-text" style="width:96px"></span></td>
                <td><span class="skeleton skeleton-text" style="width:140px"></span></td>
                <td class="col-num"><span class="skeleton skeleton-text" style="width:56px; margin-inline-start:auto"></span></td>
            </tr>
            <tr>
                <td><span class="skeleton skeleton-text" style="width:64px"></span></td>
                <td><span class="skeleton skeleton-text" style="width:120px"></span></td>
                <td><span class="skeleton skeleton-text" style="width:110px"></span></td>
                <td class="col-num"><span class="skeleton skeleton-text" style="width:48px; margin-inline-start:auto"></span></td>
            </tr>
            <tr>
                <td><span class="skeleton skeleton-text" style="width:64px"></span></td>
                <td><span class="skeleton skeleton-text" style="width:84px"></span></td>
                <td><span class="skeleton skeleton-text" style="width:160px"></span></td>
                <td class="col-num"><span class="skeleton skeleton-text" style="width:60px; margin-inline-start:auto"></span></td>
            </tr>
        </tbody>
    </table>
</div>

A form

.skeleton-control is one control height — a field or a button — with .skeleton-control--sm and .skeleton-control--lg for the other tiers, so a form of placeholders lays out on the same grid as the form it stands in for.

<div class="card" style="max-width:520px" aria-busy="true">
    <div class="card-body sedna-col sedna-gap-3">
        <div class="sedna-grid sedna-grid--sm sedna-grid--fit">
            <div class="form-field">
                <span class="skeleton skeleton-text" style="width:38%; margin:0 0 8px"></span>
                <span class="skeleton skeleton-control"></span>
            </div>
            <div class="form-field">
                <span class="skeleton skeleton-text" style="width:30%; margin:0 0 8px"></span>
                <span class="skeleton skeleton-control"></span>
            </div>
        </div>
        <div class="form-field">
            <span class="skeleton skeleton-text" style="width:22%; margin:0 0 8px"></span>
            <span class="skeleton skeleton-block skeleton-block--sm"></span>
        </div>
        <div class="form-actions">
            <span class="skeleton skeleton-control" style="width:5rem"></span>
            <span class="skeleton skeleton-control" style="width:7rem"></span>
        </div>
    </div>
</div>

Stat tiles

Label, value, delta: the value is a .skeleton-title given the value's height, because the figure is what the reader is waiting for and its box is the one that must not move.

<div class="card" style="max-width:680px" aria-busy="true">
    <div class="card-body">
        <div class="stat-row">
            <div class="stat">
                <span class="skeleton skeleton-text" style="width:60%; margin:0 0 6px"></span>
                <span class="skeleton skeleton-title" style="width:40%; height:28px; margin:0 0 6px"></span>
                <span class="skeleton skeleton-text" style="width:70%; margin:0"></span>
            </div>
            <div class="stat">
                <span class="skeleton skeleton-text" style="width:50%; margin:0 0 6px"></span>
                <span class="skeleton skeleton-title" style="width:36%; height:28px; margin:0 0 6px"></span>
                <span class="skeleton skeleton-text" style="width:45%; margin:0"></span>
            </div>
            <div class="stat">
                <span class="skeleton skeleton-text" style="width:66%; margin:0 0 6px"></span>
                <span class="skeleton skeleton-title" style="width:48%; height:28px; margin:0 0 6px"></span>
                <span class="skeleton skeleton-text" style="width:60%; margin:0"></span>
            </div>
        </div>
    </div>
</div>

A chart

.skeleton-bars is a row of bars at different heights — each is --bar — so the box reads as a chart and not as a paragraph. --skeleton-height is the box's height, as it is for a block.

<div class="card" style="max-width:460px" aria-busy="true">
    <div class="card-head">
        <span class="skeleton skeleton-title" style="width:45%"></span>
    </div>
    <div class="card-body">
        <div class="skeleton-bars">
            <span class="skeleton" style="--bar: 35%"></span>
            <span class="skeleton" style="--bar: 55%"></span>
            <span class="skeleton" style="--bar: 42%"></span>
            <span class="skeleton" style="--bar: 78%"></span>
            <span class="skeleton" style="--bar: 60%"></span>
            <span class="skeleton" style="--bar: 90%"></span>
            <span class="skeleton" style="--bar: 68%"></span>
            <span class="skeleton" style="--bar: 100%"></span>
        </div>
    </div>
</div>

A picture, and a circle of any size

.skeleton-image is a 16:9 box for a picture that is loading, and .skeleton-circle a circle of --skeleton-size — an avatar in another tier, a status dot, a logo tile.

<div class="sedna-grid" style="max-width:640px" aria-busy="true">
    <div class="card">
        <div class="skeleton skeleton-image" style="border-radius: var(--radius-surface) var(--radius-surface) 0 0"></div>
        <div class="card-body">
            <span class="skeleton skeleton-title" style="width:70%"></span>
            <span class="skeleton skeleton-text"></span>
            <span class="skeleton skeleton-text" style="width:60%"></span>
        </div>
    </div>
    <div class="card">
        <div class="card-body sedna-row sedna-gap-3">
            <span class="skeleton skeleton-circle" style="--skeleton-size: 48px"></span>
            <span class="sedna-fill">
                <span class="skeleton skeleton-text" style="width:50%"></span>
                <span class="skeleton skeleton-text" style="width:80%"></span>
            </span>
        </div>
    </div>
</div>

A surface that has not arrived

.skeleton-block is the shape of a surface — a card of settings, a table of rows, a graph. It is 8rem, with .skeleton-block--sm at 4rem for a field or a row and .skeleton-block--lg at 12rem for a document or a table; between the steps, set --skeleton-height, as the third card does.

Open tasks

Recent notes

Relationship graph

Audit log

<div class="sedna-grid">
    <div class="card">
        <div class="card-head"><h3>Open tasks</h3></div>
        <div class="card-body">
            <div class="skeleton skeleton-block skeleton-block--sm"></div>
        </div>
    </div>
    <div class="card">
        <div class="card-head"><h3>Recent notes</h3></div>
        <div class="card-body">
            <div class="skeleton skeleton-block"></div>
        </div>
    </div>
    <div class="card">
        <div class="card-head"><h3>Relationship graph</h3></div>
        <div class="card-body">
            <div class="skeleton skeleton-block" style="--skeleton-height: 6.5rem"></div>
        </div>
    </div>
    <div class="card">
        <div class="card-head"><h3>Audit log</h3></div>
        <div class="card-body">
            <div class="skeleton skeleton-block skeleton-block--lg"></div>
        </div>
    </div>
</div>