Skip to content

Overlays

sb-dialog styles a native <dialog>. Open it modally with dialog.showModal(): the browser puts it in the top layer, draws the ::backdrop, traps focus, closes on Escape and returns focus to the opener. dialog.show() opens it non-modally, inline.

The demos on this page use one delegated script: data-open="#id" on a button calls showModal(), and data-close on a button inside the dialog calls close().

Confirm transfer

Send 8,750 eddies to Regina?

The transfer settles immediately and cannot be reversed.

The body scrolls on its own when it overflows. Header, body and footer are optional parts.

Balance: 46,350

Delete save slot?

The top hairline turns danger for destructive flows.

Large dialog

Paragraph one of a long body.

Paragraph two.

Paragraph three.

Paragraph four.

Paragraph five.

Paragraph six.

Paragraph seven.

Paragraph eight.

Paragraph nine.

Paragraph ten.

Paragraph eleven.

Paragraph twelve.

Paragraph thirteen.

Paragraph fourteen.

Paragraph fifteen.

Inventory

Right drawer

Slides in from the inline-end edge.

Bottom sheet

Rounded top corners.
<div class="sb-cluster">
<button type="button" class="sb-btn" data-open="#dlg-md">Open dialog</button>
<button type="button" class="sb-btn sb-btn-soft sb-btn-danger" data-open="#dlg-danger">Danger dialog</button>
<button type="button" class="sb-btn sb-btn-outline sb-btn-secondary" data-open="#dlg-lg">Large, scrolling</button>
<button type="button" class="sb-btn sb-btn-outline sb-btn-secondary" data-open="#drw-right">Drawer right</button>
<button type="button" class="sb-btn sb-btn-outline sb-btn-secondary" data-open="#drw-bottom">Drawer bottom</button>
</div>
<dialog class="sb-dialog sb-dialog-md" id="dlg-md" aria-labelledby="dlg-md-title">
<header class="sb-dialog-header">
<p class="sb-dialog-eyebrow">Confirm transfer</p>
<h2 class="sb-dialog-title" id="dlg-md-title">Send 8,750 eddies to Regina?</h2>
<p class="sb-dialog-description">The transfer settles immediately and cannot be reversed.</p>
<button type="button" class="sb-dialog-close" data-close aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
</header>
<div class="sb-dialog-body">
<p>The body scrolls on its own when it overflows. Header, body and footer are optional parts.</p>
</div>
<footer class="sb-dialog-footer">
<span class="sb-dialog-footer-start sb-text-subtle">Balance: 46,350</span>
<button type="button" class="sb-btn sb-btn-ghost sb-btn-neutral" data-close>Cancel</button>
<button type="button" class="sb-btn" data-close>Send</button>
</footer>
</dialog>
<dialog class="sb-dialog sb-dialog-sm sb-dialog-danger" id="dlg-danger" aria-labelledby="dlg-danger-title">
<header class="sb-dialog-header">
<h2 class="sb-dialog-title" id="dlg-danger-title">Delete save slot?</h2>
<p class="sb-dialog-description">The top hairline turns danger for destructive flows.</p>
</header>
<footer class="sb-dialog-footer">
<button type="button" class="sb-btn sb-btn-ghost sb-btn-neutral" data-close autofocus>Keep</button>
<button type="button" class="sb-btn sb-btn-danger" data-close>Delete</button>
</footer>
</dialog>
<dialog class="sb-dialog sb-dialog-lg" id="dlg-lg" aria-labelledby="dlg-lg-title">
<header class="sb-dialog-header">
<h2 class="sb-dialog-title" id="dlg-lg-title">Large dialog</h2>
<button type="button" class="sb-dialog-close" data-close aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
</header>
<div class="sb-dialog-body">
<p>Paragraph one of a long body.</p><p>Paragraph two.</p><p>Paragraph three.</p><p>Paragraph four.</p><p>Paragraph five.</p><p>Paragraph six.</p><p>Paragraph seven.</p><p>Paragraph eight.</p><p>Paragraph nine.</p><p>Paragraph ten.</p><p>Paragraph eleven.</p><p>Paragraph twelve.</p><p>Paragraph thirteen.</p><p>Paragraph fourteen.</p><p>Paragraph fifteen.</p>
</div>
<footer class="sb-dialog-footer"><button type="button" class="sb-btn" data-close>Done</button></footer>
</dialog>
<dialog class="sb-dialog sb-dialog-drawer" id="drw-right" aria-labelledby="drw-right-title">
<header class="sb-dialog-header">
<p class="sb-dialog-eyebrow">Inventory</p>
<h2 class="sb-dialog-title" id="drw-right-title">Right drawer</h2>
<button type="button" class="sb-dialog-close" data-close aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
</header>
<div class="sb-dialog-body"><p>Slides in from the inline-end edge.</p></div>
<footer class="sb-dialog-footer"><button type="button" class="sb-btn" data-close>Done</button></footer>
</dialog>
<dialog class="sb-dialog sb-dialog-drawer sb-dialog-drawer-bottom" id="drw-bottom" aria-labelledby="drw-bottom-title">
<header class="sb-dialog-header">
<h2 class="sb-dialog-title" id="drw-bottom-title">Bottom sheet</h2>
<button type="button" class="sb-dialog-close" data-close aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
</header>
<div class="sb-dialog-body">Rounded top corners.</div>
</dialog>
native-behaviors.js
// Native behaviors for the plain-HTML component demos. The CSS classes need none of this; it only
// wires the platform features the markup relies on.
export function bindNativeBehaviors() {
document.addEventListener('click', (event) => {
const target = event.target
if (!(target instanceof Element)) return
// [data-open="#id"] opens a modal <dialog>; [data-close] closes the dialog it sits in.
const opener = target.closest('[data-open]')
if (opener) {
const dialog = document.querySelector(opener.getAttribute('data-open'))
if (dialog instanceof HTMLDialogElement && !dialog.open) dialog.showModal()
return
}
const closer = target.closest('[data-close]')
if (closer) closer.closest('dialog')?.close()
})
// Tab lists: click and arrow keys move selection (roving tabindex), Home and End jump.
for (const tabs of document.querySelectorAll('[data-tabs]')) {
const list = tabs.querySelector('[role="tablist"]')
if (!list) continue
const vertical = list.getAttribute('aria-orientation') === 'vertical'
const enabledTabs = () => [...list.querySelectorAll('[role="tab"]:not(:disabled)')]
const select = (tab, focus) => {
for (const item of list.querySelectorAll('[role="tab"]')) {
const selected = item === tab
item.setAttribute('aria-selected', String(selected))
item.tabIndex = selected ? 0 : -1
const panel = document.getElementById(item.getAttribute('aria-controls'))
if (panel) panel.hidden = !selected
}
if (focus) tab.focus()
}
list.addEventListener('click', (event) => {
const tab = event.target instanceof Element ? event.target.closest('[role="tab"]') : null
if (tab && !tab.disabled) select(tab, false)
})
list.addEventListener('keydown', (event) => {
const all = enabledTabs()
const index = all.indexOf(document.activeElement)
if (index < 0) return
const previousKey = vertical ? 'ArrowUp' : 'ArrowLeft'
const nextKey = vertical ? 'ArrowDown' : 'ArrowRight'
let target
if (event.key === nextKey) target = all[(index + 1) % all.length]
else if (event.key === previousKey) target = all[(index - 1 + all.length) % all.length]
else if (event.key === 'Home') target = all[0]
else if (event.key === 'End') target = all[all.length - 1]
if (!target) return
event.preventDefault()
select(target, true)
})
}
// The indeterminate state of a checkbox has no attribute; it is a DOM property.
for (const box of document.querySelectorAll('[data-indeterminate]')) box.indeterminate = true
}

Always name the dialog with aria-labelledby (pointing at the title) or aria-label. Put autofocus on the safest control of a destructive dialog, as the danger example does.

Class Effect
sb-dialog The panel (medium by default): canvas fill, hairline, radius xl, a gradient bloom hairline on top, enter animation.
sb-dialog-sm, -md, -lg, -xl, -full Width 24rem, 32rem (default), 48rem and 64rem, and full-viewport with no radius.
sb-dialog-header Header part.
sb-dialog-eyebrow Small uppercase caption above the title.
sb-dialog-title Title; use a heading element.
sb-dialog-description Supporting text under the title.
sb-dialog-close Close button in the header corner; needs an aria-label.
sb-dialog-body Scrolling content area.
sb-dialog-footer Action row, pinned to the bottom.
sb-dialog-footer-start Pushes a footer item to the start (for a note beside the actions).
sb-dialog-danger The top hairline turns danger, for destructive flows.
sb-dialog-drawer Slides in from a viewport edge (inline-end by default).
sb-dialog-drawer-left, -top, -bottom Edge for a drawer.
[open] The visible state; the enter animation plays when it is set.
::backdrop Dimmed, blurred scrim using --sb-backdrop.

sb-popover styles an element with the popover attribute. The Popover API gives you toggling with popovertarget, light dismiss (click outside, Escape) and top-layer stacking with no script. popovertargetaction="hide" on a button inside closes it.

The browser centers an unpositioned popover in the viewport, so these demos open in the middle of the window. In a real UI, position it with CSS anchor positioning or a library such as Floating UI.

Netrunner build

Intelligence 20, Cool 15

Panel fill, hairline border and the overlay shadow.

Small popover: 12rem wide.

Large popover

22rem minimum width.
Glass popover for HUD overlays over game imagery.
<div class="sb-cluster">
<button type="button" class="sb-btn sb-btn-soft" popovertarget="pop-md">Popover</button>
<button type="button" class="sb-btn sb-btn-soft" popovertarget="pop-sm">Small</button>
<button type="button" class="sb-btn sb-btn-soft" popovertarget="pop-lg">Large</button>
<button type="button" class="sb-btn sb-btn-soft" popovertarget="pop-glass">Glass</button>
</div>
<div id="pop-md" popover class="sb-popover">
<header class="sb-popover-header">
<h3 class="sb-popover-title">Netrunner build</h3>
<p class="sb-popover-description">Intelligence 20, Cool 15</p>
</header>
<button type="button" class="sb-popover-close" popovertarget="pop-md" popovertargetaction="hide" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" width="14" height="14"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
<div class="sb-popover-body"><p>Panel fill, hairline border and the overlay shadow.</p></div>
<footer class="sb-popover-footer">
<button type="button" class="sb-btn sb-btn-ghost sb-btn-neutral sb-btn-sm" popovertarget="pop-md" popovertargetaction="hide">Close</button>
<button type="button" class="sb-btn sb-btn-sm">Equip</button>
</footer>
</div>
<div id="pop-sm" popover class="sb-popover sb-popover-sm">Small popover: 12rem wide.</div>
<div id="pop-lg" popover class="sb-popover sb-popover-lg">
<header class="sb-popover-header"><h3 class="sb-popover-title">Large popover</h3></header>
<div class="sb-popover-body">22rem minimum width.</div>
</div>
<div id="pop-glass" popover class="sb-popover sb-popover-glass">Glass popover for HUD overlays over game imagery.</div>
Class Effect
sb-popover Panel fill, hairline, radius md, overlay shadow, enter animation on :popover-open.
sb-popover-sm, -md, -lg Width presets: 12rem, 16rem (default) and 22rem (up to 32rem).
sb-popover-glass Glass fill for HUD overlays over game imagery.
sb-popover-header, -title, -description Header, title (use a heading) and supporting text.
sb-popover-body, -footer Content and action row.
sb-popover-close Corner close button; needs an aria-label.
sb-popover-arrow An 8px rotated square for a pointer; position it yourself.
[popover] Hidden until open.
:popover-open Open state: enter animation.
[data-state="open"], [data-state="closed"] Enter and leave motion for a popover positioned by script (no popover attribute).
[data-side="top"], "left", "right" Motion direction and arrow side for a script-positioned popover.

sb-popover also works as a plain box with no popover attribute, driven by data-state and data-side, which is how a positioning library renders it:

data-state="open"
data-side="top" with an arrow
<div class="sb-cluster sb-cluster-6">
<div class="sb-popover sb-popover-sm" data-state="open" data-side="bottom">data-state="open"</div>
<div class="sb-popover sb-popover-sm" data-side="top" style="position: relative">
data-side="top" with an arrow
<span class="sb-popover-arrow" style="bottom: -0.3rem; left: 50%; translate: -50% 0"></span>
</div>
</div>

sb-menu is a list of actions. It works as a static block (sidebars, menus a script renders) or as a Popover API element. A menu is a command surface, so give it role="menu" and its items role="menuitem", menuitemcheckbox or menuitemradio.

A popover menu opens and dismisses natively. The arrow-key navigation, typeahead and focus management of the menu pattern are not provided by HTML; use the Vue SbDropdownMenu family when you need them.

<button type="button" class="sb-btn sb-btn-secondary" popovertarget="menu-pop" aria-haspopup="menu">Open menu</button>
<div id="menu-pop" popover class="sb-menu" role="menu" aria-label="Actions">
<div class="sb-menu-label">Job</div>
<button type="button" class="sb-menu-item" role="menuitem"><span class="sb-menu-item-text">Accept</span><span class="sb-menu-shortcut">Enter</span></button>
<button type="button" class="sb-menu-item" role="menuitem"><span class="sb-menu-item-text">Track on map</span><span class="sb-menu-shortcut">T</span></button>
<div class="sb-menu-separator" role="separator"></div>
<button type="button" class="sb-menu-item" role="menuitemcheckbox" aria-checked="true"><span class="sb-menu-item-text">Notify on update</span></button>
<button type="button" class="sb-menu-item" role="menuitemcheckbox" aria-checked="false"><span class="sb-menu-item-text">Pin</span></button>
<div class="sb-menu-separator" role="separator"></div>
<button type="button" class="sb-menu-item sb-menu-item-danger" role="menuitem"><span class="sb-menu-item-text">Abandon</span><span class="sb-menu-shortcut">Del</span></button>
</div>

Highlight is a primary wash plus a 2px bar on the inline-start edge. It shows on hover, on :focus-visible and on [data-highlighted], which is how a script marks the keyboard-active item.

<div class="sb-menu" role="menu" aria-label="Context" style="position: static; display: flex">
<div class="sb-menu-label">Character</div>
<button type="button" class="sb-menu-item" role="menuitem"><span class="sb-menu-icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/></svg></span><span class="sb-menu-item-text">Profile</span><span class="sb-menu-shortcut">P</span></button>
<button type="button" class="sb-menu-item" role="menuitem" data-highlighted><span class="sb-menu-item-text">Highlighted (data-highlighted)</span><span class="sb-menu-shortcut">I</span></button>
<button type="button" class="sb-menu-item" role="menuitem"><span class="sb-menu-item-text">With description<span class="sb-menu-item-description">Secondary line under the label</span></span></button>
<button type="button" class="sb-menu-item" role="menuitem" aria-haspopup="menu" aria-expanded="false"><span class="sb-menu-item-text">Submenu trigger</span></button>
<button type="button" class="sb-menu-item" role="menuitem" aria-haspopup="menu" aria-expanded="true"><span class="sb-menu-item-text">Submenu open</span></button>
<button type="button" class="sb-menu-item" role="menuitem" disabled><span class="sb-menu-item-text">Disabled</span><span class="sb-menu-shortcut">D</span></button>
<div class="sb-menu-separator" role="separator"></div>
<div class="sb-menu-label">Toggles</div>
<button type="button" class="sb-menu-item" role="menuitemcheckbox" aria-checked="true"><span class="sb-menu-item-text">Checked</span></button>
<button type="button" class="sb-menu-item" role="menuitemcheckbox" aria-checked="false"><span class="sb-menu-item-text">Unchecked</span></button>
<button type="button" class="sb-menu-item" role="menuitemcheckbox" aria-checked="mixed"><span class="sb-menu-item-text">Mixed</span></button>
<div class="sb-menu-separator" role="separator"></div>
<div class="sb-menu-group" role="group" aria-label="Difficulty">
<button type="button" class="sb-menu-item" role="menuitemradio" aria-checked="false"><span class="sb-menu-item-text">Easy</span></button>
<button type="button" class="sb-menu-item" role="menuitemradio" aria-checked="true"><span class="sb-menu-item-text">Hard</span></button>
</div>
<div class="sb-menu-group" role="group">
<button type="button" class="sb-menu-item sb-menu-item-danger" role="menuitem"><span class="sb-menu-item-text">Delete character</span><span class="sb-menu-shortcut">Del</span></button>
</div>
</div>
<div class="sb-stack">
<div class="sb-menu sb-menu-sm" role="menu" aria-label="Small" style="position: static; display: flex">
<div class="sb-menu-label">sb-menu-sm</div>
<button type="button" class="sb-menu-item" role="menuitem"><span class="sb-menu-item-text">Compact item</span><span class="sb-menu-shortcut">1</span></button>
</div>
<div class="sb-menu sb-menu-lg" role="menu" aria-label="Large" style="position: static; display: flex">
<div class="sb-menu-label">sb-menu-lg</div>
<button type="button" class="sb-menu-item" role="menuitem"><span class="sb-menu-item-text">Roomy item</span><span class="sb-menu-shortcut">2</span></button>
</div>
<div class="sb-menubar" role="menubar" aria-label="Menubar">
<button type="button" class="sb-menu-item" role="menuitem" aria-haspopup="menu">File</button>
<button type="button" class="sb-menu-item" role="menuitem" aria-haspopup="menu" aria-expanded="true">Edit</button>
<button type="button" class="sb-menu-item" role="menuitem" aria-haspopup="menu">View</button>
<button type="button" class="sb-menu-item" role="menuitem" aria-haspopup="menu" disabled>Help</button>
</div>
</div>
<div class="sb-menu sb-menu-glass" role="menu" aria-label="Glass" style="position: static; display: flex">
<div class="sb-menu-label">sb-menu-glass</div>
<button type="button" class="sb-menu-item" role="menuitem"><span class="sb-menu-item-text">Over game imagery</span></button>
<button type="button" class="sb-menu-item" role="menuitem" data-highlighted><span class="sb-menu-item-text">Highlighted</span></button>
</div>
Class / selector Effect
sb-menu The panel. As [popover] it is hidden until open, with the :popover-open enter animation.
sb-menu-sm, sb-menu-lg Compact and roomy items.
sb-menu-glass Glass fill for HUD context menus.
sb-menu-label Non-interactive section heading.
sb-menu-separator Divider line; give it role="separator".
sb-menu-group Wrapper for related items (role="group").
sb-menu-item A row. Use a <button>.
sb-menu-item-text The label cell.
sb-menu-item-description Secondary line inside the text cell.
sb-menu-icon Leading icon slot; mark it aria-hidden.
sb-menu-shortcut Trailing key hint, in mono.
sb-menu-item-danger Danger ink.
sb-menubar Horizontal row of triggers (role="menubar").
[aria-checked="true"], [aria-checked="mixed"], [data-state="checked"] Draws the check or dash mark on an item.
[aria-haspopup], [aria-expanded="true"] A submenu trigger: chevron, and the highlight stays while open.
:disabled, [aria-disabled="true"], [data-disabled] Disabled item.
[data-highlighted] Keyboard-active item.
[data-state="open"], [data-state="closed"] Enter and leave motion for a script-rendered menu.

There are two tooltips, for two environments.

[data-tooltip] (websites only) is CSS only. The text comes from the attribute and shows on hover and :focus-visible after a short delay. Placement is data-tooltip-placement (top by default, bottom, left, right). An empty attribute shows nothing. The tooltip is an absolutely positioned pseudo-element, so near the edge of a narrow page it can widen the page; the demo’s overflow-x: clip on the wrapper prevents that. It cannot escape an ancestor with overflow: hidden or a transform, it has no touch or gamepad path, and it is not announced as a description, so keep the same information in a visible label or aria-label for essential controls.

<div class="sb-cluster" style="overflow-x: clip">
<button type="button" class="sb-btn sb-btn-outline sb-btn-neutral" data-tooltip="Default placement: top">Top</button>
<button type="button" class="sb-btn sb-btn-outline sb-btn-neutral" data-tooltip="Placement: bottom" data-tooltip-placement="bottom">Bottom</button>
<button type="button" class="sb-btn sb-btn-outline sb-btn-neutral" data-tooltip="Placement: left" data-tooltip-placement="left">Left</button>
<button type="button" class="sb-btn sb-btn-outline sb-btn-neutral" data-tooltip="Placement: right" data-tooltip-placement="right">Right</button>
<button type="button" class="sb-btn sb-btn-outline sb-btn-neutral" data-tooltip="A longer tooltip that wraps onto a second line once it passes eighteen rem of width" data-tooltip-placement="bottom">Long text</button>
</div>

.sb-tooltip (in-game safe) is the box itself. A script (Vue SbTooltip, Floating UI) positions it next to its anchor and sets data-state and data-side. It carries role="tooltip" and should be referenced from the anchor with aria-describedby. Shown here statically:

Equip to slot 11 data-side="bottom" data-side="left" data-side="right"
<div class="sb-cluster sb-cluster-6">
<span class="sb-tooltip" role="tooltip" data-state="open" data-side="top">Equip to slot 1<kbd class="sb-tooltip-kbd">1</kbd></span>
<span class="sb-tooltip" role="tooltip" data-side="bottom">data-side="bottom"</span>
<span class="sb-tooltip" role="tooltip" data-side="left">data-side="left"</span>
<span class="sb-tooltip" role="tooltip" data-side="right">data-side="right"</span>
</div>
Class / attribute Effect
[data-tooltip] Adds a CSS-only tooltip from the attribute value (websites only).
[data-tooltip-placement] top (default), bottom, left or right.
sb-tooltip The tooltip box: panel fill, strong hairline, small type, max 18rem, no pointer events.
sb-tooltip-arrow Styles an <svg> arrow with the box’s fill and stroke.
sb-tooltip-kbd Trailing key hint inside the box.
[data-state="open"], [data-state="closed"] Enter and leave animation.
[data-side="top"], "bottom", "left", "right" Motion direction.

A toast is a transient message. The CSS styles the elements; a script adds them to a .sb-toast-region, removes them and sets data-state="closed" for the leave motion. The region is position: fixed, pinned to a corner. The demo below overrides that inline so it can sit inside the page.

Use role="status" for routine messages and role="alert" for errors. Do not put an action that exists nowhere else in a toast: it disappears.

Default (primary)
Job accepted. Dakota will ping you with coordinates.
Saved
Loadout stored to slot 2.
Info, no icon
The title column starts at the accent bar.
Solid
Opaque panel for pages without a backdrop.
<div class="sb-toast-region sb-toast-region-bottom-right" role="region" aria-label="Notifications" style="position: static; width: auto; translate: none">
<div class="sb-toast" role="status">
<svg class="sb-toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 8v4M12 16h.01"/></svg>
<div class="sb-toast-title">Default (primary)</div>
<div class="sb-toast-description">Job accepted. Dakota will ping you with coordinates.</div>
<button type="button" class="sb-toast-action sb-btn sb-btn-ghost sb-btn-primary sb-btn-sm">View</button>
<button type="button" class="sb-toast-close" aria-label="Dismiss"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
<div class="sb-toast-timer" style="--sb-toast-progress: 0.6"></div>
</div>
<div class="sb-toast sb-toast-success" role="status">
<svg class="sb-toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 13l4 4L19 7"/></svg>
<div class="sb-toast-title">Saved</div>
<div class="sb-toast-description">Loadout stored to slot 2.</div>
<button type="button" class="sb-toast-action sb-btn sb-btn-ghost sb-btn-success sb-btn-sm">Undo</button>
</div>
<div class="sb-toast sb-toast-info" role="status">
<div class="sb-toast-title">Info, no icon</div>
<div class="sb-toast-description">The title column starts at the accent bar.</div>
</div>
<div class="sb-toast sb-toast-warning" role="alert">
<div class="sb-toast-title">Low ammo</div>
<div class="sb-toast-description">Reload before the next wave.</div>
</div>
<div class="sb-toast sb-toast-danger" role="alert">
<div class="sb-toast-title">Connection lost</div>
<div class="sb-toast-description">Retrying in 5 s.</div>
<button type="button" class="sb-toast-action sb-btn sb-btn-soft sb-btn-danger sb-btn-sm">Retry</button>
</div>
<div class="sb-toast sb-toast-solid" role="status">
<div class="sb-toast-title">Solid</div>
<div class="sb-toast-description">Opaque panel for pages without a backdrop.</div>
</div>
</div>
Class / attribute Effect
sb-toast-region Fixed stack container (role="region" with an aria-label). Bottom-right by default. Top regions reverse the stacking order.
sb-toast-region-top-left, -top-center, -top-right, -bottom-left, -bottom-center, -bottom-right Region position.
sb-toast The toast: glass fill, hairline, an accent bar on the inline-start edge with a bloom, slide-in animation. Accent is primary by default.
sb-toast-info, -success, -warning, -danger, -neutral Accent color.
sb-toast-solid Opaque panel for pages without a backdrop.
sb-toast-icon Leading icon; mark it aria-hidden. Without it the title column starts at the accent bar.
sb-toast-title, sb-toast-description Text parts.
sb-toast-action An action button (combine with sb-btn).
sb-toast-close Dismiss button; needs an aria-label.
sb-toast-timer Optional countdown bar. Set --sb-toast-progress from 0 to 1.
[data-state="open"], [data-state="closed"] Enter and leave motion.
[data-swipe="move"], "cancel", "end" Swipe-to-dismiss states, mirroring Reka UI’s Toast.