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

Cards

tier 2 — classes

Three parts, all optional except the outer one: .card holds the border and clips its children, .card-head is the title row, .card-body is the padded content. There is no card component and no variant parameter — for a variant, write the markup you need and take any colour from a token. All three share one gutter, --card-pad-block / --card-pad-inline, which data-density="compact" tightens — so a dense card list needs no rules of its own.

A card declares --surface-here — the colour of the surface its content is sitting on. Anything that has to be opaque reads it and is right without naming a surface (--timeline-surface, --group-head-bg, --scroll-bg); on a surface your app paints, declare it once on that element and everything inside follows.

Head and body

Group multiple right-hand items in .card-head-right so they share a gap.

Reconciliation
Enabled

Nightly maintenance: prunes stale orders and trims the run history.

Last run2026-07-30 03:15
Duration4.2 s
Rows removed1 284
<div class="card">
    <div class="card-head">
        <strong>Reconciliation</strong>
        <div class="card-head-right">
            <span class="badge badge-go">Enabled</span>
            <button class="btn btn-sm">Run now</button>
        </div>
    </div>
    <div class="card-body">
        <p class="form-hint" style="margin-bottom:10px">
            Nightly maintenance: prunes stale orders and trims the run history.
        </p>
        <div class="kv"><span class="k">Last run</span><span class="v">2026-07-30 03:15</span></div>
        <div class="kv"><span class="k">Duration</span><span class="v">4.2 s</span></div>
        <div class="kv"><span class="k">Rows removed</span><span class="v">1 284</span></div>
    </div>
</div>

Body only

Drop the head when the content names itself.

No pending approvals

Everything in the queue has been decided. New requests appear here automatically.

<div class="card">
    <div class="card-body">
        <h2 style="margin:0 0 4px; font-size:14px">No pending approvals</h2>
        <p class="form-hint">Everything in the queue has been decided. New requests appear here automatically.</p>
    </div>
</div>

Body without padding

.card-body--flush drops the padding, for content drawn edge to edge inside the card — a canvas, a chart, a map. It is the sibling of .list-row--flush.

Relationships

The block above reaches the card's own edges. How tall it is stays the content's decision; the class only takes the padding away.

<div class="card" style="max-width:420px">
    <div class="card-head"><h3>Relationships</h3></div>
    <div class="card-body card-body--flush" style="height:9rem">
        <div class="skeleton" style="height:100%; border-radius:0">
        </div>
    </div>
    <div class="card-body">
        <p class="form-hint">
            The block above reaches the card's own edges. How tall it is stays the content's
            decision; the class only takes the padding away.
        </p>
    </div>
</div>

A card in a card in a card

.card takes --surface-raised-1, a card inside one takes -2 and a card inside that takes -3, with nothing in the markup saying which level it is; a fourth level stays at the third. The levels resolve through the theme's own base ramp, so Graphite takes all three neutral.

Run 4 812 1 step failed
Started08:14:02
Extract

27 sources, two of them slower than their timeout allows.

src-db-14

Timed out after 30 s. Retried once, then skipped — the hint you are reading is the level this ladder stops at.

<div class="card">
    <div class="card-head">
        <strong>Run 4 812</strong>
        <span class="badge badge-warn">1 step failed</span>
    </div>
    <div class="card-body sedna-col sedna-gap-2">
        <div class="kv"><span class="k">Started</span><span class="v">08:14:02</span></div>

        <div class="card">
            <div class="card-head"><strong>Extract</strong></div>
            <div class="card-body sedna-col sedna-gap-2">
                <p class="form-hint" style="margin:0">
                    27 sources, two of them slower than their timeout allows.
                </p>

                <div class="card">
                    <div class="card-body">
                        <strong>src-db-14</strong>
                        <p class="form-hint" style="margin:6px 0 0">
                            Timed out after 30 s. Retried once, then skipped — the hint you are
                            reading is the level this ladder stops at.
                        </p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

Head, body and foot

.card-foot is the row a card ends with — the actions that decide what happens to the content above them. Use it instead of a .card-body holding .form-actions, and put a note or a destructive action on the far side with .card-foot--split.

Merge two tags

The cataloguer found postgres and postgresql on 41 records between them. Approving keeps the first and retags the rest.

Raised bycataloguer
Confidence0.94
Nightly reconciliation

Prunes stale orders and trims the run history.

Last run 03:15, 4.2 s
<div class="sedna-col sedna-gap-2">
    <div class="card">
        <div class="card-head">
            <strong>Merge two tags</strong>
        </div>
        <div class="card-body">
            <p class="form-hint" style="margin-bottom:10px">
                The cataloguer found <code>postgres</code> and <code>postgresql</code> on 41 records
                between them. Approving keeps the first and retags the rest.
            </p>
            <div class="kv"><span class="k">Raised by</span><span class="v">cataloguer</span></div>
            <div class="kv"><span class="k">Confidence</span><span class="v">0.94</span></div>
        </div>
        <div class="card-foot">
            <input class="form-input form-input-sm" style="max-width:260px" type="text"
                   placeholder="Why, in one line" aria-label="Reason" />
            <button class="btn btn-sm">Decline</button>
            <button class="btn btn-go btn-sm">Approve</button>
        </div>
    </div>

    <div class="card">
        <div class="card-body">
            <strong>Nightly reconciliation</strong>
            <p class="form-hint" style="margin:6px 0 0">
                Prunes stale orders and trims the run history.
            </p>
        </div>
        <div class="card-foot card-foot--split">
            <span class="form-hint">Last run 03:15, 4.2 s</span>
            <button class="btn btn-sm">Run now</button>
        </div>
    </div>
</div>

A foot read left to right

.card-foot--start aligns the row to the leading edge, for a work card rather than a form: the primary action first and its secondaries after it, with one trailing control pushed to the far end by .sedna-push. It is also what a status line in place of the actions needs — right-aligned text reads as a mistake. Use --split only for two groups, because it spreads whatever it holds across the whole card.

Credit note for order 10482

Northwind Retail returned four of six items. The refund waits on this.

Credit note for order 10488

Same queue, already taken by somebody else.

View only — claimed by Alex Fischer
<div class="sedna-col sedna-gap-2" style="max-width:520px">
    <div class="card">
        <div class="card-body">
            <strong>Credit note for order 10482</strong>
            <p class="form-hint" style="margin:6px 0 0">
                Northwind Retail returned four of six items. The refund waits on this.
            </p>
        </div>
        <div class="card-foot card-foot--start">
            <button class="btn btn-go btn-sm">Approve</button>
            <button class="btn btn-sm">Send back</button>
            <button class="btn btn-ghost btn-sm sedna-push" aria-label="More actions">
                <i class="ri-more-2-fill"></i>
            </button>
        </div>
    </div>

    <div class="card">
        <div class="card-body">
            <strong>Credit note for order 10488</strong>
            <p class="form-hint" style="margin:6px 0 0">
                Same queue, already taken by somebody else.
            </p>
        </div>
        <div class="card-foot card-foot--start">
            <span class="text-muted">
                <i class="ri-lock-line"></i> View only — claimed by Alex Fischer
            </span>
        </div>
    </div>
</div>

Sections inside one body

.card-section divides sub-sections of one body with a hairline and the card's own spacing. The rule is drawn only above a section that has something before it, so a section you do not render takes its divider with it. Prefer it to a second .card-body, which ends the first body's layout at the seam, and to a .divider, whose margins are sized for a page.

Reservation 10482
CustomerNorthwind Retail
Held until2026-08-02 18:00
Attachments

Two files, both added when the hold was raised.

History

Claimed by Alex Fischer, four minutes ago.

<div class="card" style="max-width:520px">
    <div class="card-head"><strong>Reservation 10482</strong></div>
    <div class="card-body">
        <div class="card-section">
            <div class="kv"><span class="k">Customer</span><span class="v">Northwind Retail</span></div>
            <div class="kv"><span class="k">Held until</span><span class="v">2026-08-02 18:00</span></div>
        </div>
        <div class="card-section">
            <strong>Attachments</strong>
            <p class="form-hint" style="margin:6px 0 0">
                Two files, both added when the hold was raised.
            </p>
        </div>
        <div class="card-section">
            <strong>History</strong>
            <p class="form-hint" style="margin:6px 0 0">
                Claimed by Alex Fischer, four minutes ago.
            </p>
        </div>
    </div>
</div>

A table in a card

No .card-body — the table brings its own cell padding.

Recent decisions 3
OrderActionByWhen
ORD-4209ApprovedAlex Fischer4 min ago
ORD-4204Taken overJ. Weber22 min ago
ORD-4198RejectedAlex Fischer1 h ago
<div class="card">
    <div class="card-head">
        <strong>Recent decisions</strong>
        <span class="badge">3</span>
    </div>
    <table class="table">
        <thead>
            <tr><th>Order</th><th>Action</th><th>By</th><th>When</th></tr>
        </thead>
        <tbody>
            <tr><td>ORD-4209</td><td><span class="badge badge-go">Approved</span></td><td>Alex Fischer</td><td class="col-muted">4 min ago</td></tr>
            <tr><td>ORD-4204</td><td><span class="badge badge-warn">Taken over</span></td><td>J. Weber</td><td class="col-muted">22 min ago</td></tr>
            <tr><td>ORD-4198</td><td><span class="badge badge-danger">Rejected</span></td><td>Alex Fischer</td><td class="col-muted">1 h ago</td></tr>
        </tbody>
    </table>
</div>

Key/value rows

.kv pushes label and value apart; the value takes the monospace token, so numbers and IDs align down the column.

Assignment groupEU-Central
PriorityP3
SLA remaining01:42:18
Confidence0.87
<div class="card" style="max-width:380px">
    <div class="card-body">
        <div class="kv"><span class="k">Assignment group</span><span class="v">EU-Central</span></div>
        <div class="kv"><span class="k">Priority</span><span class="v">P3</span></div>
        <div class="kv"><span class="k">SLA remaining</span><span class="v">01:42:18</span></div>
        <div class="kv"><span class="k">Confidence</span><span class="v">0.87</span></div>
    </div>
</div>

A guarded card

A card the reader can read but must claim, accept or unlock before acting on. .guard is absolute, so the card needs position: relative; put inert on the guarded content, or it stays in the tab order behind the scrim.

Mailbox quota exceeded on EXCH-02
P2

The shared mailbox has 200 MB left. The customer has replied twice.

Raised2026-08-28 07:41
CustomerContoso Logistics

Claim this order to read the customer's reply and answer on the team's behalf.

<div class="card" style="position:relative; max-width:420px">
    <!-- inert, because legible is not the same as reachable: without it the whole
         card stays in the tab order behind a scrim nobody can type through. -->
    <div inert>
        <div class="card-head">
            <strong>Mailbox quota exceeded on EXCH-02</strong>
            <div class="card-head-right"><span class="badge badge-sev-2">P2</span></div>
        </div>
        <div class="card-body">
            <p class="form-hint" style="margin-bottom:10px">
                The shared mailbox has 200 MB left. The customer has replied twice.
            </p>
            <div class="kv"><span class="k">Raised</span><span class="v">2026-08-28 07:41</span></div>
            <div class="kv"><span class="k">Customer</span><span class="v">Contoso Logistics</span></div>
        </div>
    </div>

    <div class="guard">
        <div class="guard-card">
            <span class="guard-icon"><i class="ri-lock-line" aria-hidden="true"></i></span>
            <p>Claim this order to read the customer's reply and answer on the team's behalf.</p>
            <button class="btn btn-primary" type="button">Claim</button>
        </div>
    </div>
</div>

A guarded card over taller content

The prompt is centred, not stretched: over a card taller than it, the guard card hovers in the middle and the blurred content stays visible above and below it.

Mailbox quota exceeded on EXCH-02
P2
Raised2026-08-28 07:41
CustomerContoso Logistics
QueueExchange / Storage
Assigneeunassigned
Last reply2026-08-29 16:02
SLAbreaches in 3 h 40 m
TicketTCK-204471

Claim this order to read the customer's reply and answer on the team's behalf.

<div class="card" style="position:relative; max-width:420px">
    <!-- inert, because legible is not the same as reachable: without it the whole
         card stays in the tab order behind a scrim nobody can type through. -->
    <div inert>
        <div class="card-head">
            <strong>Mailbox quota exceeded on EXCH-02</strong>
            <div class="card-head-right"><span class="badge badge-sev-2">P2</span></div>
        </div>
        <div class="card-body">
            <div class="kv"><span class="k">Raised</span><span class="v">2026-08-28 07:41</span></div>
            <div class="kv"><span class="k">Customer</span><span class="v">Contoso Logistics</span></div>
            <div class="kv"><span class="k">Queue</span><span class="v">Exchange / Storage</span></div>
            <div class="kv"><span class="k">Assignee</span><span class="v">unassigned</span></div>
            <div class="kv"><span class="k">Last reply</span><span class="v">2026-08-29 16:02</span></div>
            <div class="kv"><span class="k">SLA</span><span class="v">breaches in 3 h 40 m</span></div>
            <div class="kv"><span class="k">Ticket</span><span class="v">TCK-204471</span></div>
        </div>
        <div class="card-foot">
            <button class="btn" type="button">Snooze</button>
            <button class="btn btn-primary" type="button">Answer</button>
        </div>
    </div>

    <div class="guard">
        <div class="guard-card">
            <span class="guard-icon"><i class="ri-lock-line" aria-hidden="true"></i></span>
            <p>Claim this order to read the customer's reply and answer on the team's behalf.</p>
            <button class="btn btn-primary" type="button">Claim</button>
        </div>
    </div>
</div>

A caveat about the card's own content

.card-warning pins an amber strip to the card's bottom edge — a stale figure, a degraded source, a partial result.

Open orders
P12
P211
P364
The warehouse last answered 14 minutes ago — these counts may lag.
<div class="card" style="max-width:380px">
    <div class="card-head"><strong>Open orders</strong></div>
    <div class="card-body">
        <div class="kv"><span class="k">P1</span><span class="v">2</span></div>
        <div class="kv"><span class="k">P2</span><span class="v">11</span></div>
        <div class="kv"><span class="k">P3</span><span class="v">64</span></div>
    </div>
    <div class="card-warning">
        <i class="ri-time-line"></i>
        <span>The warehouse last answered 14 minutes ago — these counts may lag.</span>
    </div>
</div>
Cards side by side In a row of cards, .card--fill lets the body take the spare height, so every foot and .card-warning in the row ends on one line. It is shown on a whole dashboard under Grid and layout.