Quick Start
Installation
Add xa11y to your Cargo.toml:
[dependencies]xa11y = "0.1"Install from PyPI:
pip install xa11yRequires Python 3.9+. Pre-built wheels are available for Linux (x86_64, aarch64), macOS (x86_64, aarch64), and Windows (x86_64).
Platform Setup
Grant accessibility permissions to your terminal app (or IDE) in System Settings → Privacy & Security → Accessibility.
xa11y can check this for you:
use xa11y::*;
let provider = create_provider()?;match provider.check_permissions()? { PermissionStatus::Granted => println!("Ready!"), PermissionStatus::Denied { instructions } => { eprintln!("{}", instructions); }}import xa11y
status = xa11y.check_permissions()print(status)Ensure AT-SPI2 is running (it is by default on GNOME and most desktop environments). No special permissions are needed.
No special permissions needed — UI Automation is available to all processes.
Reading an Accessibility Tree
use xa11y::*;
fn main() -> Result<()> { let provider = create_provider()?;
// Get the accessibility tree for an app by name let tree = provider.get_app_tree( &AppTarget::ByName("Safari".to_string()), &QueryOptions::default(), )?;
println!("Root: {:?}", tree.root().role); println!("Children: {}", tree.children(tree.root()).len());
Ok(())}import xa11y
# One-liner: get the tree for an app by nametree = xa11y.app("Safari")
print(f"Root role: {tree.root.role}")print(f"Children: {len(tree.root.children)}")Querying Elements
xa11y supports CSS-like selectors to find elements in the tree.
use xa11y::*;
let provider = create_provider()?;let tree = provider.get_app_tree( &AppTarget::ByName("Safari".to_string()), &QueryOptions::default(),)?;
// Find all buttonslet buttons = tree.query("button")?;
// Find a button by namelet submit = tree.query("button[name='Submit']")?;let first = &submit[0];println!("Found: {}", first.name.as_deref().unwrap_or("unnamed"));
// Find text fieldslet fields = tree.query("textfield")?;import xa11y
tree = xa11y.app("Safari")
# Find all buttonsfor button in tree.query("button"): print(button.name)
# Find a specific buttonsubmit = tree.query("button[name='Submit']")[0]print(f"Found: {submit.name}")
# Find text fieldsfields = tree.query("textfield")Performing Actions
use xa11y::*;
let provider = create_provider()?;let target = AppTarget::ByName("Safari".to_string());
// Use a Locator for resilient element interactionlet submit = Locator::new(&*provider, target.clone(), "button[name='Submit']");
// Press the buttonsubmit.press()?;
// Type into a text fieldlet search = Locator::new(&*provider, target, "textfield[name^='Search']");search.set_value("hello world")?;import xa11y
tree = xa11y.app("Safari")
# Press a button (shorthand)tree.press("button[name='Submit']")
# Use a Locator for multiple interactionssearch = tree.locator("textfield[name^='Search']")search.set_value("hello world")Listing Applications
use xa11y::*;
let provider = create_provider()?;let apps = provider.list_apps()?;
for app in &apps { println!("{} (pid: {})", app.name, app.pid);}import xa11y
apps = xa11y.list_apps()for app in apps: print(f"{app.name} (pid: {app.pid})")Selector Syntax Reference
| 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[name='OK'] | Button named “OK” anywhere inside a window |
button:nth(2) | The 2nd button match |
Next Steps
- Overview — architecture and design principles
- Rust API Reference — full Rust API documentation
- Python API Reference — full Python API documentation