cnc-toolbar
The strip of small icon buttons that sits above an editor, a preview or a canvas. You give it an
array of items and it draws the row — buttons, vertical rules, and the gap that pushes the
rest to the right edge. It owns none of the behaviour: every click leaves as one
toolbaraction
event carrying the id of the button that was pressed, and what happens next is yours.
Quick start
items
is an array, so it is bound with $items rather than written as a plain attribute.
Each button needs an id — that is what comes back on the event — and an
icon. Everything else is optional.
Nothing pressed yet
<cnc-toolbar
$items="[
{ id: 'refresh', icon: 'lucide/refresh-cw', title: 'Refresh' },
{ id: 'copy', icon: 'lucide/copy', title: 'Copy path' }
]"
@toolbaraction="run($event.detail.id)"
></cnc-toolbar>
The bar draws its own bottom border and background, and is one fixed row tall. It is meant to sit flush against the thing it commands, not to be padded away from it.
The three items
An entry's type picks which of three things it is. Omitting type means
button, which is why the common case reads as plain data.
| type | Renders |
|---|---|
| button (or omitted) |
An icon button. The only item that emits anything. |
| divider | A short vertical rule. Purely visual grouping — it takes no id and is not clickable. |
| spacer | An elastic gap that eats the leftover width, pushing everything after it to the right edge. Two spacers split the slack between them. |
Button fields
| Field | Purpose |
|---|---|
| id |
What arrives as $event.detail.id, and the handle the imperative API takes.
A button without one still renders and still emits — with
id: undefined, which no handler can tell apart from any other. Give every
button an id.
|
| icon |
Any cnc-icon name, drawn small. There is no label: the icon is the whole
button.
|
| title | The native tooltip, and — since the button has no text — the only thing naming it. Not optional in practice. |
| active | Draws the button pressed: a filled background and a darker icon. The initial state only; moving it later is the imperative API's job. |
| activeIcon |
A second icon shown in place of icon while the button is active — a moon
that becomes a sun. Both are rendered; only one is visible.
|
| hidden | Renders the button but keeps it out of the layout, for a tool that only applies to some documents. The button exists, so it can be revealed later without rebuilding the bar. |
Things to know
-
The row is fixed after the first render.
init()copiesitemsinto the private array the template loops over, andinit()runs once. Adding, removing or relabelling an item later updates nothing, whether you bound it with$itemsor:items. Server-render the tools you need — including the ones that starthidden— and drive them with_toolbar. -
The buttons are icons and nothing else.
There is no label field and no text variant.
titleis the only name a button has, for a pointer and for a screen reader alike, so a button without one is unusable rather than merely undocumented. -
Active is paint, not state.
A pressed-in button carries no
aria-pressedand reports nothing back — the widget does not track which of your buttons is on. What is active is whatever you last set, and keeping that in step with your own model is your side of the contract. -
hiddenhides, it does not remove. The button stays in the DOM and stays findable bygetButton; only the layout skips it. That is what makes revealing it later a one-line call. -
Nested toolbars bubble through their parent.
A
cnc-toolbarslotted inside another one emits from itself, but the event still bubbles up through the outer bar, so a handler there sees both. Check$event.targetif you nest, or listen on the inner one directly. - The bar draws a bottom border, always. It is designed to be the top edge of a panel. Standing on its own, as on this page, that border reads as a stray rule; put it against the content it commands.
Usage guidance
Reach for cnc-toolbar when the actions are frequent, icon-sized and belong to one
surface — an editor, a preview frame, a canvas. It is deliberately thin: no labels, no
overflow menu, no keyboard roving, and no memory of what is on. If the actions need words, use
cnc-button-group; if they are a menu of rarely-used commands, use
cnc-context-menu, or a cnc-drop in the slot as
cnc-markdown-tiptap
does for its parameter list. Keep the row short enough to read at a glance — reach for
wrap
only when the tool count is genuinely the point.