Skip to content

About the two scanning models: static source vs live DOM

b8e can look for accessibility defects in two ways, and the command you run decides which you get. b8e scan . reads your source files and never opens a browser. b8e scan --url, --crawl, and --story drive a real browser and inspect the rendered page. They find different things, so it is worth knowing which question you are asking.

A source scan asks what does the code say? It parses your components across eight stacks — React/TSX, Astro, Shopify Liquid, iOS SwiftUI and UIKit, Interface Builder scenes (.storyboard and .xib), Jetpack Compose, Android XML layouts, and Unity scenes — and checks the accessibility contract each element declares. An <img> with no alt, an Image with no accessibilityLabel, a control with no accessible name, a heading level that skips: all visible without running anything. A run prints one coverage line per stack it found, and a stack it recognises but cannot analyze says so rather than passing quietly.

A live-DOM scan asks what does the page become? It launches a browser, navigates, waits for the framework to hydrate and the JavaScript to run, then reads the accessibility tree a screen reader would actually get. That tree is the product of routing, data fetching, client-side rendering, portals, and conditional branches.

Neither covers the other. A defect can be plainly wrong in the source and gone by the time the page renders, because a wrapper injected the missing value. Another is invisible in the source and only appears once the page runs — a modal that traps focus, a live region that never announces.

Source scan when you want speed and repeatability. The same source produces the same findings every time, with no network and no browser, which is what makes it usable on every commit and trustworthy as a pull-request gate. Its limit is that it can only reason about code it can resolve; when it cannot, it tells you rather than guessing — see About components the scanner can and cannot see.

Live-DOM scan when the question is about runtime: focus order, dynamic ARIA state, live-region announcements, contrast against actually-painted pixels, what a route does after client navigation. Three flags run the same live-DOM scan, differing in what gets rendered:

  • --url renders specific pages and aggregates them into one report.
  • --crawl expands a seed URL into a same-origin page set first, for breadth without listing every route.
  • --story renders Storybook stories — often the most useful during development, since components appear in the states their authors care about.

The cost is the mirror image: richer, closer to real experience, but it needs a browser, a network, and a page that renders predictably. For flags and defaults, see the b8e scan reference.

b8e audit is a third thing, and worth not confusing with either scan. It runs journeys through a real browser and a real screen reader, and answers whether a person can complete a task — not whether the markup is well formed. A scan reports defects on a surface; an audit reports whether a journey succeeds. See the b8e audit reference.

Three separate questions, then: does the code declare the right thing, does the rendered page expose the right thing, and can someone actually get through the flow. Each has its own command.