# Toolbar The bar of actions at the edge of a working surface. - Group: Surfaces - Import: `import { Toolbar } from '@misoto22/design'` - Page: https://ui.misoto22.com/components/toolbar/ - Related: button, app-shell, card ## When to reach for it A form’s actions that must stay in reach while the form scrolls, or a filter bar over a list. Not a page header — that is AppShell. ## Anatomy - **Bar** (required) — A
named by label, wrapping its children on a flex row at --z-sticky. It is not role="toolbar": that role promises a single tab stop with arrow keys between the controls, and this implements no such thing. - **Ground** (required) — Opaque --paper, and deliberately not a blur. Content scrolls UNDER this bar, so anything translucent puts the last row of a table behind the submit button and makes both unreadable. - **Edge** (required) — A --rule-2 hairline on the side the bar sticks to: border-t for bottom, border-b for top. position="static" keeps the rule and drops the stickiness. - **Actions** (required) — children, on a flex-wrap row with a --gap of 3. align places them along the inline axis and defaults to end, which is where a form’s primary action goes. ## Best practices ### Do - Write label as what the bar IS — "Form actions", "List filters". A group with no name is announced as "group", and a page with two of them announces the same nothing twice. - Give the scrolling ancestor a height for position="bottom". A sticky element sticks within its scroll container, so a bar inside a container that is as tall as its content has nothing to stick to and simply sits at the end. - Keep it to the actions. A bar that has grown a title, a status and a breadcrumb is a page header, and a page header that follows the reader down the screen is a page with less of itself visible. ### Don’t - Do not add role="toolbar" through props. The role tells a screen-reader user that arrow keys move between the controls; nothing here implements roving tabindex, so those keys would do nothing and the promise would be false. - Do not make the ground translucent to "let the content show through". The content it would show through is the row the reader is trying to read, and the button they are trying to press. ## Accessibility - label is required and becomes the group’s accessible name, so a page with a filter bar and an action bar announces two distinct things. - Every control keeps its own place in the tab order, because the bar deliberately does not claim role="toolbar" and its single-tab-stop contract. - The ground is opaque, so a control on the bar always meets its contrast ratio against --paper rather than against whatever happens to be scrolling behind it. ## Keyboard - Tab — Reaches each control in turn — the bar itself is not a stop. ## Toolbar The bar of actions at the edge of a working surface. Two page templates built the same thing independently — a sticky strip on `--paper` with a rule along the edge it sticks to — which is the signal that it belongs here rather than in each of them. The ground is opaque `--paper` and not a blur. Content scrolls UNDER this bar, so anything translucent puts the last row of a table behind the submit button and makes both unreadable; `FloatingIconButton` blurs because it floats over a gap, which is a different problem. It is not `role="toolbar"`. That role's contract is a single tab stop with arrow keys moving between the controls inside it, and this implements no such thing — declaring the role without the behaviour tells a screen-reader user to press arrow keys that do nothing. It is a named `group`, so the controls keep their own places in the tab order and the bar is still announced. ### Props - `label` (required) — `string`. Names the bar for a screen reader — "Form actions", "List filters". Required, because a group with no name is announced as "group" and a page with two of them is a page with two identical announcements. - `position` — `ToolbarPosition` default `'bottom'`. Which edge the bar sticks to. See ToolbarPosition. - `align` — `ToolbarAlign` default `'end'`. Where the contents sit along the inline axis. - `children` (required) — `ReactNode`. Also accepts: `HTMLAttributes`. ## Example — form actions ```tsx import { Button, Text, Toolbar } from '@misoto22/design'
Scroll this panel — the bar below stays where it is. A sticky element sticks within its scroll container, so the container needs a height of its own for the bar to have anything to stick to. Keep the bar to the actions. One that has grown a title and a breadcrumb is a page header following the reader down the screen.
``` ## Example — a filter bar on top ```tsx import { Button, NativeSelect, Text, Toolbar } from '@misoto22/design'
    {ROWS.map((sha) => (
  • {sha}
  • ))}
``` ## Example — every control is a tab stop ```tsx import { Button, Text, Toolbar } from '@misoto22/design' 3 rows selected
```