A labelled bar of related tools for a view, a selection or an editor: what it acts on, groups of tools, and its main action, moved through with the arrow keys as one Tab stop.
Options
| Choice | Values | Default | What it does |
|---|---|---|---|
placementsmbk-toolbar--placement-<value> | floatingdocked | floating | Floating: a raised bar as wide as its tools, with its rim, rounded corners and shadow, over a page, a map or a text selection. Docked: flat across the top of a panel, a line under it, the actions pushed to its end. Where it sticks (the top, the foot of the window) is the page’s to set. Figma: Placement. |
Examples
Usage
What the bar acts on and how many, four commands, the period, then the overflow menu and the one main action. Tab reaches the bar once; the arrows move along it.
Applied filters
A search field, the filters opener with its count and the sort, and under them the filters applied with the number of results they leave. The search field keeps its own tab stop and arrow keys. Remove a chip and the count is read out.
Selection
The count says what the actions apply to, in words, and is read out as it changes. The cross clears the selection; Select all takes every item.
Editor
The saving status, read out as it changes; the text tools as toggles, each saying whether it is on; undo and redo; and a split button for the main action.
Calendar
Today, the previous and next month, and the month shown, then the period.
Canvas
Back and the trail of pages, the zoom tools, undo and redo, and a favorite that stays on.
Guidelines
Purpose
A labelled bar of related tools for one view, one selection or one editor: what the bar acts on, a group or two of tools, and its main action. A keyboard reaches it with one Tab and moves along it with the arrow keys, so a bar of twenty tools costs one stop, not twenty. It differs from a button group, which is a few actions side by side with a Tab stop each, and from an app bar, which is the application's own navigation and is not a toolbar.
When to use
- A view of a list, a board, a calendar or a map with commands for it: refresh, search, download, change the period.
- An editor's formatting and history tools, with the saving status beside them.
- A selection's bulk actions, with the selection's size in words.
- The header of a panel: back, its title, Cancel and Save.
- A small bar floating over a canvas or a text selection.
When not to use
- The application's or the site's navigation. Use the app bar, the header or the sidebar.
- Two or three actions under a form or in a dialog. Use a button group.
- Search, filters and sort above a list, with nothing else. Use the filter bar, a search landmark whose controls each keep their own Tab stop.
- A row of choices of which exactly one is on. Use a segmented control, inside a toolbar if it shares the bar with other tools.
Usage rules
Do
- Name the bar for what it acts on.
- Order the tools by how often they are used, and group related ones.
- Keep one main action, at the end; put the rest in the overflow menu.
- Use the small size of every control in it.
Don't
- Put two main actions in one bar.
- Make a command look pressed: only a toggle stays on.
- Hide every tool behind unnamed icons.
- Put more than one text field in one bar: each is a Tab stop of its own.
Name every icon-only tool, and let a tool that stays on say so.
Two main actions compete for the same press; keep one, and put the rest in the overflow menu.
Anatomy
- The block: the bar, labelled, on the raised surface. Floating, it is as wide as its tools, with a rim, rounded corners and a shadow; docked, it runs flat across the top of a panel with a line under it.
row: the zones, side by side. When the space is too narrow for them all, the row wraps onto a second line rather than pushing the page sideways.context: what the bar acts on, always first. A title with an optional icon and a count, a back button with a trail of pages, the selection in words, a status, a person, a notice of the mode the page is in, a summary, or a list to pick from.tools: one group of tools, after a vertical divider. Icon-only tools (the small ghost icon button, or the ghost toggle button for a tool that stays on, each named in a tooltip), buttons with words, a segmented control, a search field and a filters opener, chips, a switch, a slider, the value of a setting. A bar may hold two.actions: always last, after a divider: the overflow menu and at most one main action, a split button, a pair such as Cancel and Save, a few icon buttons, a close button, or stop and start. Docked, it sits at the bar's end.divider: the vertical divider that starts a tools or actions zone. A divider that would start the whole row is not drawn.title,value: what the bar acts on, the period a calendar shows, a setting's value, in Label MD.icongoes before a title.text,meta: a status, a summary, a setting's name, a note beside an action, in Body SM;metais the quieter note after it, such as when it last changed.status: a status's dot, or the spinner's ring while busy, with its words and its note.field: a field in the bar, such as the list that picks what it acts on or a search field, at the width the bar gives a field.details: an optional row under the zones: the filters applied as removable chips with Clear all, then the result count at the row's end, the filter bar's own parts.wide-only,narrow-only: keep a zone, or anything wrapped in one of them, to a wide or a narrow space.
Every control in the bar is the system's own component at its small size, 32 tall, so the bar is 48 tall.
Behavior
The zones sit in their order whatever the page passes: context first, actions last. Each tools group and the actions start with a divider unless it is turned off. In a narrow space a part marked wide-only goes and one marked narrow-only comes: the tools can fold into one opener, such as a Filters button with a count, and the overflow menu can take the commands that went. Where a bar sticks, at the top of a scrolling panel or the foot of the window, is the page's to set.
An icon-only tool can show its key after its name in its tooltip, such as "Zoom in" then "]", drawn as a small key cap; the page listens for the key. The overflow menu opens a small dropdown lined up with the bar's end. The value of a setting is a small secondary button with a caret, which opens a popover holding the control that changes it, such as a fill slider.
A status in the bar, the selection's count and the result count are read out when they change. Nothing moves when the bar appears.
Accessibility
What it does for you
- The bar has the toolbar role and a name of its own, given as its label.
- It is one Tab stop: Tab reaches the bar once, on the tool used last, or the first. Left and Right move between its buttons, toggles, links, chips and choices; Home and End go to the first and the last. The arrows follow the reading direction. The next Tab leaves the bar.
- A field inside the bar, such as a search field, a select, a slider or a number field, keeps its own Tab stop and its own arrow keys, so typing and choosing work as anywhere else; the arrows pass over it. A menu or a panel a tool opens keeps its own keys.
- A tool that stays on is a toggle button and says whether it is pressed; a command never does. Its name stays the same on and off.
- A tool written as the toolbar's own icon-only tool shows its name in a tooltip under it while it is pointed at or reached with the keyboard, and the overflow button does the same; the tooltip is hidden from screen readers, which hear the name once.
- An icon-only tool given a key (
shortcut) shows it after its name in the tooltip, as a key cap, and carries it asaria-keyshortcuts, so a screen reader can say it too. A segmented control in the bar can take a key the same way. - A zone given a label is a named group, so a screen reader says which group a tool is in.
- The selection's count says the scope in words ("3 selected") and is read out as it changes, so the actions after it never act on an unstated selection.
- The overflow menu keeps each tool's name, tick and disabled state; its button is named "More actions" unless given another name.
What you must do
- Give every toolbar on a page a different name.
- Give every icon-only tool a name.
- Listen for a tool's key yourself, and press the tool when it comes: the toolbar only shows and names the key. Take a single key only outside text fields, and never one a screen reader or the browser needs.
- Hand-written markup must give the bar the toolbar role and a name, and move focus with the arrow keys itself; without script every tool is its own Tab stop.
How to test
- Tab: focus enters the bar once, on the tool used last or the first, with the focus ring. Press Left and Right, Home and End to move along it; the next Tab leaves the bar.
- Move onto a search field or a select in the bar: it keeps its own Tab stop, and typing and choosing work as anywhere else.
- With a screen reader, the bar is read as a toolbar by its name, a tool that stays on as pressed or not pressed, a labeled zone as a named group, and a changing count is read out as it changes.
- In a right-to-left page, the arrows follow the reading direction.
- With text at 200% and the page 320 pixels wide, tools fold into the opener and overflow menu where the page marks them so.
Keyboard
| Key | What it does |
|---|---|
| Tab | Moves focus into the bar, onto the tool used last or the first; pressed again, leaves the bar. A field in the bar keeps its own Tab stop. |
| Left ArroworRight Arrow | Moves to the previous or next tool, following the reading direction. |
| HomeorEnd | Moves to the first or the last tool. |
| EnterorSpace | Presses the tool in focus. |
Source
The files the toolbar is made of. Its folder on GitHub (opens in a new tab) holds them with its wrapper and its document.
Stylesheet
/* smbk-toolbar — Toolbar. Figma "Toolbar" 7343:1575 (Placement: Floating, Docked), holding ToolbarContext 7339:1264,
* ToolbarTools 7341:1454 and ToolbarActions 7342:1452.
* A column on the raised surface, the medium inset all round: a row of zones (what the bar acts on, one or two groups
* of tools, the overflow menu and the main action), and under it, when there is one, a row of details (the filters
* applied and the result count). Each zone is a row of the system's own controls at their small size, 32 tall, the
* small inline space apart; zones sit the medium inline space apart, and a tools or actions zone starts with a
* vertical divider. Every tool keeps its own look: this sheet draws only the bar, the zones and a few words.
* The edge is drawn inside, as Figma draws it, so each padding gives its width back: the bar is 48 tall.
* Narrow (the layout switch): a part marked wide-only goes, and one marked narrow-only comes, so a phone can fold the
* tools into one opener or the overflow menu. */
.smbk-toolbar {
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: var(--smbk-space-inline-md);
inline-size: fit-content;
max-inline-size: 100%;
padding: calc(var(--smbk-space-inset-md) - var(--smbk-border-width-hairline));
border: var(--smbk-border-width-hairline) solid var(--smbk-color-border-rim);
border-radius: var(--smbk-radius-container-md);
background: var(--smbk-color-surface-raised);
box-shadow: var(--smbk-elevation-raised);
color: var(--smbk-color-text-default);
}
/* Docked: flat across the top of a panel, as wide as it, with a subtle line under it and no corners or shadow. */
.smbk-toolbar--placement-docked {
inline-size: auto;
padding: var(--smbk-space-inset-md) var(--smbk-space-inset-md) calc(var(--smbk-space-inset-md) - var(--smbk-border-width-hairline));
border: 0;
border-block-end: var(--smbk-border-width-hairline) solid var(--smbk-color-border-subtle);
border-radius: 0;
box-shadow: none;
}
/* ---- the row of zones: wraps onto a second line rather than pushing the page sideways ---- */
.smbk-toolbar__row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--smbk-space-inline-md);
min-inline-size: 0;
}
.smbk-toolbar__context,
.smbk-toolbar__tools,
.smbk-toolbar__actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--smbk-space-inline-sm);
min-block-size: var(--smbk-size-control-sm);
min-inline-size: 0;
}
/* Docked: the free room goes before the actions, so they sit at the bar's end. */
.smbk-toolbar--placement-docked .smbk-toolbar__actions {
margin-inline-start: auto;
}
/* The divider that starts a zone: the vertical divider, 24 tall in the 32 row, as Figma draws it. A divider that
would start the whole row has nothing to divide, so it goes. */
.smbk-toolbar .smbk-toolbar__divider {
align-self: center;
block-size: var(--smbk-size-24);
min-block-size: var(--smbk-size-20);
}
.smbk-toolbar__row > :first-child > .smbk-toolbar__divider:first-child {
display: none;
}
/* ---- the bar's own words ---- */
/* What the bar acts on, the period a calendar shows, a setting's value: Label MD in the primary text color. */
.smbk-toolbar__title,
.smbk-toolbar__value {
color: var(--smbk-color-text-default);
font-family: var(--smbk-typography-label-md-font-family);
font-size: var(--smbk-typography-label-md-font-size);
font-weight: var(--smbk-typography-label-md-font-weight);
line-height: var(--smbk-typography-label-md-line-height);
letter-spacing: var(--smbk-typography-label-md-letter-spacing);
white-space: nowrap;
}
/* Figures of one width in all the bar's words, so a number that changes in place (a count of what is selected, a
status such as "Updated 12 s ago", a value) never moves the words after it. */
.smbk-toolbar__title,
.smbk-toolbar__value,
.smbk-toolbar__text,
.smbk-toolbar__meta {
font-variant-numeric: tabular-nums;
}
/* A line of status, a summary, a setting's name, a note beside an action: Body SM in the secondary text color. */
.smbk-toolbar__text,
.smbk-toolbar__meta {
color: var(--smbk-color-text-subtle);
font-family: var(--smbk-typography-body-sm-font-family);
font-size: var(--smbk-typography-body-sm-font-size);
font-weight: var(--smbk-typography-body-sm-font-weight);
line-height: var(--smbk-typography-body-sm-line-height);
letter-spacing: var(--smbk-typography-body-sm-letter-spacing);
white-space: nowrap;
}
/* Something quieter after it, such as when it last changed: the same type in the faintest text color. */
.smbk-toolbar__meta {
color: var(--smbk-color-text-subtlest);
}
/* An icon before the title, the size Label MD's text takes. */
.smbk-toolbar .smbk-toolbar__icon {
flex: none;
font-size: var(--smbk-size-icon-sm);
color: var(--smbk-color-icon-default);
}
/* A status: its dot or the spinner's ring, its words, and a quieter note, read out when they change. */
.smbk-toolbar__status {
display: inline-flex;
align-items: center;
gap: var(--smbk-space-inline-sm);
}
/* A field in the bar (a list to pick what it acts on, a search field, a time range), at the width Figma draws it,
since a field fills the room it is given; never wider than the bar. */
.smbk-toolbar__field {
display: flex;
flex: none;
inline-size: var(--smbk-size-toolbar-field-width);
max-inline-size: 100%;
}
/* ---- the details under the row: the filters applied, then the result count at the end. Never wider than the
row: its own content does not widen the bar, and its chips wrap instead. ---- */
.smbk-toolbar__details {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--smbk-space-inline-sm);
contain: inline-size;
}
.smbk-toolbar__details > .smbk-filter-bar__count {
margin-inline-start: auto;
}
/* ---- wide and narrow ---- */
.smbk-toolbar .smbk-toolbar__narrow-only {
display: none;
}
@container style(--smbk-layout: narrow) {
.smbk-toolbar .smbk-toolbar__wide-only {
display: none;
}
/* Anything else marked narrow-only is a wrapper that lends its content to the row or the menu around it. */
.smbk-toolbar .smbk-toolbar__narrow-only {
display: contents;
}
.smbk-toolbar .smbk-toolbar__narrow-only:is(.smbk-toolbar__context, .smbk-toolbar__tools, .smbk-toolbar__actions) {
display: flex;
}
}
/* ---- touch: the tools stop their press areas halfway to each other ----
Every button takes an invisible press area on a touch screen. In the toolbar's rows of tools and actions, which
stand closer than that and wrap, each area reaches only halfway into the row's gap, side to side and up and down,
so every tool stays one press. Nothing drawn moves. */
@media (pointer: coarse) {
:is(.smbk-toolbar__tools, .smbk-toolbar__actions) .smbk-btn::after {
inset: max(min(0%, (100% - var(--smbk-size-target-comfortable)) / 2), var(--smbk-space-inline-sm) / -2);
}
:is(.smbk-toolbar__tools, .smbk-toolbar__actions) .smbk-segmented__item::after,
:is(.smbk-toolbar__tools, .smbk-toolbar__actions) .smbk-btn-group .smbk-btn::after {
inset-block: max(min(0%, (100% - var(--smbk-size-target-comfortable)) / 2), var(--smbk-space-inline-sm) / -2);
}
:is(.smbk-toolbar__tools, .smbk-toolbar__actions) :is(button, a[href]).smbk-chip::after {
inset: max(min(0%, (100% - var(--smbk-size-target-comfortable)) / 2), var(--smbk-space-inline-sm) / -2);
}
}Tokens it reads
| Token and CSS | Value | Preview | Foundation |
|---|---|---|---|
| space.inline.md--smbk-space-inline-md | comfortable: 8pxcompact: 6pxchanges with density | Spacing | |
| space.inset.md--smbk-space-inset-md | comfortable: 8pxcompact: 6pxchanges with density | Spacing | |
| border-width.hairline--smbk-border-width-hairline | 1px | Border width | |
| color.border.rim--smbk-color-border-rim | light: #ffffffe6dark: #ffffff0dchanges with theme | Color | |
| radius.container.md--smbk-radius-container-md | 12px | Radius | |
| color.surface.raised--smbk-color-surface-raised | light: #f9fafbdark: #232527changes with theme | Color | |
| elevation.raised--smbk-elevation-raised | light: 0px 2px 8px 1px #373d431a, 0px 1px 2px 0px #373d431adark: 0px 2px 8px 1px #00000026, 0px 1px 2px 0px #00000026 | Elevation | |
| color.text.default--smbk-color-text-default | light: #232527dark: #e7ebf0changes with theme | Color | |
| color.border.subtle--smbk-color-border-subtle | light: #c4cdd8dark: #2d3237changes with theme | Color | |
| space.inline.sm--smbk-space-inline-sm | comfortable: 6pxcompact: 4pxchanges with density | Spacing | |
| size.control.sm--smbk-size-control-sm | comfortable: 32pxcompact: 28pxchanges with density | Size | |
| size.24--smbk-size-24 | 24px | Size | |
| size.20--smbk-size-20 | 20px | Size | |
| typography.label-md--smbk-typography-label-md-font-family--smbk-typography-label-md-font-size--smbk-typography-label-md-font-weight--smbk-typography-label-md-line-height--smbk-typography-label-md-letter-spacing--smbk-typography-label-md-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 14pxfontWeight: 500lineHeight: 1.4letterSpacing: 0px | The quick brown fox | Typography |
| color.text.subtle--smbk-color-text-subtle | light: #4e555edark: #9ba5b1changes with theme | Color | |
| typography.body-sm--smbk-typography-body-sm-font-family--smbk-typography-body-sm-font-size--smbk-typography-body-sm-font-weight--smbk-typography-body-sm-line-height--smbk-typography-body-sm-letter-spacing--smbk-typography-body-sm-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 14pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px | The quick brown fox | Typography |
| color.text.subtlest--smbk-color-text-subtlest | light: #606a75dark: #7c8692changes with theme | Color | |
| size.icon.sm--smbk-size-icon-sm | 16px | Size | |
| color.icon.default--smbk-color-icon-default | light: #232527dark: #e7ebf0changes with theme | Color | |
| size.toolbar.field-width--smbk-size-toolbar-field-width | 200px | Size | |
| size.target.comfortable--smbk-size-target-comfortable | 44px | Size |
Manifest
{
"name": "toolbar",
"block": "smbk-toolbar",
"summary": "A labelled bar of related tools for a view, a selection or an editor: what it acts on, groups of tools, and its main action, moved through with the arrow keys as one Tab stop.",
"adapt": "css",
"axes": {
"placement": {
"values": [
"floating",
"docked"
],
"default": "floating",
"description": "Floating: a raised bar as wide as its tools, with its rim, rounded corners and shadow, over a page, a map or a text selection. Docked: flat across the top of a panel, a line under it, the actions pushed to its end. Where it sticks (the top, the foot of the window) is the page’s to set. Figma: Placement."
}
},
"flags": {},
"parts": [
"row",
"context",
"tools",
"actions",
"divider",
"icon",
"title",
"value",
"text",
"meta",
"status",
"field",
"details",
"wide-only",
"narrow-only"
],
"figmaNodeId": "7343:1575"
}