Skip to content

Expose aria-pressed/aria-expanded on stateful command buttons - #808

Open
etuan wants to merge 1 commit into
embedpdf:v2from
etuan:fix/command-button-aria-state
Open

etuan wants to merge 1 commit into
embedpdf:v2from
etuan:fix/command-button-aria-state

Conversation

@etuan

@etuan etuan commented Sep 11, 2026

Copy link
Copy Markdown

Summary

CommandButton renders active/disabled styling for every command, but never surfaced that state to assistive tech:

  • Toggle-style commands (Pan Mode, Pointer Mode) had no aria-pressed, so a screen reader announced them as a plain, stateless button — you couldn't tell whether pan mode was currently on.
  • Every menu- or panel-opening command (Document Menu, Page Settings, Zoom Menu, and, once I swept the file for the same shape, the annotation/shapes/form tools overflow menus, the tab overflow menu, and every sidebar panel toggle) had no aria-expanded.

Additionally, "Toggle Pan Mode" / "Toggle Pointer Mode" is an odd accessible name for a button whose pressed state is (now) announced separately — no other toggle in this toolbar says "Toggle" in its name.

Fix

  • @embedpdf/plugin-commands: add activeAriaState?: 'pressed' | 'expanded' to Command, resolved onto ResolvedCommand in commands-plugin.ts.
  • @embedpdf/snippet:
    • CommandButton maps activeAriaState to the matching ARIA prop (aria-pressed for 'pressed', aria-expanded for 'expanded') and spreads it onto Button.
    • commands.ts: mark pan:toggle/pointer:toggle as 'pressed', and every toggleMenu(...)/toggleSidebar(...)-backed command as 'expanded' (21 commands total — see diff).
    • translations.ts: pan.toggle/pointer.toggle labels changed from "Toggle Pan Mode"/"Toggle Pointer Mode" to "Pan Mode"/"Pointer Mode".
    • mode-select-button.tsx renders its trigger directly rather than through CommandButton, so it gets the same aria-expanded fix applied inline.

Deliberately out of scope

mode:view / mode:annotate / mode:shapes / mode:insert / mode:form are excluded. Their active represents which mutually-exclusive editing mode is currently selected, not a pressed or expanded state — that's a tabs/radiogroup shape (aria-selected/aria-checked on a role="tablist"/role="radiogroup" structure), which is a bigger, separate change I didn't want to fold into this one.

Steps to reproduce (before this fix)

  1. Open a document with a screen reader running.
  2. Tab to the Pan Mode button and activate it, then Tab back to it (or re-focus). It announces as a plain button with no pressed state, and its name reads "Toggle Pan Mode" rather than "Pan Mode".
  3. Tab to the Document Menu / Page Settings / Zoom Menu button and open it. It announces as a plain button with no expanded/collapsed state.

WCAG 4.1.2 (Name, Role, Value).

Changesets

Two changeset files included, one per affected package, per .agents/skills/embedpdf-changesets.

Testing

Manually verified per the repro steps above; no existing test suite for this package.

CommandButton rendered active/disabled styling for every command but
never surfaced the underlying state to assistive tech: toggle-style
commands (Pan Mode, Pointer Mode) had no aria-pressed, and every
menu- or panel-opening command (Document Menu, Page Settings, Zoom
Menu, the tools overflow menus, sidebar panels, ...) had no
aria-expanded, so a screen reader announced a plain unstated button
for all of them.

Add activeAriaState to Command ('pressed' | 'expanded') so a command
declares which ARIA state its `active` value maps to, and have
CommandButton apply it. Pan Mode and Pointer Mode also drop the
"Toggle " prefix from their label now that the pressed state is
announced separately, matching how no other toggle in this toolbar
says "Toggle" in its name.

mode-select-button.tsx renders its trigger directly rather than
through CommandButton, so it gets the same aria-expanded fix inline.

Deliberately left out: the mode:view/annotate/shapes/insert/form
commands, whose `active` represents which mutually-exclusive editing
mode is selected rather than a pressed or expanded state — that shape
fits a tablist/radiogroup pattern, which is a bigger, separate change.
@vercel

vercel Bot commented Sep 11, 2026

Copy link
Copy Markdown

@etuan is attempting to deploy a commit to the CloudPDF Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant