THE PRACTICAL GUIDE · BROWSER SEMANTICS

Accessibility Tree for AI Agents: Inspect and Test It

A button can look obvious and still expose the wrong meaning. Inspect what the browser reports, then test what changes when someone uses it.

This guide expands the controls check in our AI-agent-ready website checklist. It is for developers debugging a specific failure: an agent cannot identify a button, selects the wrong plan, or keeps trying an action that the interface has already completed.

1. What does the accessibility tree tell an AI agent?

The accessibility tree is the browser’s representation of accessible content and controls. It includes information such as an element’s role, accessible name, and state. A visual rectangle might be a button, a decoration, or a disabled control; the semantic representation can make that distinction explicit.

Agents can work from screenshots, HTML, an accessibility representation, or a combination. The web.dev guidance on agent-friendly websites describes these different inputs and recommends clear signals across them. Do not assume that an agent receives the exact tree you see in your browser. Its tooling may transform or filter the page, and its browser and session can differ from yours.

For a pricing journey, write down three questions before opening a tool: which control reveals the Team plan, what identifies that control, and how will the page show that the details are open? These questions turn a vague “agent readiness” audit into inspectable expectations.

A small contract for one control
PropertyExpected meaningFailure worth investigating
RoleA button changes whether details are shown.The clickable text has no button semantics.
NameTeam plan details identifies the action’s subject.Several controls are named only More.
StateExpanded changes when details open or close.The visual panel opens while the state stays collapsed.
ResultThe intended plan information becomes available.A state attribute changes but the content remains hidden.

This contract is an editorial testing recommendation. It is not an agent protocol, a ranking factor measurement, or a claim that all repeated button names are invalid. What matters is whether the target can be distinguished within the context the consumer actually receives.

2. Inspect the actual task in Chrome DevTools

Use a representative URL and session. Record the browser version, viewport, language, signed-in state, and any consent choice. A desktop screenshot from a returning visitor can miss the mobile menu or overlay that a fresh session encounters.

  1. Open the page and locate the first control needed for the task.
  2. Open DevTools, select Elements, and select that control’s DOM node.
  3. Open the Accessibility tab, which may be inside the additional-tabs menu. Inspect its computed role and name, alongside relevant ARIA attributes.
  4. Use Show accessibility tree to view the full-page tree and inspect the control’s context.
  5. Activate the control, then inspect the resulting content and state. Repeat after closing, changing selection, or triggering a relevant error.

These controls are documented in the Chrome DevTools accessibility reference. The computed value is the useful evidence: finding an aria-label attribute in the source is not enough if another naming relationship produces an unexpected result.

Keep a compact observation, such as “mobile, signed out, menu open: two buttons named Account; expected one account destination in the active navigation.” Include a screenshot and the relevant node details. That gives the implementer a reproducible state rather than an unbounded request to add more ARIA.

Also navigate with the keyboard. Tree inspection and keyboard operation answer different questions. A control can appear in an accessibility representation while remaining unreachable or unusable through the intended input method. Tab order alone is not a complete inventory either: composite widgets can use arrow keys internally.

3. Repair the meaning before adding test selectors

Give actions and destinations the right elements

Use a native button for a local action and a link with an href for navigation. A clickable div with a class called button does not acquire the native element’s behavior. Adding a role to a custom element still leaves you responsible for keyboard interaction and state management.

For show/hide behavior, W3C’s disclosure pattern specifies a button whose expanded state follows content visibility, with Enter and Space activation. The example below uses this small pattern so the semantics and interaction can be reviewed together.

Keep the visible label and accessible name aligned

If the button says “Team plan details,” avoid replacing its accessible name with an unrelated phrase such as “Purchase business subscription.” That introduces disagreement between visual and semantic inputs. W3C’s Label in Name guidance explains why the accessible name should contain the visible text label. For an icon-only control, supply an accurate name that states its purpose.

When three plan cards each have a “Choose” button, decide whether the plan context is sufficiently clear. One option is visible text such as “Choose Team.” Another is a properly named group with a clearly associated control. Do not resolve ambiguity only by selecting the second matching DOM node in a test; that can conceal the interface problem.

Remove accidental contradictions around hidden content

An off-screen copy of a mobile menu can leave duplicate destinations exposed if it is merely moved with CSS. Inspect which copy is active and how the inactive copy is hidden. Conversely, a visible control can disappear from accessibility APIs because an ancestor has aria-hidden="true".

According to MDN’s aria-hidden reference, that attribute removes an element and its descendants from the accessibility tree but does not visually hide them. It should not be applied to focusable elements or their ancestors. Audit both visibility and focus behavior when repairing an overlay; changing a single attribute is not a complete modal implementation.

4. Turn the expectation into a Playwright test

Here is a self-contained example for an existing project with @playwright/test installed and a Chromium test project configured. Save it as tests/disclosure.spec.ts and run npx playwright test tests/disclosure.spec.ts. It builds fictional plan content in memory; it does not open the example destination or submit anything.

import { test, expect } from '@playwright/test';

test('plan details expose the current state', async ({ page }) => {
  await page.setContent(`
    <main>
      <h1>Compare plans</h1>
      <button type="button" id="details-toggle"
              aria-expanded="false" aria-controls="team-details">
        Team plan details
      </button>
      <div id="team-details" hidden>
        <p>Includes three seats.</p>
        <a href="https://example.com/team/">View Team plan</a>
      </div>
    </main>
    <script>
      const toggle = document.querySelector('#details-toggle');
      const details = document.querySelector('#team-details');
      toggle.addEventListener('click', () => {
        const opening = toggle.getAttribute('aria-expanded') !== 'true';
        toggle.setAttribute('aria-expanded', String(opening));
        details.hidden = !opening;
      });
    </script>
  `);

  const toggle = page.getByRole('button', {
    name: 'Team plan details', exact: true
  });
  const destination = page.getByRole('link', {
    name: 'View Team plan', exact: true
  });
  await expect(toggle).toHaveCount(1);
  await expect(toggle).toHaveAttribute('aria-expanded', 'false');
  await expect(destination).toHaveCount(0);

  await page.keyboard.press('Tab');
  await expect(toggle).toBeFocused();
  await page.keyboard.press('Enter');
  await expect(toggle).toHaveAttribute('aria-expanded', 'true');
  await expect(destination).toBeVisible();
  await expect(page.getByRole('main')).toMatchAriaSnapshot(`
    - heading "Compare plans" [level=1]
    - button "Team plan details" [expanded]
    - paragraph: Includes three seats.
    - link "View Team plan"
  `);
  await expect(destination).toHaveAttribute(
    'href', 'https://example.com/team/'
  );

  await page.keyboard.press('Space');
  await expect(toggle).toHaveAttribute('aria-expanded', 'false');
  await expect(destination).toHaveCount(0);
});

The test checks initial exclusion of the hidden link, keyboard access to the button, opening, exposed content, the expected destination, and closing. On a real application, replace page.setContent(...) with navigation to your test page and use its real task expectations. Keep the staging data stable enough that a failure has a useful interpretation.

Playwright’s role locators target controls using roles and accessible names; they exclude hidden elements by default. Exact matching and the count assertion make a duplicate or renamed target visible as a test failure. Scoping to the appropriate named region is useful when the same action legitimately appears in several places.

The ARIA snapshot documentation explains the YAML representation and matching rules. This small inline snapshot checks the listed structure and order; it is not an exhaustive assertion that no extra nodes exist. Keep explicit count, state, and destination assertions for requirements where an extra control or wrong value matters.

Do not regenerate a baseline just because a test fails. Review whether the product change intentionally changed the contract. A new label can be correct; a lost label is a regression even if the screenshot still looks right. A Playwright pass records behavior under the tested browser setup. It is neither a screen-reader certification nor an observed AI-agent success.

5. Test states that change the next available action

A homepage snapshot is only one moment. Select states that change what the visitor can understand or do. Start with the transitions on your most important journey and expand when a component or session condition introduces a different branch.

A focused state matrix for a commercial website
StateSemantic checkInteraction check
Mobile navigation closed / openToggle name stays meaningful; expanded state follows visibility.Active destinations are reachable; inactive copies do not interfere.
Plan changedSelected plan and relevant terms agree with the visible choice.The next action applies to that plan.
Dialog opened / closedThe dialog has an appropriate name and current content.Focus enters it, stays within a modal, and returns appropriately on close.
Request pending / completedProgress and outcome have understandable text.The user can tell whether to wait, retry, or continue.
Fresh / returning sessionConsent and account surfaces expose the actions actually available.The task resumes after the authorized choice.

For modal details, follow the W3C modal-dialog pattern, including naming and focus behavior. A dialog role alone does not establish a working modal. Test closing and returning to the workflow, not just the open screenshot.

For forms, use our demo-form state test matrix to cover invalid inputs, slow responses, and the actual stored result. The tree checks here diagnose how those states are represented; they do not replace validation, permission, or backend checks.

6. A readable tree can still fail at the next step

If the role and name are correct but a click fails, preserve the browser trace and inspect the failure category. Playwright’s actionability checks distinguish conditions such as visibility, stability, event reception, and enabled state. An overlay can intercept interaction even when the intended control has good semantics.

  • Target missing: compare the actual URL, session, frame, and rendered DOM with the expected state. Inspect hidden ancestors and whether content has loaded.
  • Target ambiguous: review repeated names and their groups. Fix confusing UI context or scope the test to a legitimately distinct region.
  • Target found, action blocked: inspect overlays, animation, disabled state, and authentication requirements before changing labels.
  • Action works, agent chooses incorrectly: inspect the agent’s available inputs and instructions, then repeat the same bounded task after the relevant repair.

Avoid forcing clicks to silence a readiness failure. That can remove the condition you were trying to test. Likewise, an HTTP-only crawler never exercising this browser control needs a different investigation; use the crawler-rendering guide when public content is absent from initial HTML.

If your site exposes WebMCP tools, validate those operations separately with the WebMCP testing workflow. A well-named button does not validate tool arguments, and a working tool does not establish that the ordinary browser journey is understandable.

7. Keep a small, useful release record

For each repaired task, save the URL and release, the tested environment, the failing observation, the intended semantic contract, and the result of the repeat check. Add the focused test to the component’s existing suite so the expectation survives a later redesign.

  • Important controls have appropriate roles and understandable names.
  • Names agree with visible labels and identify the intended object in context.
  • Expanded, selected, and other relevant states track the actual interface.
  • Hidden content and overlays behave correctly in both semantic and interaction checks.
  • Desktop and mobile versions of the task have been exercised where their controls differ.
  • Snapshot changes have been reviewed alongside explicit behavioral assertions.
  • Agent execution is reported separately, with the task, environment, and observed outcome.

Broader accessibility work still needs automated checks and manual evaluation. Playwright’s accessibility-testing guidance describes automated scanning and its limits. Treat a focused semantic test as one maintained part of that work, not a substitute for evaluating the experience of people using assistive technology.

If no agent was run, write “semantic and browser interaction checks passed; agent task untested.” If an agent was run, record what it actually did and which result you verified. This keeps the evidence useful without turning one successful local test into a universal readiness claim.

Sources & methodology

Prepared by Adapt My Page with AI-assisted research and drafting. Primary documentation from web.dev, Chrome DevTools, W3C, MDN, and Playwright was checked on 14 September 2026. Sources are linked beside the claims they support.

The task contract, diagnostic workflow, and state matrix are editorial recommendations. The fictional disclosure example is a reproducible browser test, not an AI-agent benchmark or a customer result. No ranking, citation, or conversion effect is claimed.