C-01 · Components · Actions
All elements · Button on the Components page
Last checked 2026-10-22 · Awaiting approval · Accessibility tests
Approve Button
Approving as
Button: command
C-01 OfficeThe transparent command button of ribbons, menus bars, panes and status bars (QEditor spec §2.3 C11). sm is the Full small row (1.5rem), md the Compact row (2rem), lg stacked the Full large button.
| Axis / element | Values |
|---|---|
data-variant="command" |
on .qf-button |
data-size |
sm 24px · md 32px (default) · lg 66px |
data-layout="stacked" |
icon over a two-line label (Full large) |
.qf-button__label · .qf-button__chevron |
label · menu chevron |
aria-pressed · aria-expanded · :disabled |
toggle · open menu · unavailable |
data-priority · data-collapsed |
1–4 · label / hidden (P-20) |
Guidance
When to use
- Use a button to run an action now: save, approve, send, refresh, delete.
- Use the contained primary button for the one main action in a dialog, pane or form.
- Use outlined, soft, text or ghost buttons for secondary actions next to the main one.
- Use an icon-only button in dense toolbars and table rows where the icon is well known and a name is set.
-
Use
data-variant="command"for commands in a ribbon, menu bar, task pane or status bar inside the office editors.
When to consider something else
- If the action takes the user to another page or record, use a link, not a button.
- If one action has a few close variants, use a split button so the default stays one click. See SplitButton.
-
If the control switches between a few views or formats, use a button group with
data-selection. See ButtonGroup. - If the control stores an on or off setting, use a toggle instead of a pressed button. See Toggle.
-
If the button opens a list of actions, pair it with a menu and set
aria-haspopup="menu". See Menu.
Usability
- Start labels with a verb and name the object when it is not obvious: "Save view", "Export".
- Keep one contained primary button per surface; in dialogs put Cancel on the left and the primary action on the right.
-
Use
data-color="error"for destructive actions and confirm them first with a confirmation. -
Use
data-loadingwhile an action runs, and keep the button width stable so the layout does not move. -
In the editors, set
data-priorityso the ribbon drops low-priority commands into the overflow first. -
Give a button that shows and hides a region in place (the search toolbar's Filters)
data-disclosurewitharia-expandedandaria-controls; while the region is open it reads as pressed in.
- Don't rely on color alone to tell buttons apart; the label must say what happens.
- Don't disable a button without telling the user why; show the reason nearby or in a rich tooltip.
-
Don't use
xs(24px) buttons as the main action, or pack them tightly in touch layouts. -
Don't mix Tabler icons into
.qf-officecommand buttons, or Fluent icons outside it. -
Don't put
data-disclosureon a button that opens a menu or a window (aria-haspopup), or usedata-selectedto draw a pressed state.
Accessibility
Keyboard
| Keys | Action |
|---|---|
| Tab |
Moves focus to the button. A command button inside a data-rove toolbar is one stop
for the whole toolbar.
|
| Enter or Space |
Activates the button; on a button with aria-pressed, flips the pressed state.
|
| Arrow Left or Arrow Right |
Inside a data-rove toolbar, moves to the previous or next command, wrapping at the
ends.
|
| Home or End | Inside a data-rove toolbar, moves to the first or last command. |
| Ctrl+Arrow Left or Ctrl+Arrow Right | Inside a ribbon, jumps to the first command of the previous or next group. |
| Escape | Closes the menu a command button opened and returns focus to the button. |
Roles and names
-
Use a native
<button>withtype="button"unless it submits a form. -
Icon-only buttons need
aria-label; the icon carriesaria-hidden="true". Office commands also settitleas the tooltip. -
A toggle button uses
aria-pressed="true|false"; a menu button usesaria-haspopup="menu"andaria-expanded. -
While
data-loadingis set, keep the visible label in the DOM so the name stays, setaria-busy="true", and ignore further activation; the static layer only blocks pointer input. -
A
__badgecount isaria-hidden; the button's accessible name carries it ("Filters, 2 applied"). The badge sits on the button's corner wherever the button is.
Focus
-
Focus shows the token ring (
--qf-focus-ring); command buttons use an inset outline so it is not clipped by the ribbon. -
A native
disabledbutton leaves the tab order. When a disabled command must stay reachable to read its reason, usearia-disabled="true"instead; both are styled. - After a menu or dialog the button opened closes, focus returns to the button.
Other requirements
- Text contrast must meet 4.5:1 (1.4.3). The a11y report lists success, warning and error fills and some outlined colors as known contrast debt in the classic and dark themes; check before using them for text.
-
Target size 2.5.8:
mdis 44px andsm32px;xsandsmcommand buttons are 24px, so keep at least 24px of spacing around them. -
The loading spinner animates;
prefers-reduced-motionshortens it (2.3.3). Announce the result of a long action in a live region (4.1.3). - A visible label must be part of the accessible name (2.5.3).
Test your implementation too. This guidance covers the element as specified here. How a screen composes it (labels, order, announcements) still needs its own keyboard and screen reader check.
Accessibility tests
Passed the gate (run 358b316 on 2026-10-22): no finding in any theme. Awaiting a person’s approval of the checklist below.
| Area | Automated | A person checks |
|---|---|---|
| General | Passed | Color is never the only cue, text alternatives say the same thing as the graphic, and the element reads the same in the light and the dark theme. |
| Zoom | Passed | At 200% zoom, at 320 CSS pixels wide and with the WCAG text-spacing overrides, nothing is lost, clipped or overlapping. |
| Keyboard | Passed | Every row of the Keyboard table works, focus is always visible and never trapped, and the tab order follows the reading order. (6 rows) |
| Screen reader | Passed | With NVDA or JAWS on Windows and VoiceOver on macOS, names, roles and states are announced as Roles and names says, and changes are announced once. (5 items) |
Approving. When the checklist passes on the checked run, record it with
bun run a11y:approve -- button --by "Your name" and commit
a11y/approvals.json with the rebuilt pages.
All test status
Package
Consumers link one stylesheet, css/classic/index.css, which includes every element. The
files below are where this element is defined.
| Source |
css/classic/components/actions.csscss/classic/components/button-states.csscss/classic/components/commands.css
|
|---|---|
| Specimens also use | Box · Card · Chip · Icon · Table · Text · Toolbar |
| Sprites | sprites/icons.svg · sprites/office-icons.svg |
| React | Not yet available: the React binding has not started. |
Latest updates
| Date | Version | Breaking | Description |
|---|---|---|---|
| 2026-10-20 | 0.1 | Yes |
Button: the pressed disclosure. A button that shows and hides a region in place (the search
toolbar's Filters) carries data-disclosure beside aria-expanded and
aria-controls; open, it reads as pressed in on every screen: an info-200 fill, the
selected border and an inset shade (--qf-shadow-pressed, dropped under the focus
ring). __badge (the applied-filter count, sized by --qf-badge-text)
now takes the button as its containing block wherever the button is
(components/button-states.css). Breaking: the inbox-only
.qf-search.qf-inbox .qf-button[data-selected] tint is gone; set
data-disclosure on the Filters button instead, classic skin
|