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

State art

tier 2 — classes

The drawing that says which state a view is in, before the sentence is read. Thirteen of them ship with the package, one per state; an app adds its own with the same classes. Where the thing that is missing has a glyph of its own, the state icon takes the drawing's place.

Putting it on a page

<SednaStateArt /> once, at the top of <body> in the host page, writes the sprite into the document; every page then references a drawing by id with a same-document <use>. It is in the page and not a file on purpose — a <use> pointing at an external file is rendered as a document of its own in WebKit, with no inherited colour and no custom properties, so every line came out black — and it arrives in the first server-rendered paint, which matters most on the pages that show when something is broken. aria-hidden, because the drawing repeats what the title says.

<!-- Once, at the top of <body> in the host page. It writes the sprite into the page. -->
<SednaStateArt />

<!-- Anywhere on any page: -->
<svg class="state-art state-art--lg" aria-hidden="true">
    <use href="#no-access" />
</svg>

The thirteen states

Pick by what happened: #nothing-yet and #all-done are both an empty list and are opposites; #failed came back wrong and #offline never left; #no-results searched everything and #filtered-out excluded it. Reusing one drawing across two states makes them look like the same page to somebody scanning a screenshot in a ticket.

nothing-yet nothing has been created
no-results searched, no match
filtered-out the filters excluded it all
no-access it exists and is shut
failed it came back wrong
offline it never left
waiting accepted and queued
all-done cleared, not broken
first-run nothing set up yet
not-found 404, the address is wrong
server-error 500, the machine threw
session-expired time ran out
maintenance planned, and it comes back
<div class="sedna-row-wrap sedna-gap-3" style="row-gap: var(--space-9)">
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art" aria-hidden="true"><use href="#nothing-yet" /></svg>
        <code>nothing-yet</code>
        <span class="text-muted text-sm text-center">nothing has been created</span>
    </div>
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art" aria-hidden="true"><use href="#no-results" /></svg>
        <code>no-results</code>
        <span class="text-muted text-sm text-center">searched, no match</span>
    </div>
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art" aria-hidden="true"><use href="#filtered-out" /></svg>
        <code>filtered-out</code>
        <span class="text-muted text-sm text-center">the filters excluded it all</span>
    </div>
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art" aria-hidden="true"><use href="#no-access" /></svg>
        <code>no-access</code>
        <span class="text-muted text-sm text-center">it exists and is shut</span>
    </div>
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art" aria-hidden="true"><use href="#failed" /></svg>
        <code>failed</code>
        <span class="text-muted text-sm text-center">it came back wrong</span>
    </div>
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art" aria-hidden="true"><use href="#offline" /></svg>
        <code>offline</code>
        <span class="text-muted text-sm text-center">it never left</span>
    </div>
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art" aria-hidden="true"><use href="#waiting" /></svg>
        <code>waiting</code>
        <span class="text-muted text-sm text-center">accepted and queued</span>
    </div>
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art" aria-hidden="true"><use href="#all-done" /></svg>
        <code>all-done</code>
        <span class="text-muted text-sm text-center">cleared, not broken</span>
    </div>
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art" aria-hidden="true"><use href="#first-run" /></svg>
        <code>first-run</code>
        <span class="text-muted text-sm text-center">nothing set up yet</span>
    </div>
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art" aria-hidden="true"><use href="#not-found" /></svg>
        <code>not-found</code>
        <span class="text-muted text-sm text-center">404, the address is wrong</span>
    </div>
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art" aria-hidden="true"><use href="#server-error" /></svg>
        <code>server-error</code>
        <span class="text-muted text-sm text-center">500, the machine threw</span>
    </div>
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art" aria-hidden="true"><use href="#session-expired" /></svg>
        <code>session-expired</code>
        <span class="text-muted text-sm text-center">time ran out</span>
    </div>
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art" aria-hidden="true"><use href="#maintenance" /></svg>
        <code>maintenance</code>
        <span class="text-muted text-sm text-center">planned, and it comes back</span>
    </div>
</div>

Three sizes

.state-art--sm (56px) for a row inside a table or a narrow panel, the bare .state-art (96px) for a card, and .state-art--lg (152px) for a page that is nothing but this — see Common pages. There is no step below 56px; where there is less room than that, use the sentence on its own.

ReferenceCustomerValue
No orders match these filters

Removing the date range would show 84 orders.

No orders match these filters

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

No results No orders match these filters

Three filters are applied to 1,204 orders. Removing the date range would show 84 of them.

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

    <table class="table">
        <thead>
            <tr><th>Reference</th><th>Customer</th><th class="col-num">Value</th></tr>
        </thead>
        <tbody>
            <tr>
                <td colspan="3">
                    <div class="empty-state empty-state--bare">
                        <svg class="state-art state-art--sm" aria-hidden="true"><use href="#filtered-out" /></svg>
                        <span class="empty-state-title">No orders match these filters</span>
                        <p>Removing the date range would show 84 orders.</p>
                    </div>
                </td>
            </tr>
        </tbody>
    </table>

    <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 empty-state--page">
            <svg class="state-art state-art--lg" aria-hidden="true"><use href="#filtered-out" /></svg>
            <span class="empty-state-code">No results</span>
            <span class="empty-state-title">No orders match these filters</span>
            <p>Three filters are applied to 1,204 orders. Removing the date range would show 84
            of them.</p>
            <div class="empty-state-actions">
                <button class="btn btn-sm btn-primary"><i class="ri-filter-off-line"></i> Clear filters</button>
                <button class="btn btn-sm"><i class="ri-calendar-line"></i> Widen the date range</button>
            </div>
        </div>
    </div>

</div>

The accent, by meaning

Every drawing has one accent — the part that says what happened — and it is --state-accent, the brand colour by default. .state-art--go, --warn, --danger and --info point it at the same families the badges and buttons use, and .empty-state--failed points it at danger for everything inside. The line work stays muted in all of them: a whole green drawing reads as an illustration of green, not of a cleared queue. Switch the theme in the header and the drawings follow; nothing here names a colour.

.state-art the brand, by default
.state-art--go a cleared queue
.state-art--warn a role to ask for
.state-art--danger a machine that failed
.state-art--info something to try
<div class="sedna-row-wrap sedna-gap-3" style="row-gap: var(--space-9)">
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art" aria-hidden="true"><use href="#all-done" /></svg>
        <code>.state-art</code>
        <span class="text-muted text-sm text-center">the brand, by default</span>
    </div>
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art state-art--go" aria-hidden="true"><use href="#all-done" /></svg>
        <code>.state-art--go</code>
        <span class="text-muted text-sm text-center">a cleared queue</span>
    </div>
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art state-art--warn" aria-hidden="true"><use href="#no-access" /></svg>
        <code>.state-art--warn</code>
        <span class="text-muted text-sm text-center">a role to ask for</span>
    </div>
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art state-art--danger" aria-hidden="true"><use href="#server-error" /></svg>
        <code>.state-art--danger</code>
        <span class="text-muted text-sm text-center">a machine that failed</span>
    </div>
    <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
        <svg class="state-art state-art--info" aria-hidden="true"><use href="#first-run" /></svg>
        <code>.state-art--info</code>
        <span class="text-muted text-sm text-center">something to try</span>
    </div>
</div>

The part that moves

Seven of the drawings have a part that can move, and it is paused until the page says otherwise: .state-art--live runs one drawing, and .empty-state--pending runs the drawing inside it — the card at the end — because a wait is the one state where “still happening” is the message. Everything else stays still: a drawing beside a sentence is read, and a movement beside it competes with the reading. Every animation's first frame is the resting pose, so a paused drawing is the still drawing, and prefers-reduced-motion switches all of them off, last.

nothing-yet the first item arriving
no-results the magnifier sweeping
waiting the arrow at the free slot
server-error the fault light
session-expired the clock ticking
first-run the sparkle breathing
offline the signal that is not there
offline the same, without --live
Queued behind two other exports

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

<div class="sedna-col sedna-gap-3">
    <div class="sedna-row-wrap sedna-gap-3" style="row-gap: var(--space-9)">
        <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
            <svg class="state-art state-art--live" aria-hidden="true"><use href="#nothing-yet" /></svg>
            <code>nothing-yet</code>
            <span class="text-muted text-sm text-center">the first item arriving</span>
        </div>
        <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
            <svg class="state-art state-art--live" aria-hidden="true"><use href="#no-results" /></svg>
            <code>no-results</code>
            <span class="text-muted text-sm text-center">the magnifier sweeping</span>
        </div>
        <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
            <svg class="state-art state-art--live" aria-hidden="true"><use href="#waiting" /></svg>
            <code>waiting</code>
            <span class="text-muted text-sm text-center">the arrow at the free slot</span>
        </div>
        <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
            <svg class="state-art state-art--live state-art--danger" aria-hidden="true"><use href="#server-error" /></svg>
            <code>server-error</code>
            <span class="text-muted text-sm text-center">the fault light</span>
        </div>
        <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
            <svg class="state-art state-art--live" aria-hidden="true"><use href="#session-expired" /></svg>
            <code>session-expired</code>
            <span class="text-muted text-sm text-center">the clock ticking</span>
        </div>
        <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
            <svg class="state-art state-art--live" aria-hidden="true"><use href="#first-run" /></svg>
            <code>first-run</code>
            <span class="text-muted text-sm text-center">the sparkle breathing</span>
        </div>
        <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
            <svg class="state-art state-art--live" aria-hidden="true"><use href="#offline" /></svg>
            <code>offline</code>
            <span class="text-muted text-sm text-center">the signal that is not there</span>
        </div>
        <div class="sedna-col sedna-center sedna-gap-1" style="width:150px">
            <svg class="state-art" aria-hidden="true"><use href="#offline" /></svg>
            <code>offline</code>
            <span class="text-muted text-sm text-center">the same, without <code>--live</code></span>
        </div>
    </div>

    <div class="card" style="max-width:420px">
        <div class="empty-state empty-state--pending">
            <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>

Drawing your own

An app's own drawing

Put a <symbol> in a hidden <svg class="sedna-sprite"> anywhere in the page — the layout, the page itself — and reference it exactly as a shipped one. The classes are the drawing: .sedna-art-line for the line work, .sedna-art-soft for what is there but not the point, .sedna-art-dash for what is absent or expected, .sedna-art-accent (and .sedna-art-accent-fill) for the one part that says what happened, .sedna-art-tint for a faint wash behind a shape, and .sedna-art-knockout for a shape that has to cover the line behind it and takes the colour of whatever surface the drawing sits on. Sizing, colour, the accent families and the motion gating are all the classes', so the paths are the only thing you write.

Draw on the same 120-unit grid, with nothing thinner than the 3-unit stroke the classes set and no gap under 6 units — the smallest size renders at 56px, and detail below that fills in. One accent per scene. A part that moves takes one of the motion classes, .sedna-art-bob, .sedna-art-sweep, .sedna-art-nudge, .sedna-art-blink, .sedna-art-tick or .sedna-art-twinkle, and rests until .state-art--live says otherwise. Prefix the id with something of your own, so it can never collide with a state that ships later.

No invoices for this period

Nothing was billed between 1 and 31 August. The first invoice appears here once a delivery is confirmed.

<svg class="sedna-sprite" aria-hidden="true" focusable="false">
    <symbol id="ex-no-invoices" viewBox="0 0 120 120">
        <path class="sedna-art-tint" d="M30 16h40l18 18v64a6 6 0 0 1-6 6H30a6 6 0 0 1-6-6V22a6 6 0 0 1 6-6z" />
        <path class="sedna-art-soft" d="M30 16h40l18 18v64a6 6 0 0 1-6 6H30a6 6 0 0 1-6-6V22a6 6 0 0 1 6-6z" />
        <path class="sedna-art-soft" d="M70 16v18h18" />
        <path class="sedna-art-soft" d="M38 50h32M38 62h32M38 74h20" />
        <circle class="sedna-art-knockout" cx="86" cy="86" r="24" />
        <g class="sedna-art-bob">
            <circle class="sedna-art-accent" cx="86" cy="86" r="18" />
            <path class="sedna-art-accent" d="M78 86h16" />
        </g>
    </symbol>
</svg>

<div class="card" style="max-width:420px">
    <div class="empty-state">
        <svg class="state-art state-art--live" aria-hidden="true"><use href="#ex-no-invoices" /></svg>
        <span class="empty-state-title">No invoices for this period</span>
        <p>Nothing was billed between 1 and 31 August. The first invoice appears here once a
        delivery is confirmed.</p>
        <button class="btn btn-sm"><i class="ri-calendar-line"></i> Change the period</button>
    </div>
</div>
Forced colours and print need nothing from you Under Windows high contrast the tokens the classes read are remapped, so the line work is CanvasText, the accent Highlight and a knockout Canvas; on paper the drawing prints as it stands, in the light palette. The sprite writes no colour, which is what makes both true without a second file.