A Playwright-style library for driving native desktop apps on macOS, Windows, and Linux. Built for end-to-end tests, computer-use agents, and assistive tools.
Use cases: end-to-end desktop testing, computer-use agents, MCP tools, assistive technology.
Documentation | Rust API | Python API
use xa11y::*;
use std::time::Duration;
fn main() -> Result<()> {
// `by_name` polls for up to the given timeout. Useful when the app
// may not yet be registered with the a11y API. Pass `Duration::ZERO`
// for a single attempt with no waiting.
let safari = App::by_name("Safari", Duration::from_secs(5))?;
// Find elements with CSS-like selectors
let buttons = safari.locator("button[name='Submit']").elements()?;
println!("Found {} buttons", buttons.len());
// Interact with elements via locator (re-resolves every call)
safari.locator("button[name='Submit']").press()?;
Ok(())
}import xa11y
safari = xa11y.App.by_name("Safari")
# Find elements with CSS-like selectors via locator
for button in safari.locator("button").elements():
print(button.name)
# Interact with elements via locator (re-resolves every call)
safari.locator("button[name='Submit']").press()
safari.locator("text_field[name^='Search']").set_value("hello world")[dependencies]
xa11y = "0.4"pip install xa11yRequires Python 3.9+. Pre-built wheels available for Linux, macOS, and Windows.
macOS: Grant your terminal two permissions in System Settings > Privacy & Security:
- Accessibility — required for all accessibility API access.
- Screen & System Audio Recording (macOS 26+) — required to read window content. Without this, only menu bars are visible.
Restart your terminal after changing permissions.
Linux: AT-SPI2 must be running (default on GNOME/most DEs). No special permissions needed.
Windows: No special permissions needed.
Query accessibility trees with CSS-like selectors:
| Pattern | Meaning |
|---|---|
button |
Elements with role Button |
button[name='OK'] |
Button named exactly "OK" |
textfield[name^='Search'] |
Text field whose name starts with "Search" |
textfield[name*='email'] |
Text field whose name contains "email" |
group > button |
Buttons that are direct children of a group |
window button |
Buttons anywhere inside a window |
button:nth(2) |
The 2nd button match |
| Action | Description |
|---|---|
press |
Click / activate |
focus / blur |
Move or remove keyboard focus |
toggle |
Toggle a checkbox or switch |
expand / collapse |
Expand or collapse a disclosure |
select |
Select an item |
set_value |
Set a text field's value |
type_text |
Type text into an element |
increment / decrement |
Adjust a slider or stepper |
show_menu |
Open a context menu |
| Platform | Backend |
|---|---|
| macOS | AXUIElement |
| Linux | AT-SPI2 (D-Bus) |
| Windows | UI Automation |
git clone https://github.com/xa11y/xa11y && cd xa11y
cargo build --workspace
cargo xtask check # fmt, lint, test, python bindingsSee the development docs for architecture and setup.
MIT. All dependencies are permissively licensed (MIT, Apache-2.0, BSD, or similar), enforced via cargo-deny.