Split Buttons for Primary Row Actions

Permalink to "Split Buttons for Primary Row Actions"

A split button pairs a primary action (“Approve”) with a dropdown of related actions (“Approve with comment”, “Approve and assign”). In tables, it saves space while keeping the common action one click away. Accessibility problems arise when the split button is built as a single element with two click zones, so keyboard users can only reach one half and screen readers announce one name. The robust approach is two real buttons in a labelled group.

This guide belongs to context menus and row actions.

Spec reference

Permalink to "Spec reference"
  • ARIA menu button pattern — the dropdown half has aria-haspopup="menu" and aria-expanded.
  • SC 2.1.1 (A) — both halves operable from the keyboard.
  • SC 4.1.2 (A) — both halves named.
  • SC 2.5.8 (AA) — the dropdown half, often narrow, still meets 24 px.
Split button in a table row Mock: an invoice row with an Approve button and an adjacent arrow button; the arrow button is focused and its menu shows Approve with comment, Approve and assign, and Reject. Split button in a table rowINV-10421,240.00Approve▾ @ "More approve…Approve with…Approve and assignReject
Two buttons, one visual unit.

When to use — and when not to

Permalink to "When to use — and when not to"

Use split buttons when one action is clearly most common and related variants exist. Keep the menu short (two to five items).

Do not use a split button when no action is primary; a plain menu button is clearer. The misapplication: one div with a click handler that checks the click position to decide between primary action and menu.

Single element versus two buttons Comparison: a single element with two click zones exposes one name and one keyboard action; two buttons in a group expose both actions to every user. Single element versus two buttons✗ One element, two zonesOne nameKeyboard reaches one halfPosition-based clicks✓ Two buttons in a group"Approve INV-1042" + "More approve actions forINV-1042"Both reachableNormal button semantics

Annotated code example

Permalink to "Annotated code example"
<div role="group" aria-label="Approve actions for INV-1042" class="split">
  <button type="button" class="split-main">Approve<span class="sr-only"> INV-1042</span></button>
  <button type="button" class="split-toggle" aria-haspopup="menu" aria-expanded="false"
          aria-controls="m-1042" aria-label="More approve actions for INV-1042">▾</button>
  <ul role="menu" id="m-1042" hidden>
    <li role="menuitem" tabindex="-1">Approve with comment</li>
    <li role="menuitem" tabindex="-1">Approve and assign</li>
    <li role="separator"></li>
    <li role="menuitem" tabindex="-1">Reject</li>
  </ul>
</div>
.split-toggle { min-inline-size: 24px; min-block-size: 24px; }    /* SC 2.5.8 */
.split-main:focus-visible, .split-toggle:focus-visible { outline: 3px solid currentColor; outline-offset: 2px; }

Keyboard & AT behaviour

Permalink to "Keyboard & AT behaviour"
Key On primary On toggle In menu
Enter / Space Runs Approve Opens menu, focus first item Activates item
Down Arrow — Opens menu Next item
Escape — — Closes, focus to toggle
Tab To toggle To next control Closes menu
Choosing a secondary action Sequence: the user tabs to the toggle, presses Down Arrow, the menu opens with focus on the first item, the user selects Approve and assign, the menu closes and focus returns to the toggle, a status announces the result. Choosing a secondary actionUserToggleMenuStatusDown Arrowopen, focus "Approve withcomment"Down, Enter on "Approve and assign"close, focus returns"INV-1042 approved and assigned"
Focus returns to the control that opened the menu.

Step-by-step implementation

Permalink to "Step-by-step implementation"
  1. Group the halves. Wrap both buttons in an element with role="group" and a label, so screen reader users understand they belong together.
  2. Name both halves with the row. In tables, names without row identifiers are ambiguous: “Approve INV-1042” and “More approve actions for INV-1042”.
  3. Implement the menu pattern. Open on Enter, Space or Down Arrow; arrow keys move; Home and End jump; Escape closes; focus returns to the toggle.
  4. Style as one unit, focus as two. The halves look joined, but each shows its own focus ring.
  5. Confirm the result. After an action, announce the outcome politely and keep focus on the split button or move it to a logical place if the row disappears.

Integration context

Permalink to "Integration context"

The menu pattern is described in row action menus with the menu button pattern. Destructive menu items should follow confirming and undoing destructive row actions. Focus after rows disappear is covered in preserving focus when rows are deleted. Toolbars that contain split buttons follow toolbar pattern for grid actions.

Gotchas

Permalink to "Gotchas"

Inside a grid. In an ARIA grid, the split button lives inside a cell; Tab or Enter moves into the cell, and arrow keys may be captured by the grid. Document how users reach the toggle.

Menu positioning. Menus opening near the bottom of a scroll container can be clipped; flip upward.

Disabled primary. If the primary action is unavailable, disable it with aria-disabled and keep the menu available if its items apply.

Edge cases

Permalink to "Edge cases"
  • Primary changes per row. If the primary action depends on row state (“Approve” versus “Reopen”), the toggle’s name must change with it.
  • Long menu labels. Keep items short; long labels wrap awkwardly in narrow cells.
  • Bulk actions. For many rows, use a batch action bar rather than repeating split buttons.

Worked example

Permalink to "Worked example"

An expense approval table used a custom split button component made of one element with two click zones. Keyboard users could approve but never reach “Approve with comment”, which managers were required to use for large claims. The team rebuilt it as two buttons in a labelled group with the menu button pattern and row-specific names. Keyboard and screen reader users could use all approval options.

Common mistakes

Permalink to "Common mistakes"
  • One element for both halves.
  • Unnamed toggle (“▾”).
  • Menu without arrow key support.
  • Focus not returned after closing.

Testing checklist

Permalink to "Testing checklist"

Testing it with a screen reader

Permalink to "Testing it with a screen reader"

With NVDA, tab to the primary button and confirm “Approve INV-1042, button”, then tab to the toggle and confirm “More approve actions for INV-1042, menu button, collapsed”. Press Down Arrow and confirm the menu opens with the first item announced. Arrow through items, press Escape and check focus returns to the toggle. In JAWS, confirm that “group” and its label are announced on entry. In VoiceOver, verify “pop-up button” semantics and that menu items are read.

Choosing the primary action

Permalink to "Choosing the primary action"

Base the primary action on what users actually do most often in that row state, using analytics where possible. If the most common action is destructive, do not make it the primary half; a misclick or stray Enter should never delete data. When the primary action varies by row, keep the visual label short and consistent in length so the toggle does not jump around the column. And review the choice periodically; workflows change, and a primary action that was right at launch may become the least used.

Visual design of the two halves

Permalink to "Visual design of the two halves"

The halves should read as one control and two targets at once. A thin divider line between them, with at least 3:1 contrast against the button fill, tells sighted users there are two parts. Give the toggle a chevron icon that rotates or changes when the menu is open, mirroring aria-expanded. Keep each half’s hover and focus states independent so the user can see which half will activate. In dense tables, resist shrinking the toggle to a sliver; 24 pixels wide is the floor, and wider is easier for everyone.

Design system notes

Permalink to "Design system notes"

Provide a split button component that requires a row-context label and composes the menu button primitive, so naming and keyboard support are consistent.

FAQ

Permalink to "FAQ"
Should the split button be one tab stop?

No. Two tab stops, so each half is reachable.

Can the toggle be an icon only?

Yes, with an accessible name.

Is role group necessary?

It helps users understand the pair; it is recommended.

What does Down Arrow do on the primary half?

Nothing by default; some designs open the menu from either half, but be consistent.

Can the menu contain checkboxes?

Use menuitemcheckbox for toggles, but keep split button menus to actions.

How do I handle many rows?

Consider one shared toolbar acting on the selected row instead.

Permalink to "Related"

← Back to Row Actions & Context Menus