Accessibility Tree: Definition, Example and How to Read It

The accessibility tree is the browser's semantic model of a page: roles, names and states that screen readers and AI agents read. With tested snapshot code.

By Simon Chiu · Updated

The accessibility tree is a simplified copy of the DOM that the browser computes for assistive technology: each node has a role (button, link, heading), an accessible name and states, and nodes with no meaning to a user are left out.

Screen readers read it instead of the HTML. Test tools and AI agents read it too, because it describes a page the way a person would. This entry is part of the screenshot glossary.

What it means in plain terms

The DOM describes how a page is built. The accessibility tree describes what a person can find and do on it. A <div> used only for layout disappears. A <button> with an × icon and aria-label="Close dialog" becomes button "Close dialog". Text hidden with display: none is gone, because no one can perceive it.

The same markup, two views

We loaded this small page in Chrome and printed the tree with the Puppeteer script below:

<nav><a href="/pricing">Pricing</a></nav>
<main>
  <h1>Checkout</h1>
  <div class="btn" onclick="pay()">Pay now</div>
  <button aria-label="Close dialog">×</button>
  <img src="..." alt="">
  <label>Email <input type="email" value="[email protected]"></label>
  <span style="display:none">Hidden promo</span>
</main>
HTML element In the accessibility tree Why
<a href="/pricing"> link "Pricing" Native role; name from the text
<h1> heading "Checkout" Native role
<div onclick>Pay now</div> StaticText "Pay now", not a button A click handler does not create a role
<button aria-label="Close dialog">×</button> button "Close dialog" aria-label replaces the visible × as the name
<img alt=""> absent Empty alt marks it as decorative
<input> inside <label> textbox "Email " Name computed from the label
<span style="display:none"> absent Hidden from everyone

The "Pay now" row is the useful finding. On a screenshot it looks like a button. In the tree it is plain text, so a screen reader user cannot find it as a control, and an agent that clicks by role cannot either. The two views disagree, and that disagreement is a real accessibility bug.

How it works technically

The browser derives the tree from the DOM, the computed CSS and ARIA attributes. For each element it computes:

  • Role: from the tag (<button>, <nav>) or an explicit role attribute.
  • Accessible name: from aria-labelledby, aria-label, the label, the content or alt, in a defined order of precedence.
  • States and properties: checked, expanded, disabled, the heading level, the current value.

The operating system's accessibility API exposes the result to screen readers. Automation tools reach the same data through the Chrome DevTools Protocol. Content inside a shadow DOM is included, which is one reason the tree is easier to work with than querying the DOM.

How to read it

Puppeteer

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto(process.argv[2] ?? 'https://example.com');

  // interestingOnly (the default) drops generic wrappers and keeps what assistive tech reads.
  const tree = await page.accessibility.snapshot({ interestingOnly: true });

  const print = (node, depth = 0) => {
    console.log(`${'  '.repeat(depth)}${node.role} "${node.name ?? ''}"`);
    for (const child of node.children ?? []) print(child, depth + 1);
  };
  print(tree);
} finally {
  await browser.close();
}

Playwright

Playwright's current API is ariaSnapshot(), which returns YAML and is also what its toMatchAriaSnapshot assertion compares:

import { chromium } from 'playwright';

const browser = await chromium.launch();
try {
  const page = await browser.newPage();
  await page.goto(process.argv[2] ?? 'https://example.com');
  // ariaSnapshot returns a YAML view of the accessibility tree under the locator.
  console.log(await page.locator('body').ariaSnapshot());
} finally {
  await browser.close();
}

For the test page it printed, among other lines:

- main:
  - heading "Checkout" [level=1]
  - text: Pay now
  - button "Close dialog": ×
  - textbox "Email": [email protected]

Tested on 2026-10-15 with Node 22.18.0, [email protected] (Chrome 148, run with headless: 'shell') and [email protected] (Chromium 156).

RenderScreenshot

RenderScreenshot returns rendered images and PDFs. It does not return an accessibility tree. The two are complementary: the tree tells you what is on the page, and a capture tells you how it looks. A common agent pattern reads the tree with Playwright, then requests a capture of only the region in question to check layout, using capture.selector:

{
  "url": "https://shop.example.com/checkout",
  "capture": { "selector": "main" },
  "output": { "format": "png" }
}

How the two compare in size and usefulness for language models is measured in screenshot vs DOM vs accessibility tree. This request was not run for this page.

Common mistakes

  • Treating display: none and visibility: hidden content as readable. It is not in the tree.
  • Clickable <div> and <span> elements without a role. They look fine on screen and vanish as controls.
  • Icon buttons without names. They appear as button "".
  • Snapshots of a page that is not ready. The tree reflects the current DOM. Use the same wait strategy you would use for a screenshot.
  • Assuming the tree covers visual problems. Contrast, overlap and clipped text are invisible to it.

Building an agent that needs to see pages too? Read about screenshots for AI agents or get a free API key.

Frequently asked questions

Is the accessibility tree the same as the DOM?
No. The browser builds it from the DOM plus CSS and ARIA. It drops nodes that carry no meaning, such as layout wrappers and hidden elements, and adds computed facts, such as the accessible name of a button that has only an icon.
Why do AI browsing agents use the accessibility tree?
It lists what a user can perceive and operate, with roles and names, in far fewer tokens than raw HTML. Its weakness is that it says nothing about layout, colour or images, which is where a screenshot helps.

Related in this topic

Try RenderScreenshot free

150 free credits every month, no credit card required.