npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

a11y_parse

v1.0.9

Published

A TypeScript library for parsing and querying accessibility trees. Provides a CSS-inspired selector language for navigating AX (Accessibility) tree structures — useful for browser automation, testing, and accessibility analysis.

Readme

a11y_parse

A TypeScript library for parsing and querying accessibility trees. Provides a CSS-inspired selector language for navigating AX (Accessibility) tree structures — useful for browser automation, testing, and accessibility analysis.

  • originally designed for fastbrowser_cli, a SKILL.md which helps AI agents browse the web efficiently by exposing a simplified accessibility tree interface and powerful querying capabilities (aka the CSS-like selector syntax).

What is an Accessibility Tree?

An accessibility tree is a hierarchical representation of a UI that assistive technologies (screen readers, automation tools) use to interact with an application. Every visible element — buttons, links, headings, form fields — appears as a node with a role, a name, and optional attributes.

References:


Install

npm install a11y_parse

How to Use as a CLI Tool

Good trick to get the accessibility tree of any webpage: use the fastbrowser_cli SKILL.md to take a snapshot of the current page's accessibility tree, which outputs in the text format that A11yTree.parse can consume.

npx fastbrowser_cli take_snapshot | tail -n +2 > page.a11y.txt
npx a11y_parse --file page.a11y.txt 'link[href^="https"]'

Build

npm run build       # compile TypeScript to dist/
npm run typecheck   # type-check without emitting

Test

npm test

Example

import { A11yTree, A11yQuery } from 'a11y_parse';

const root = A11yTree.parse(`
uid=1 WebArea "Main Page"
  uid=2 main
    uid=3 heading "Welcome"
    uid=4 link "Click here" href="https://example.com"
    uid=5 button "Submit" disabled="true"
  uid=6 navigation
    uid=7 link "Home" href="/"
    uid=8 link "About" href="/about"
`);

// All links anywhere in the tree
A11yQuery.querySelectorAll(root, 'link');

// Only links that are direct children of navigation
A11yQuery.querySelectorAll(root, 'navigation > link');

// Links with an external href
A11yQuery.querySelectorAll(root, 'link[href^="https"]');

// The submit button by name
A11yQuery.querySelector(root, 'button[name="Submit"]');

// Any disabled element
A11yQuery.querySelectorAll(root, '*[disabled="true"]');

// Both headings and buttons in one query
A11yQuery.querySelectorAll(root, 'heading, button');

// A node by its UID
A11yQuery.querySelector(root, '#7');

Selector Language

The selector syntax is modelled on CSS selectors, adapted for accessibility tree structures.

The spec is documented in full in docs/spec_a11y_selector_language.md.

Role selector

Matches nodes by their accessibility role.

button
link
comboxbox
searchbox
heading
WebArea

Universal selector

Matches any node.

*

UID selector

Matches a node by its exact unique identifier.

#4
#1_3

Attribute selectors

Attribute selectors match values inside node.attributes. The special virtual attribute name maps to node.name.

| Syntax | Semantics | |--------|-----------| | [attr] | attribute is present | | [attr="value"] | exact match | | [attr^="prefix"] | starts with | | [attr$="suffix"] | ends with | | [attr*="sub"] | contains substring |

link[href]
button[disabled="true"]
link[href^="https"]
link[href$=".com"]
link[href*="example"]
heading[name="Welcome"]
link[name="Click \"here\""]

Combinators

| Syntax | Semantics | |--------|-----------| | A B | B is a descendant of A (any depth) | | A > B | B is a direct child of A | | A, B | union — matches A or B |

WebArea link
main > button
heading, button
RootWebArea > link[href^="https"]

API

A11yTree

A11yTree.parse(input: string): AxNode

Parses whitespace-indented accessibility tree text into an AxNode tree. Each line follows this format:

uid=<id> <role> ["<name>"] [key="value" ...]

Indentation level determines parent-child relationships. Quoted names and attribute values support backslash escaping (\").

const root = A11yTree.parse(`
uid=1 WebArea "Main Page"
  uid=2 main
    uid=3 heading "Welcome"
    uid=4 link "Click here" href="https://example.com"
    uid=5 button "Submit" disabled="true"
  uid=6 navigation
    uid=7 link "Home" href="/"
`);

A11yTree.stringify(root: AxNode): string

Serializes an AxNode tree back to the text format. Round-trips cleanly through parse.

const text = A11yTree.stringify(root);

A11yTree.walk(node: AxNode): Generator<AxNode>

Depth-first pre-order traversal over all nodes.

for (const node of A11yTree.walk(root)) {
        console.log(node.role, node.name);
}

A11yTree.findOne(root: AxNode, testFn: AxTreeTestFn): AxNode | undefined

Returns the first node in walk order that satisfies testFn.

A11yTree.findAll(root: AxNode, testFn: AxTreeTestFn): AxNode[]

Returns all nodes that satisfy testFn.

A11yTree.filterByUid(uid: string): AxTreeTestFn

Returns a test function that matches a node by exact UID.

const node = A11yTree.findOne(root, A11yTree.filterByUid('4'));

A11yTree.filterByRole(role: string): AxTreeTestFn

Returns a test function that matches a node by exact role.

const links = A11yTree.findAll(root, A11yTree.filterByRole('link'));

A11yTree.filterByName(name: string | RegExp): AxTreeTestFn

Returns a test function that matches a node by name — exact string or regular expression. Nodes with no name are never matched.

const welcome = A11yTree.findOne(root, A11yTree.filterByName('Welcome'));
const navLinks = A11yTree.findAll(root, A11yTree.filterByName(/^Home|Back$/));

A11yTree.buildAncestorTree(axNodes: AxNode[]): AxNode

Builds a minimal pruned tree that contains the given nodes and all their ancestors, discarding unrelated siblings. Useful for presenting query results with context. Throws if axNodes is empty.

const buttons = A11yTree.findAll(root, A11yTree.filterByRole('button'));
const pruned = A11yTree.buildAncestorTree(buttons);
console.log(A11yTree.stringify(pruned));

A11yQuery

A11yQuery.querySelector(root: AxNode, selector: string): AxNode | undefined

Returns the first node matching the selector.

const submit = A11yQuery.querySelector(root, 'button[name="Submit"]');

A11yQuery.querySelectorAll(root: AxNode, selector: string): AxNode[]

Returns all nodes matching the selector.

const externalLinks = A11yQuery.querySelectorAll(root, 'link[href^="https"]');
const interactive   = A11yQuery.querySelectorAll(root, 'button, link');
const navLinks      = A11yQuery.querySelectorAll(root, 'navigation > link');

Types

interface AxNode {
        uid: string;
        role: string;
        name?: string;
        attributes: Record<string, string>;
        children: AxNode[];
        parent?: AxNode;
}

type AxTreeTestFn = (node: AxNode) => boolean;