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.31.0 · main

Buttons

tier 2 — classes

.btn is the base; every variant is one extra class. A panel's primary call to action is always a filled button in its semantic colour.

Choose a variant by meaning, not appearance.

ClassUse for
.btn--brand.btn--solidThe neutral primary action
.btn--go.btn--solidSends something outward — approve, apply, send, reroute
.btn--warn.btn--solidChanges who is in control — accept and take over
.btn--warnThe same action as a secondary, beside a green primary
.btn--dangerDestructive

Tone and emphasis

A tone says what a button does — .btn--brand, --go, --warn, --danger, --info, --secret — and an emphasis how loudly: soft by default, .btn--solid filled, .btn--ghost with no fill until hovered. Any tone takes any emphasis; a panel's one primary action is the filled one.

<div class="sedna-col sedna-gap-1">
    <div class="sedna-row-wrap"><button class="btn" type="button">Neutral</button><button class="btn btn--solid" type="button">Neutral solid</button><button class="btn btn--ghost" type="button">Neutral ghost</button></div>
    <div class="sedna-row-wrap"><button class="btn btn--brand" type="button">Publish</button><button class="btn btn--brand btn--solid" type="button">Publish</button><button class="btn btn--brand btn--ghost" type="button">Publish</button><button class="btn btn--brand btn--ghost btn--icon" type="button" aria-label="Publish"><i class="ri-arrow-right-line"></i></button></div>
    <div class="sedna-row-wrap"><button class="btn btn--go" type="button">Approve</button><button class="btn btn--go btn--solid" type="button">Approve</button><button class="btn btn--go btn--ghost" type="button">Approve</button><button class="btn btn--go btn--ghost btn--icon" type="button" aria-label="Approve"><i class="ri-arrow-right-line"></i></button></div>
    <div class="sedna-row-wrap"><button class="btn btn--warn" type="button">Take over</button><button class="btn btn--warn btn--solid" type="button">Take over</button><button class="btn btn--warn btn--ghost" type="button">Take over</button><button class="btn btn--warn btn--ghost btn--icon" type="button" aria-label="Take over"><i class="ri-arrow-right-line"></i></button></div>
    <div class="sedna-row-wrap"><button class="btn btn--danger" type="button">Delete</button><button class="btn btn--danger btn--solid" type="button">Delete</button><button class="btn btn--danger btn--ghost" type="button">Delete</button><button class="btn btn--danger btn--ghost btn--icon" type="button" aria-label="Delete"><i class="ri-arrow-right-line"></i></button></div>
    <div class="sedna-row-wrap"><button class="btn btn--info" type="button">Details</button><button class="btn btn--info btn--solid" type="button">Details</button><button class="btn btn--info btn--ghost" type="button">Details</button><button class="btn btn--info btn--ghost btn--icon" type="button" aria-label="Details"><i class="ri-arrow-right-line"></i></button></div>
    <div class="sedna-row-wrap"><button class="btn btn--secret" type="button">Reveal key</button><button class="btn btn--secret btn--solid" type="button">Reveal key</button><button class="btn btn--secret btn--ghost" type="button">Reveal key</button><button class="btn btn--secret btn--ghost btn--icon" type="button" aria-label="Reveal key"><i class="ri-arrow-right-line"></i></button></div>
</div>

Variants

The full set, all at the same height.

<button class="btn">Neutral</button>
<button class="btn btn--brand btn--solid">Primary</button>
<button class="btn btn--go btn--solid"><i class="ri-check-line"></i> <span>Approve &amp; send</span></button>
<button class="btn btn--warn btn--solid"><i class="ri-user-shared-line"></i> <span>Accept &amp; take over</span></button>
<button class="btn btn--warn">Take over</button>
<button class="btn btn--danger"><i class="ri-delete-bin-line"></i> <span>Delete</span></button>
<button class="btn" disabled>Disabled</button>

Three sizes

.btn--sm, the default, and .btn--lg. Every control that can share a row takes the same three heights — 28px, 36px and 44px — so a button, a link, an icon button, a field and a chip line up; a badge does not, because it has its own type scale.

Small chip Small link
Default chip Default link
Large link
<div class="sedna-col sedna-gap-2">
    <div class="sedna-row-wrap">
        <button class="btn btn--sm">Small</button>
        <button class="btn btn--sm btn--brand btn--solid"><i class="ri-check-line"></i> <span>Small</span></button>
        <button class="btn btn--sm btn--icon" aria-label="Small icon"><i class="ri-refresh-line"></i></button>
        <span class="chip"><span class="chip-label">Small chip</span></span>
        <a class="btn btn--sm" href="#">Small link</a>
    </div>
    <div class="sedna-row-wrap">
        <button class="btn">Default</button>
        <button class="btn btn--brand btn--solid"><i class="ri-check-line"></i> <span>Default</span></button>
        <button class="btn btn--icon" aria-label="Default icon"><i class="ri-refresh-line"></i></button>
        <span class="chip chip--lg"><span class="chip-label">Default chip</span></span>
        <a class="btn" href="#">Default link</a>
    </div>
    <div class="sedna-row-wrap">
        <button class="btn btn--lg">Large</button>
        <button class="btn btn--lg btn--brand btn--solid"><i class="ri-check-line"></i> <span>Large</span></button>
        <button class="btn btn--lg btn--icon" aria-label="Large icon"><i class="ri-refresh-line"></i></button>
        <a class="btn btn--lg" href="#">Large link</a>
    </div>
</div>

While its action runs

aria-busy="true" while a save or a call is in flight. The label stays in the layout and is painted out, so the button keeps its measured width and does not shrink under the cursor; a ring in the variant's own ink spins in its place, and a second pointer click is ignored. Leave disabled off — it drops focus and stops the state being announced — and have the handler ignore a second Enter while the work runs.

<div class="sedna-row-wrap">
    <button class="btn btn--brand btn--solid" type="button">
        <i class="ri-save-line"></i> <span>Save changes</span>
    </button>
    <button class="btn btn--brand btn--solid" type="button" aria-busy="true">
        <i class="ri-save-line"></i> <span>Save changes</span>
    </button>
    <button class="btn btn--sm" type="button" aria-busy="true">Refresh</button>
</div>

Icon-only

.btn--icon is not optional — a bare .btn keeps its text padding, so one glyph sits in a box half again as wide as it is tall. Give every icon-only button an aria-label; the glyph itself is a bundled Remix Icon, linked from _content/Sedna.UI/lib/remixicon/remixicon.css as ri-* on an <i>.

<button class="btn">Return to queue</button>
<button class="btn btn--icon" aria-label="Refresh" data-tip="Reload the list from the server."><i class="ri-refresh-line"></i></button>
<button class="btn btn--icon" aria-label="Copy link" data-tip="Copy a direct link to this item to the clipboard."><i class="ri-link"></i></button>
<button class="btn btn--icon btn--brand btn--solid" aria-label="Save"><i class="ri-save-line"></i></button>
<button class="btn btn--icon btn--sm" aria-label="Unpin"><i class="ri-pushpin-line"></i></button>

Ghost

.btn--ghost defers its border and fill until hover, for a dense row of secondary actions — a table row's actions, a toolbar. It keeps the same box as a bordered button, so a ghost and a solid button in one row still line up.

<button class="btn btn--ghost btn--icon" type="button" aria-label="More actions" data-tip="Rename, duplicate, export."><i class="ri-more-2-fill"></i></button>
<button class="btn btn--ghost" type="button"><i class="ri-eye-line"></i> <span>Preview</span></button>
<button class="btn btn--ghost btn--sm" type="button"><i class="ri-download-2-line"></i> <span>Export</span></button>
<button class="btn" type="button">Reassign</button>

In a line of text

.btn-bare is a <button> with no background, border or padding, taking its font and colour from the text around it — for an identifier that acts: a record number that opens the record, a key with a copy button beside it, a chevron that unfolds a section. .btn-bare--link colours it like a link, for one that goes somewhere.

Write it without .btn — it is the absence of the button's chrome, not a variant of it, and both classes together bring the height and the hover fill back. It keeps the library's focus ring, which is the declaration a hand-written inline reset forgets: removing the border removes the browser's own outline with it. Tab through this example to see it.

Reserved against order by Alex Fischer, four minutes ago.

Publishing key pk-live-8f2c4d

<div class="sedna-col sedna-gap-2" style="max-width:520px">
    <p style="margin:0">
        Reserved against order
        <button class="btn-bare btn-bare--link" type="button">
            <span>10482</span> <i class="ri-external-link-line" aria-hidden="true"></i>
        </button>
        by Alex Fischer, four minutes ago.
    </p>
    <p style="margin:0">
        Publishing key <code>pk-live-8f2c4d</code>
        <button class="btn-bare" type="button" data-copy="pk-live-8f2c4d" aria-label="Copy the publishing key">
            <i class="ri-file-copy-line" aria-hidden="true" data-copied-hide></i>
            <i class="ri-check-line" aria-hidden="true" data-copied-show="ok"></i>
        </button>
    </p>
    <p style="margin:0">
        <button class="btn-bare" type="button" aria-expanded="false">
            <i class="ri-arrow-right-s-line" aria-hidden="true"></i> <span>Three earlier attempts</span>
        </button>
    </p>
</div>

A name that is read but not shown

.visually-hidden takes text out of the rendering and leaves it in the accessibility tree — here the button shows a count and the hidden span says what the count is of. Do not reach for display:none or a zero-size box instead: both drop the element from the accessibility tree too.

Continue to the approval step
<button class="btn">
    <i class="ri-notification-3-line"></i> <span>12</span>
    <span class="visually-hidden">unread notifications</span>
</button>
<a class="btn btn--brand btn--solid" href="#">
    Continue <span class="visually-hidden">to the approval step</span>
</a>

.btn works on an <a> — it sets its own text-decoration and colour, so no reset is needed.

<a class="btn" href="#"><i class="ri-external-link-line"></i> <span>Open in the warehouse</span></a>
<a class="btn btn--sm" href="#">Details</a>

A label on two lines

.btn--wrap lets the label wrap and starts it at the inline start, for a picker whose choices are full-width buttons with a long name. A button is otherwise one line.

<div class="sedna-col sedna-gap-2" style="max-width:280px">
    <button class="btn btn--wrap" type="button">
        <i class="ri-user-line"></i>
        <span>Alex Fischer<br><span class="text-muted text-sm">Northwind Retail, accounts payable and reconciliation</span></span>
    </button>
    <button class="btn btn--wrap" type="button">
        <i class="ri-user-line"></i>
        <span>Shared mailbox for the regional fulfilment team</span>
    </button>
</div>

Cancel on the left of the primary, primary last — the reading order ends on the action.

<div class="modal-footer" style="width:100%; border-top:none; padding:0;">
    <button class="btn">Cancel</button>
    <button class="btn btn--go btn--solid"><i class="ri-send-plane-line"></i> <span>Approve &amp; send</span></button>
</div>

Floating action button

.fab is one page-level action, pinned to a corner on a screen too small to show a toolbar; .fab--icon is the circular, label-in-an-aria-label form. Shown here position:static; in an app it is fixed to the corner and must stay below every overlay.

<div class="sedna-row-wrap">
    <button class="fab" type="button" style="position:static">
        <i class="ri-add-line"></i> <span>New order</span>
    </button>
    <button class="fab fab--icon" type="button" style="position:static" aria-label="New order">
        <i class="ri-add-line"></i>
    </button>
</div>