A toolbar somebody would actually ship
Four groups and a split button, doing the four jobs a real editing bar has: actions that just happen, one-of-N that changes the view, independent toggles that change the text, and a default action with alternatives behind it. Everything below the fold is the same components taken apart.
The sample paragraph really changes. Formatting toggles restyle it, the view control relabels it, and the split button's menu reports which alternative you chose — because a control that only writes to a log has not demonstrated anything a reader could not have assumed.
Get the code
<script src="/assets/bmx-components.min.js"></script>
<bmx-button-group label="History" attached="true" variant="outline" tone="neutral" size="sm">
<bmx-button id="bar-undo">Undo</bmx-button>
<bmx-button id="bar-redo" disabled="true">Redo</bmx-button>
</bmx-button-group>
<bmx-button-group id="bar-view" label="View" selection="single" value="grid" attached="true" variant="outline" tone="neutral" size="sm">
<bmx-button value="list">List</bmx-button>
<bmx-button value="grid">Grid</bmx-button>
<bmx-button value="board">Board</bmx-button>
</bmx-button-group>
<bmx-button-group id="bar-format" label="Formatting" selection="multiple" attached="true" variant="soft" tone="neutral" size="sm">
<bmx-button value="bold">Bold</bmx-button>
<bmx-button value="italic">Italic</bmx-button>
<bmx-button value="underline">Underline</bmx-button>
</bmx-button-group>
<bmx-split-button id="bar-save" label="Save" size="sm">Save</bmx-split-button>
<script>
const splitButton = document.getElementById('bar-save');
splitButton.items = [ /* your items */ ];
</script>
Button group
Three widgets in one tag. One Tab stop; arrow keys move within it, Home and End jump to the ends, disabled items are skipped.
Guards work inside a group for free: the rejected button stops the click inside its own shadow root, so the group never sees it.
Get the code
<script src="/assets/bmx-components.min.js"></script> <bmx-button-group label="Document actions" attached="true" variant="outline" tone="neutral"> <bmx-button>Cut</bmx-button> <bmx-button>Copy</bmx-button> <bmx-button>Paste</bmx-button> <bmx-button disabled="true">Undo</bmx-button> </bmx-button-group> <bmx-button-group id="view-mode" label="View mode" selection="single" value="grid" attached="true" variant="outline" tone="neutral"> <bmx-button value="list">List</bmx-button> <bmx-button value="grid">Grid</bmx-button> <bmx-button value="board">Board</bmx-button> </bmx-button-group> <bmx-button-group label="Text formatting" selection="multiple" attached="true" variant="soft" tone="neutral"> <bmx-button value="bold" label="Bold" icon-only="true"><svg slot="start" viewBox="0 0 24 24"><path d="M6 4h8a4 4 0 0 1 0 8H6zM6 12h9a4 4 0 0 1 0 8H6z" /></svg></bmx-button> <bmx-button value="italic" label="Italic" icon-only="true"><svg slot="start" viewBox="0 0 24 24"><path d="M19 4h-9M14 20H5M15 4 9 20" /></svg></bmx-button> <bmx-button value="underline" label="Underline" icon-only="true"><svg slot="start" viewBox="0 0 24 24"><path d="M6 4v6a6 6 0 0 0 12 0V4M4 21h16" /></svg></bmx-button> </bmx-button-group> <bmx-button-group label="Navigation" orientation="vertical" attached="true" variant="outline" tone="neutral"> <bmx-button>Overview</bmx-button> <bmx-button>Members</bmx-button> <bmx-button>Billing</bmx-button> </bmx-button-group> <bmx-button-group label="Spaced actions" variant="soft"> <bmx-button tone="success">Approve</bmx-button> <bmx-button tone="danger" confirm="Confirm reject">Reject</bmx-button> </bmx-button-group>
Split button
A default action joined to a menu of alternatives — and the menu is bmx-menu, delegated rather than duplicated, so it cascades and it is in the browser's top layer.
Keyboard: Down or Up opens the menu, arrows move, letters search, Right opens a submenu, Enter chooses, Escape
closes one level, Tab dismisses. Export has two levels — an entry with its own
items is a submenu, so a second level is data rather than markup, and the event reports the route
to what was chosen rather than only its position in the list it sits in. Keep them shallow: a split button is a
short list of alternatives to one obvious action, and a menu deep enough to need navigating is a sign the
obvious action is not obvious.
Get the code
<script src="/assets/bmx-components.min.js"></script>
<bmx-split-button id="split-save" label="Save">Save</bmx-split-button>
<bmx-split-button id="split-export" variant="outline" tone="neutral" label="Export" placement="bottom-end">Export</bmx-split-button>
<bmx-split-button id="split-danger" variant="soft" tone="danger" label="Archive" size="sm">Archive</bmx-split-button>
<script>
const splitButton = document.getElementById('split-save');
splitButton.items = [ /* your items */ ];
const splitButton2 = document.getElementById('split-export');
splitButton2.items = [ /* your items */ ];
const splitButton3 = document.getElementById('split-danger');
splitButton3.items = [ /* your items */ ];
</script>