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.
| Class | Use for |
|---|---|
.btn-primary | The neutral primary action |
.btn-go | Sends something outward — approve, apply, send, reroute |
.btn-warn-solid | Changes who is in control — accept and take over |
.btn-warn | The same action as a secondary, beside a green primary |
.btn-danger | Destructive |
Variants
The full set, all at the same height.
<button class="btn">Neutral</button>
<button class="btn btn-primary">Primary</button>
<button class="btn btn-go"><i class="ri-check-line"></i> Approve & send</button>
<button class="btn btn-warn-solid"><i class="ri-user-shared-line"></i> Accept & take over</button>
<button class="btn btn-warn">Take over</button>
<button class="btn btn-danger"><i class="ri-delete-bin-line"></i> Delete</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.
<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-primary"><i class="ri-check-line"></i> Small</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-primary"><i class="ri-check-line"></i> Default</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-primary"><i class="ri-check-line"></i> Large</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-primary" type="button">
<i class="ri-save-line"></i> Save changes
</button>
<button class="btn btn-primary" type="button" aria-busy="true">
<i class="ri-save-line"></i> Save changes
</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-primary" 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> Preview</button>
<button class="btn btn-ghost btn-sm" type="button"><i class="ri-download-2-line"></i> Export</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">
10482 <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> Three earlier attempts
</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.
<button class="btn">
<i class="ri-notification-3-line"></i> 12
<span class="visually-hidden">unread notifications</span>
</button>
<a class="btn btn-primary" href="#">
Continue <span class="visually-hidden">to the approval step</span>
</a>
As a link
.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> Open in the warehouse</a>
<a class="btn btn-sm" href="#">Details</a>
In a footer row
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"><i class="ri-send-plane-line"></i> Approve & send</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> New order
</button>
<button class="fab fab--icon" type="button" style="position:static" aria-label="New order">
<i class="ri-add-line"></i>
</button>
</div>