accesskit

A cross-platform accessibility tree schema and action trait for UI toolkits, written in Rust.

Library
Cargo
v0.25.0
1,513stars
MIT OR Apache-2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
90/100Excellent
Development Activity100
Maintenance100
Community64
Maturity56
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
80/100Excellent
Architecture90
Code Quality88
Innovation82
Learning Curve60

AccessKit is the core crate of the AccessKit project: a data schema that describes everything a screen reader or other assistive technology needs to understand a UI, plus the trait toolkits implement to handle incoming action requests such as focus, click, or text selection. Rather than each UI toolkit reverse-engineering Windows UI Automation, macOS NSAccessibility, AT-SPI, and Android’s accessibility API separately, toolkit authors build one AccessKit tree and hand it to a platform adapter crate that translates it into the native API.

The schema is modeled closely on Chromium’s internal cross-platform accessibility abstraction: nodes carry a Role, a bag of optional properties, and a set of supported Actions, and updates are pushed incrementally via TreeUpdate rather than pulled on demand. This crate is no_std-compatible by default (falling back to alloc), keeps zero required third-party runtime dependencies beyond uuid, and gates serde/schemars/PyO3 support behind Cargo features so consumers only pay for what they use.

What You Get

  • A Role enum covering the ARIA role vocabulary (button, text input, tree item, alert, and dozens more), ordered for compact serialization
  • A Node type with property getter/setter/clear macros for labels, bounds, states, relationships, and text runs
  • An Action enum plus ActionRequest/ActionHandler trait for receiving focus, click, scroll, and text-editing requests from assistive technologies
  • TreeUpdate, the incremental-update structure adapters consume to build and mutate the live accessibility tree, including sub-tree/graft-node support for multi-process or multi-actor UIs
  • A geometry module (Affine, Point, Rect, Size, Vec2) adapted from kurbo for expressing node bounds and transforms
  • Optional serde and schemars features for serializing the tree and generating a JSON Schema, and an optional pyo3 feature for the official Python bindings

Common Use Cases

  • Immediate-mode or custom-rendered GUI toolkits (e.g. egui-style libraries) exposing their widgets to screen readers without hand-rolling each platform’s accessibility API
  • Game engines and canvas-based applications adding assistive-technology support to a UI that doesn’t use native platform widgets
  • Cross-platform Rust application frameworks wiring up accesskit_winit (or another adapter) to get accessibility across desktop targets from one tree definition
  • Non-Rust toolkits consuming the C or Python bindings to get the same schema and adapters without writing Rust directly

Under The Hood

Architecture accesskit’s design mirrors Chromium’s browser/renderer split: the crate defines a TreeUpdate push model where the producer (a UI toolkit) incrementally submits (NodeId, Node) pairs plus tree/focus metadata, and a downstream accesskit_consumer::Tree (in the sibling crate) retains the full tree and diffs updates against it. Node itself is built from macro-generated property accessors (impl_node_id_property_methods!-style patterns around line 1300+ of lib.rs) backed by a Properties/PropertyIndices struct that trades a flat, indexed representation for compact storage rather than one field per possible attribute. Action, Role, and the various attribute enums (Invalid, Live, AriaCurrent, etc.) are plain #[repr(u8)] enums so they serialize cheaply and map directly onto platform API constants in the adapter crates. The sub-tree/graft-node mechanism (documented in ARCHITECTURE.md) lets independent actors — for example separate processes or components — each own a NodeId namespace that gets stitched together by the adapter, without requiring coordination between producers.

Tech Stack The crate is Rust, no_std by default (#![cfg_attr(not(any(feature = "pyo3", feature = "schemars")), no_std)]) with alloc for Box/String/Vec, so it can run in constrained embedded-style environments as well as full applications. Its only unconditional dependency is uuid (for TreeId); serde/serde_json and schemars are opt-in for serialization and JSON Schema generation, enumn backs numeric round-tripping for enums under the serde feature, and pyo3 is opt-in for the official Python bindings. It lives inside a Cargo workspace alongside accesskit_consumer and platform adapters for Android, iOS, macOS, Unix (AT-SPI via zbus), Windows (UI Automation), and winit, all pinned to a shared rust-version = "1.85" and 2024 edition. CI runs cargo fmt, cargo deny across eight target triples, and clippy/tests per platform, so the workspace enforces cross-target buildability on every change.

Code Quality Testing is extensive and inline: lib.rs alone (3,700 lines) carries dozens of #[cfg(test)] mod tests blocks generated by the same macros that produce the property accessors, so every generated getter/setter/clearer pattern gets a matching round-trip test rather than relying on hand-written coverage. geometry.rs (866 lines, ported from kurbo) follows the same pattern for its Affine/Point/Rect/Vec2 math. There is no unsafe code in either file. The project enforces cargo fmt and clippy in CI across every supported OS/adapter combination, plus cargo-deny license/advisory checks across eight target triples, giving strong guardrails against regressions in a codebase with a large enum/macro surface area.

API Design The public API leans on Rust’s type system to make invalid accessibility states hard to construct: roles, actions, and node states are closed enums rather than strings, and Node’s many optional properties are exposed through consistent getter/setter/clearer method triples generated from a handful of macros, so the ergonomics stay uniform across dozens of properties instead of drifting field by field. The tradeoff is a steep initial learning curve — consumers need to understand the TreeUpdate push model and sub-tree/graft-node concepts before they can wire up a working tree — but ARCHITECTURE.md and the README-APPLICATION-DEVELOPERS.md document exist specifically to front-load that context, and the crate itself ships no CLI or scaffolding, keeping its surface limited to the schema and action trait it’s meant to own.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search