Platform Details
xa11y normalizes platform-specific APIs into a unified interface. This page documents how roles and actions map across platforms, and where behavior differs.
Role mapping
| xa11y Role | macOS (AX) | Linux (AT-SPI) | Windows (UIA) |
|---|---|---|---|
Window | AXWindow, AXDrawer | window, frame | WindowControlType |
Application | AXApplication | application | (root element) |
Button | AXButton | push button | ButtonControlType |
CheckBox | AXCheckBox | check box, check menu item | CheckBoxControlType |
RadioButton | AXRadioButton | radio button, radio menu item | RadioButtonControlType |
TextField | AXTextField, AXSecureTextField | entry, password text | EditControlType |
TextArea | AXTextArea | text | EditControlType |
StaticText | AXStaticText | label, static, caption | TextControlType |
ComboBox | AXComboBox, AXPopUpButton | combo box | ComboBoxControlType |
List | AXList, AXOutline | list, list box | ListControlType, TreeControlType |
ListItem | (via AXRow) | list item | ListItemControlType |
Menu | AXMenu | menu | MenuControlType |
MenuItem | AXMenuItem, AXMenuBarItem | menu item, tearoff menu item | MenuItemControlType |
MenuBar | AXMenuBar, AXMenuBarExtra | menu bar | MenuBarControlType |
Tab | (subrole AXTabButton) | page tab | TabItemControlType |
TabGroup | AXTabGroup | page tab list | TabControlType |
Table | AXTable | table, tree table | TableControlType, DataGridControlType |
TableRow | (via AXRow) | table row | DataItemControlType |
TableCell | AXCell | table cell, column/row header | HeaderItemControlType |
Toolbar | AXToolbar | tool bar | ToolBarControlType |
ScrollBar | AXScrollBar | scroll bar | ScrollBarControlType |
Slider | AXSlider | slider | SliderControlType |
Image | AXImage | image, icon | ImageControlType |
Link | AXLink | link | HyperlinkControlType |
Group | AXGroup, AXScrollArea, AXRadioGroup | panel, section, form, scroll pane | GroupControlType, PaneControlType |
Dialog | AXSheet (or subrole AXDialog) | dialog, file chooser | WindowControlType (with IsDialog) |
Alert | (subrole AXApplicationAlert) | alert, notification | (alert pattern) |
ProgressBar | AXProgressIndicator, AXBusyIndicator | progress bar | ProgressBarControlType |
TreeItem | AXDisclosureTriangle (or subrole AXOutlineRow) | tree item | TreeItemControlType |
WebArea | AXWebArea | document web, document frame | DocumentControlType |
Heading | AXHeading (or subrole) | heading | (landmark pattern) |
Separator | AXSplitter | separator | SeparatorControlType |
SplitGroup | AXSplitGroup | split pane | PaneControlType |
Switch | (subrole AXSwitch) | (inferred) | (inferred) |
SpinButton | AXIncrementor | spin button | SpinnerControlType |
Tooltip | AXToolTip | tooltip, tool tip | ToolTipControlType |
Status | AXStatusBar | status bar | StatusBarControlType |
Navigation | (landmark) | landmark, navigation | (landmark) |
Roles not recognized by a platform map to Unknown.
Action mapping
| xa11y Action | macOS | Linux (AT-SPI) | Windows (UIA) |
|---|---|---|---|
Press | AXPress | click, activate, press, invoke | InvokePattern |
Focus | set AXFocused=true | Component.GrabFocus | SetFocus |
Blur | set AXFocused=false | (not directly supported) | (not directly supported) |
Toggle | AXPress (on checkbox) | toggle, check, uncheck | TogglePattern |
Expand | AXShowMenu / AXPress | expand, open | ExpandCollapsePattern.Expand |
Collapse | AXCancel / AXPress | collapse, close | ExpandCollapsePattern.Collapse |
Select | AXPress | select | SelectionItemPattern |
SetValue | set AXValue attribute | Value.SetCurrentValue (numeric) / EditableText.ReplaceText (text) | RangeValuePattern / ValuePattern |
TypeText | set AXSelectedText | EditableText.InsertText | ValuePattern (splice) |
SetTextSelection | set AXSelectedTextRange | Text.SetSelection | TextPattern range ops |
Increment | AXIncrement | increment (or Value +step) | RangeValuePattern (+step) |
Decrement | AXDecrement | decrement (or Value -step) | RangeValuePattern (-step) |
ShowMenu | AXShowMenu | menu, showmenu, popup | ExpandCollapsePattern |
ScrollIntoView | (no-op — no AX equivalent) | Component.ScrollTo | ScrollItemPattern |
Platform caveats
ScrollIntoView
No direct equivalent on macOS — this action is a no-op. On Linux it uses Component.ScrollTo with top-edge alignment. On Windows it uses ScrollItemPattern.ScrollIntoView.
SetValue: text vs numeric
- macOS: Sets the
AXValueattribute directly — works for both text and numeric values. - Linux: The
ValueD-Bus interface only supportsf64. Text values requireEditableText.ReplaceText. If neither interface is available, returnsTextValueNotSupported. - Windows: Tries
RangeValuePatternfor numeric values, falls back toValuePattern.SetValuefor text.
TypeText
Inserts text via the accessibility API — never simulates keyboard events.
- macOS: Sets
AXSelectedText(replaces selection or inserts at cursor). - Linux: Calls
EditableText.InsertTextat the current caret offset. - Windows: Reads current value via
ValuePattern, splices in the new text, then callsSetValue.
Blur
- macOS: Sets
AXFocusedto false on the element. - Linux / Windows: No direct API equivalent. The action is not supported.
Coordinates
All platforms report bounds as screen coordinates with origin at the top-left of the primary display. Note:
- macOS reports in points, not pixels. On Retina displays, 1 point = 2 physical pixels.
- Windows / Linux report in physical pixels. Multi-monitor setups can produce negative coordinates for displays to the left or above the primary.
Name resolution
Platforms use different attributes to determine an element’s name:
- macOS: AXTitle, then AXDescription, then AXValue (for text elements).
- Linux: Accessible.Name, then Description. For
StaticTextwith no name, the first portion of the value is used. - Windows: CurrentName property.
Checked state
The tri-state checked value (Off / On / Mixed) is derived differently:
- macOS: Parses the
AXValueattribute —"0"= Off,"1"= On,"2"= Mixed. - Linux: Uses AT-SPI state bits —
Checked(0x10) andMixed(0x2000). - Windows: Reads
TogglePattern.CurrentToggleState— 0 = Off, 1 = On, 2 = Indeterminate.
GTK press fallback (Linux)
GTK 4 menu-button widgets (GtkMenuButton, AdwMenuButton, AdwSplitButton) present as an outer push button accessible that advertises NActions = 0 wrapping an inner toggle button that carries the real click action. Calling press() on the outer would normally raise ActionNotSupported. When the owning application identifies itself as GTK via Application.ToolkitName == "GTK", xa11y-linux instead walks a bounded slice of the outer’s subtree (BFS, depth 3, actionable roles only, name must match) and invokes the single actionable descendant it finds. The fallback is strictly scoped: it only runs inside GTK apps, only when the widget’s own Action interface is empty, and only when exactly one candidate matches — ambiguous subtrees still surface the original error.
Linux Electron and Chromium apps
On Linux, Electron and Chromium-based apps ship with their AT-SPI2 bridge disabled by default for performance. xa11y will connect to the app but its tree will contain only the top-level window — App.by_name("…").locator("button").count() returns 0 even though buttons are visible on screen.
To expose the full tree, launch the app with --force-renderer-accessibility:
# VS Codecode --force-renderer-accessibility
# Cursorcursor --force-renderer-accessibility
# Google Chrome / Chromiumgoogle-chrome --force-renderer-accessibilityRepresentative node counts on Ubuntu 24.04 + GNOME 46 (Wayland):
| App | Without flag | With flag |
|---|---|---|
| VS Code | 1 | 140 |
| Cursor | 1 | 116 |
| Chrome | 1 | 210 |
Native GTK apps (Nautilus, gnome-terminal, GNOME Calculator, gnome-text-editor) don’t need the flag — their AT-SPI2 bridge is enabled by default.
Firefox exposes its tree when launched with MOZ_ACCESSIBILITY_ATK2=1 set in the environment.
To diagnose whether a target app has AT-SPI2 enabled at all:
busctl --user tree org.a11y.atspi.Registry | grep -i "<app-name>"If the app’s subtree is missing, the problem is the app’s accessibility configuration, not xa11y.