Toolbar
A toolbar groups related controls into a band with one Tab stop: Arrow keys move
focus between controls, Home/End jump to the ends. The markup is a container with
role="toolbar" and an accessible name. With zero JavaScript every control
is a native button, so the toolbar is just N Tab stops — enhanceToolbar upgrades
it to the single-Tab-stop roving model.
import { enhanceToolbar } from "@relements/core/behaviors/toolbar";const c = enhanceToolbar(document);c.destroy(); // restores N native Tab stopsGroup related controls with .re-toolbar__group (role="group") and divide them
with a vertical .re-separator. A hosted menu uses the normal .re-menu markup —
its trigger is a single toolbar item, and while the menu is open it governs its
own keys.
<div class="re-toolbar" role="toolbar" aria-label="Text formatting" data-re-toolbar>
<div class="re-toolbar__group" role="group" aria-label="Style">
<button
class="re-button"
data-variant="ghost"
data-size="sm"
type="button"
aria-pressed="true"
aria-label="Bold"
>
B
</button>
<button
class="re-button"
data-variant="ghost"
data-size="sm"
type="button"
aria-pressed="false"
aria-label="Italic"
>
I
</button>
<button
class="re-button"
data-variant="ghost"
data-size="sm"
type="button"
aria-pressed="false"
aria-label="Underline"
>
U
</button>
</div>
<div
class="re-separator"
role="separator"
aria-orientation="vertical"
data-orientation="vertical"
></div>
<div class="re-toolbar__group" role="group" aria-label="Align">
<button class="re-button" data-variant="ghost" data-size="sm" type="button">Left</button>
<button
class="re-button"
data-variant="ghost"
data-size="sm"
type="button"
aria-disabled="true"
>
Center
</button>
</div>
<div
class="re-separator"
role="separator"
aria-orientation="vertical"
data-orientation="vertical"
></div>
<div class="re-menu" data-re-menu>
<button
class="re-button"
data-variant="ghost"
data-size="sm"
type="button"
id="tb-more-btn"
aria-haspopup="menu"
aria-expanded="false"
aria-controls="tb-more"
>
More
</button>
<div class="re-menu__panel" role="menu" id="tb-more" aria-labelledby="tb-more-btn" hidden>
<button class="re-menu__item" role="menuitem" type="button">Clear formatting</button>
<button class="re-menu__item" role="menuitem" type="button">Insert link…</button>
</div>
</div>
</div> Vertical
Section titled “Vertical”data-orientation="vertical" lays the band out as a column (Up/Down arrows roam)
and sets aria-orientation="vertical".
<div
class="re-toolbar"
role="toolbar"
aria-label="Drawing tools"
data-orientation="vertical"
data-re-toolbar
>
<button
class="re-button"
data-variant="ghost"
data-size="sm"
type="button"
aria-pressed="true"
>
Select
</button>
<button class="re-button" data-variant="ghost" data-size="sm" type="button">Pen</button>
<button class="re-button" data-variant="ghost" data-size="sm" type="button">Erase</button>
</div> Wrapping
Section titled “Wrapping”data-wrap lets a long toolbar flow onto multiple rows; arrow navigation stays
DOM-order linear and the single Tab stop is preserved.
<div
class="re-toolbar"
role="toolbar"
aria-label="Many actions"
data-wrap
data-re-toolbar
style="max-inline-size: 18rem"
>
<button class="re-button" data-variant="ghost" data-size="sm" type="button">Cut</button>
<button class="re-button" data-variant="ghost" data-size="sm" type="button">Copy</button>
<button class="re-button" data-variant="ghost" data-size="sm" type="button">Paste</button>
<button class="re-button" data-variant="ghost" data-size="sm" type="button">Undo</button>
<button class="re-button" data-variant="ghost" data-size="sm" type="button">Redo</button>
<button class="re-button" data-variant="ghost" data-size="sm" type="button">Delete</button>
</div> Accessibility
Section titled “Accessibility”- Keyboard — One Tab stop enters/leaves the band; within it the arrow keys
rove focus. Left/Right move (Up/Down when
data-orientation="vertical"), and Home/End jump to the first/last control, clamped at the ends with no wrap. Horizontal arrows mirror in RTL (Right = previous, Left = next). Each control is a native<button>, so Enter/Space activates it. Native text, range and<select>controls keep their own Arrow/Home/End; a hosted.re-menutrigger keeps its own ArrowDown/ArrowUp to open, and once the menu is open it owns the keys. Without JavaScript every control is simply its own Tab stop —enhanceToolbaronly collapses them to the single-Tab-stop roving model. - Focus — Each button shows the standard
.re-button:focus-visiblering; the band’s padding clears the ring’s offset, and active/focused members are lifted (z-index) so the ring is never cramped at the edge or painted under a neighbor. - Semantics — A container with
role="toolbar"and a required accessible name (aria-label);data-orientation="vertical"addsaria-orientation="vertical". Clusters use.re-toolbar__group(role="group"with its ownaria-label), divided by a.re-separator(role="separator"). Toggle buttons carryaria-pressed; a hosted menu trigger usesaria-haspopup="menu"/aria-expanded. - Notes — Discoverable-but-disabled controls use
aria-disabled="true"so they stay focusable in the roving order; nativedisabledcontrols are skipped.aria-disabledonly suppresses pointer activation via CSS — a real<button>still fires on Enter/Space, so gate the action in your handler (if (el.getAttribute("aria-disabled") === "true") return). Under forced colors a toggled-on control is marked with systemHighlight/HighlightTextso the pressed state survives HCM. See the accessibility guide.
Related
Section titled “Related”- For attached, bordered button clusters use the Button group component.