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

Empty states

tier 2 — classes

What a view shows when it has nothing to show, and what it shows while it is waiting. Say which of those it is: they call for different actions. Something still happening — a connection, a log being written, a job running — is a live state.

The drawing above each block is state art: thirteen shipped, one per state, referenced by id. That page has the set, the sizes, the accents and the motion.

Or an icon, when what is missing has one

State icon

.state-icon on a Remix Icon <i> takes the drawing's place, with the same title, sentence and actions under it. Use it where the thing that is missing has a glyph of its own — invoices, attachments, members — and the drawing where the state is what matters. Pick the glyph for that thing: one inbox on every empty panel says nothing the title does not. .empty-state--failed turns it to the danger colour.

No invoices yet

An invoice appears here once an order has been delivered and billed.

No attachments on this order

Upload the delivery note and the signed contract, or ask the customer to send them.

Nobody is assigned to this queue

Orders in it wait until somebody is. A team lead can assign members.

Could not load the attachments

The request did not complete. Nothing has been changed.

GET /api/orders/10482/files · 503 · 12:04:31
<div class="sedna-col sedna-gap-2" style="max-width:520px">
    <div class="card">
        <div class="empty-state">
            <i class="ri-file-list-3-line state-icon" aria-hidden="true"></i>
            <span class="empty-state-title">No invoices yet</span>
            <p>An invoice appears here once an order has been delivered and billed.</p>
            <button class="btn btn-sm btn-primary"><i class="ri-add-line"></i> Create an invoice</button>
        </div>
    </div>
    <div class="card">
        <div class="empty-state">
            <i class="ri-attachment-2 state-icon" aria-hidden="true"></i>
            <span class="empty-state-title">No attachments on this order</span>
            <p>Upload the delivery note and the signed contract, or ask the customer to send them.</p>
            <div class="empty-state-actions">
                <button class="btn btn-sm btn-primary"><i class="ri-upload-2-line"></i> Upload</button>
                <button class="btn btn-sm"><i class="ri-mail-line"></i> Ask the customer</button>
            </div>
        </div>
    </div>
    <div class="card">
        <div class="empty-state">
            <i class="ri-team-line state-icon" aria-hidden="true"></i>
            <span class="empty-state-title">Nobody is assigned to this queue</span>
            <p>Orders in it wait until somebody is. A team lead can assign members.</p>
        </div>
    </div>
    <div class="card">
        <div class="empty-state empty-state--failed">
            <i class="ri-error-warning-line state-icon" aria-hidden="true"></i>
            <span class="empty-state-title">Could not load the attachments</span>
            <p>The request did not complete. Nothing has been changed.</p>
            <span class="empty-state-detail">GET /api/orders/10482/files &middot; 503 &middot; 12:04:31</span>
            <button class="btn btn-sm"><i class="ri-refresh-line"></i> Try again</button>
        </div>
    </div>
</div>

Icon sizes

The same three places as the drawing: .state-icon--sm in a table row or a narrow panel, the bare .state-icon in a card, and .state-icon--lg in .empty-state--page.

ShipmentCarrierParcels
No shipments for this order

A shipment appears once the warehouse has packed the order.

No shipments for this order

The order is packed and waiting for a carrier.

Shipments No shipments for this order

The order is packed and waiting for a carrier. Book one here, or hand it to the customer for collection.

<div class="sedna-col sedna-gap-2" style="max-width:640px">

    <table class="table">
        <thead>
            <tr><th>Shipment</th><th>Carrier</th><th class="col-num">Parcels</th></tr>
        </thead>
        <tbody>
            <tr>
                <td colspan="3">
                    <div class="empty-state empty-state--bare">
                        <i class="ri-truck-line state-icon state-icon--sm" aria-hidden="true"></i>
                        <span class="empty-state-title">No shipments for this order</span>
                        <p>A shipment appears once the warehouse has packed the order.</p>
                    </div>
                </td>
            </tr>
        </tbody>
    </table>

    <div class="card">
        <div class="empty-state">
            <i class="ri-truck-line state-icon" aria-hidden="true"></i>
            <span class="empty-state-title">No shipments for this order</span>
            <p>The order is packed and waiting for a carrier.</p>
            <button class="btn btn-sm btn-primary"><i class="ri-add-line"></i> Book a shipment</button>
        </div>
    </div>

    <div class="card">
        <div class="empty-state empty-state--page">
            <i class="ri-truck-line state-icon state-icon--lg" aria-hidden="true"></i>
            <span class="empty-state-code">Shipments</span>
            <span class="empty-state-title">No shipments for this order</span>
            <p>The order is packed and waiting for a carrier. Book one here, or hand it to the
            customer for collection.</p>
            <div class="empty-state-actions">
                <button class="btn btn-sm btn-primary"><i class="ri-add-line"></i> Book a shipment</button>
                <button class="btn btn-sm"><i class="ri-calendar-line"></i> Schedule a collection</button>
            </div>
        </div>
    </div>

</div>

Empty and waiting

Empty

The kinds of empty are not interchangeable: nothing yet is an invitation, nothing found needs the query echoed back, filtered to nothing needs the filters named and a way to clear them, no access needs to say who can grant it, and a cleared queue has to look like success. Give the one action that resolves it as a .btn-sm, inside a .card for a panel or .empty-state--bare straight on the page.

No orders yet

The first order will appear here as soon as one is placed.

Nothing matches “eu-wets”

Check the spelling, or clear the search to see all 1,204 orders.

No orders match these filters

Three filters are applied. Removing the date range would show 84 orders.

You cannot see this region

Regional figures are limited to account leads. Ask an administrator for the orders.read role.

The queue is clear

Every order in the queue has been decided. The next one will arrive here.

<div class="sedna-col sedna-gap-2" style="max-width:520px">
    <div class="card">
        <div class="empty-state">
            <svg class="state-art" aria-hidden="true"><use href="#nothing-yet" /></svg>
            <span class="empty-state-title">No orders yet</span>
            <p>The first order will appear here as soon as one is placed.</p>
        </div>
    </div>
    <div class="card">
        <div class="empty-state">
            <svg class="state-art" aria-hidden="true"><use href="#no-results" /></svg>
            <span class="empty-state-title">Nothing matches &ldquo;eu-wets&rdquo;</span>
            <p>Check the spelling, or clear the search to see all 1,204 orders.</p>
            <button class="btn btn-sm"><i class="ri-close-circle-line"></i> Clear search</button>
        </div>
    </div>
    <div class="card">
        <div class="empty-state">
            <svg class="state-art" aria-hidden="true"><use href="#filtered-out" /></svg>
            <span class="empty-state-title">No orders match these filters</span>
            <p>Three filters are applied. Removing the date range would show 84 orders.</p>
            <button class="btn btn-sm"><i class="ri-filter-off-line"></i> Clear filters</button>
        </div>
    </div>
    <div class="card">
        <div class="empty-state">
            <svg class="state-art" aria-hidden="true"><use href="#no-access" /></svg>
            <span class="empty-state-title">You cannot see this region</span>
            <p>Regional figures are limited to account leads. Ask an administrator for the
            <code>orders.read</code> role.</p>
        </div>
    </div>
    <div class="card">
        <div class="empty-state">
            <svg class="state-art" aria-hidden="true"><use href="#all-done" /></svg>
            <span class="empty-state-title">The queue is clear</span>
            <p>Every order in the queue has been decided. The next one will arrive here.</p>
        </div>
    </div>
</div>

Waiting, and failed

.empty-state--pending is a wait with nothing to report, so put a .spinner-lg where the drawing goes — use .progress where there is progress, a .skeleton where the shape of what is coming is known, and the #waiting drawing where the reader can be told and then leave. .empty-state--failed moves --state-accent into the danger ramp and leaves the line work grey; put the endpoint, the code and the time it was tried in .empty-state-detail, and keep #failed and #offline apart — only a request that never left is worth retrying immediately.

Waiting for the first event

The stream is connected. Nothing has come through yet.

Queued behind two other exports

This report starts once the exports ahead of it finish, at about 12:20. You can leave the page.

Could not load the orders

The request did not complete. Nothing has been changed.

GET /api/orders · 504 · 12:04:31
You are offline

The request never left this device. What you have already decided is saved and will be sent when the connection is back.

<div class="sedna-col sedna-gap-2" style="max-width:520px">
    <div class="card">
        <div class="empty-state empty-state--pending">
            <span class="spinner spinner-lg"></span>
            <span class="empty-state-title">Waiting for the first event</span>
            <p>The stream is connected. Nothing has come through yet.</p>
        </div>
    </div>
    <div class="card">
        <div class="empty-state">
            <svg class="state-art" aria-hidden="true"><use href="#waiting" /></svg>
            <span class="empty-state-title">Queued behind two other exports</span>
            <p>This report starts once the exports ahead of it finish, at about 12:20. You can
            leave the page.</p>
        </div>
    </div>
    <div class="card">
        <div class="empty-state empty-state--failed">
            <svg class="state-art" aria-hidden="true"><use href="#failed" /></svg>
            <span class="empty-state-title">Could not load the orders</span>
            <p>The request did not complete. Nothing has been changed.</p>
            <span class="empty-state-detail">GET /api/orders &middot; 504 &middot; 12:04:31</span>
            <button class="btn btn-sm"><i class="ri-refresh-line"></i> Try again</button>
        </div>
    </div>
    <div class="card">
        <div class="empty-state empty-state--failed">
            <svg class="state-art" aria-hidden="true"><use href="#offline" /></svg>
            <span class="empty-state-title">You are offline</span>
            <p>The request never left this device. What you have already decided is saved and
            will be sent when the connection is back.</p>
            <button class="btn btn-sm"><i class="ri-refresh-line"></i> Try again</button>
        </div>
    </div>
</div>

When the whole page is the state

.empty-state--page is the same block at page scale: a bigger drawing (.state-art--lg), a larger title, and a wider measure. .empty-state-code is the status code above the title, small and monospace; .empty-state-actions is the wrapped, centred row for one primary action and the one or two beside it, and takes the top margin off the buttons.

Common pages has the finished ones — access denied, not found, the error, the expired session, the maintenance window — with the shell each belongs in.