[{"data":1,"prerenderedAt":2069},["ShallowReactive",2],{"site-header-nav":3,"page-\u002Ftesting-and-automating-accessibility\u002Fautomated-accessibility-testing-with-axe-core\u002F":314,"content-navigation":1917},[4,84,88,216],{"title":5,"path":6,"stem":7,"children":8},"Core Accessibility Principles For Modern Frameworks","\u002Fcore-accessibility-principles-for-modern-frameworks","core-accessibility-principles-for-modern-frameworks",[9,12,18,24,36,48,66,78],{"title":10,"path":6,"stem":11},"Core Accessibility Principles for Modern Frameworks","core-accessibility-principles-for-modern-frameworks\u002Findex",{"title":13,"path":14,"stem":15,"children":16},"Accessible Color Contrast & Theming","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Faccessible-color-contrast-theming","core-accessibility-principles-for-modern-frameworks\u002Faccessible-color-contrast-theming\u002Findex",[17],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":22},"Accessible Form Validation & Error States","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Faccessible-form-validation-error-states","core-accessibility-principles-for-modern-frameworks\u002Faccessible-form-validation-error-states\u002Findex",[23],{"title":19,"path":20,"stem":21},{"title":25,"path":26,"stem":27,"children":28},"Focus Management Strategies for SPAs","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Ffocus-management-strategies-for-spas","core-accessibility-principles-for-modern-frameworks\u002Ffocus-management-strategies-for-spas\u002Findex",[29,30],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":34},"Handling Focus Restoration After Dynamic Route Changes","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Ffocus-management-strategies-for-spas\u002Fhandling-focus-restoration-after-dynamic-route-changes","core-accessibility-principles-for-modern-frameworks\u002Ffocus-management-strategies-for-spas\u002Fhandling-focus-restoration-after-dynamic-route-changes\u002Findex",[35],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":40},"Keyboard Navigation Patterns for Modals","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Fkeyboard-navigation-patterns-for-modals","core-accessibility-principles-for-modern-frameworks\u002Fkeyboard-navigation-patterns-for-modals\u002Findex",[41,42],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":46},"Building Accessible Dropdowns Without External UI Kits","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Fkeyboard-navigation-patterns-for-modals\u002Fbuilding-accessible-dropdowns-without-external-ui-kits","core-accessibility-principles-for-modern-frameworks\u002Fkeyboard-navigation-patterns-for-modals\u002Fbuilding-accessible-dropdowns-without-external-ui-kits\u002Findex",[47],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":52},"Reduced Motion & Animation Accessibility","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Freduced-motion-and-animation-accessibility","core-accessibility-principles-for-modern-frameworks\u002Freduced-motion-and-animation-accessibility\u002Findex",[53,54,60],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":58},"Accessible Loading Skeletons and Spinners","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Freduced-motion-and-animation-accessibility\u002Faccessible-loading-skeletons-and-spinners","core-accessibility-principles-for-modern-frameworks\u002Freduced-motion-and-animation-accessibility\u002Faccessible-loading-skeletons-and-spinners\u002Findex",[59],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":64},"Respecting prefers-reduced-motion in React and CSS","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Freduced-motion-and-animation-accessibility\u002Frespecting-prefers-reduced-motion-in-react-and-css","core-accessibility-principles-for-modern-frameworks\u002Freduced-motion-and-animation-accessibility\u002Frespecting-prefers-reduced-motion-in-react-and-css\u002Findex",[65],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":70},"Screen Reader Compatibility Testing","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Fscreen-reader-compatibility-testing","core-accessibility-principles-for-modern-frameworks\u002Fscreen-reader-compatibility-testing\u002Findex",[71,72],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":76},"Testing ARIA Live Regions with Jest and Testing Library","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Fscreen-reader-compatibility-testing\u002Ftesting-aria-live-regions-with-jest-and-testing-library","core-accessibility-principles-for-modern-frameworks\u002Fscreen-reader-compatibility-testing\u002Ftesting-aria-live-regions-with-jest-and-testing-library\u002Findex",[77],{"title":73,"path":74,"stem":75},{"title":79,"path":80,"stem":81,"children":82},"Semantic HTML vs ARIA in Component Trees","\u002Fcore-accessibility-principles-for-modern-frameworks\u002Fsemantic-html-vs-aria-in-component-trees","core-accessibility-principles-for-modern-frameworks\u002Fsemantic-html-vs-aria-in-component-trees\u002Findex",[83],{"title":79,"path":80,"stem":81},{"title":85,"path":86,"stem":87},"Modern Framework Accessibility","\u002F","index",{"title":89,"path":90,"stem":91,"children":92},"React Nextjs Accessibility Patterns","\u002Freact-nextjs-accessibility-patterns","react-nextjs-accessibility-patterns",[93,96,108,132,156,162,180,204],{"title":94,"path":90,"stem":95},"React & Next.js Accessibility Patterns","react-nextjs-accessibility-patterns\u002Findex",{"title":97,"path":98,"stem":99,"children":100},"Accessible Component Libraries in React","\u002Freact-nextjs-accessibility-patterns\u002Faccessible-component-libraries-in-react","react-nextjs-accessibility-patterns\u002Faccessible-component-libraries-in-react\u002Findex",[101,102],{"title":97,"path":98,"stem":99},{"title":103,"path":104,"stem":105,"children":106},"Building Accessible Tabs in React Without Radix UI","\u002Freact-nextjs-accessibility-patterns\u002Faccessible-component-libraries-in-react\u002Fbuilding-accessible-tabs-in-react-without-radix-ui","react-nextjs-accessibility-patterns\u002Faccessible-component-libraries-in-react\u002Fbuilding-accessible-tabs-in-react-without-radix-ui\u002Findex",[107],{"title":103,"path":104,"stem":105},{"title":109,"path":110,"stem":111,"children":112},"Accessible Data Tables & Grids in React","\u002Freact-nextjs-accessibility-patterns\u002Faccessible-data-tables-and-grids","react-nextjs-accessibility-patterns\u002Faccessible-data-tables-and-grids\u002Findex",[113,114,120,126],{"title":109,"path":110,"stem":111},{"title":115,"path":116,"stem":117,"children":118},"Accessible Pagination for React Data Tables","\u002Freact-nextjs-accessibility-patterns\u002Faccessible-data-tables-and-grids\u002Faccessible-pagination-for-react-data-tables","react-nextjs-accessibility-patterns\u002Faccessible-data-tables-and-grids\u002Faccessible-pagination-for-react-data-tables\u002Findex",[119],{"title":115,"path":116,"stem":117},{"title":121,"path":122,"stem":123,"children":124},"Building a Sortable Accessible Data Table in React","\u002Freact-nextjs-accessibility-patterns\u002Faccessible-data-tables-and-grids\u002Fbuilding-a-sortable-accessible-data-table-in-react","react-nextjs-accessibility-patterns\u002Faccessible-data-tables-and-grids\u002Fbuilding-a-sortable-accessible-data-table-in-react\u002Findex",[125],{"title":121,"path":122,"stem":123},{"title":127,"path":128,"stem":129,"children":130},"Virtualizing Long Lists Accessibly in React","\u002Freact-nextjs-accessibility-patterns\u002Faccessible-data-tables-and-grids\u002Fvirtualizing-long-lists-accessibly-in-react","react-nextjs-accessibility-patterns\u002Faccessible-data-tables-and-grids\u002Fvirtualizing-long-lists-accessibly-in-react\u002Findex",[131],{"title":127,"path":128,"stem":129},{"title":133,"path":134,"stem":135,"children":136},"Dynamic Content & State Announcements","\u002Freact-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements","react-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements\u002Findex",[137,138,144,150],{"title":133,"path":134,"stem":135},{"title":139,"path":140,"stem":141,"children":142},"Accessible Toast Notifications in React","\u002Freact-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements\u002Faccessible-toast-notifications-in-react","react-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements\u002Faccessible-toast-notifications-in-react\u002Findex",[143],{"title":139,"path":140,"stem":141},{"title":145,"path":146,"stem":147,"children":148},"Announcing Client-Side Route Changes in React","\u002Freact-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements\u002Fannouncing-client-side-route-changes-in-react","react-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements\u002Fannouncing-client-side-route-changes-in-react\u002Findex",[149],{"title":145,"path":146,"stem":147},{"title":151,"path":152,"stem":153,"children":154},"React Context for Accessibility Preferences","\u002Freact-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements\u002Freact-context-for-global-accessibility-preferences","react-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements\u002Freact-context-for-global-accessibility-preferences\u002Findex",[155],{"title":151,"path":152,"stem":153},{"title":157,"path":158,"stem":159,"children":160},"Form Handling with React Hook Form & A11y","\u002Freact-nextjs-accessibility-patterns\u002Fform-handling-with-react-hook-form-a11y","react-nextjs-accessibility-patterns\u002Fform-handling-with-react-hook-form-a11y\u002Findex",[161],{"title":157,"path":158,"stem":159},{"title":163,"path":164,"stem":165,"children":166},"Next.js App Router Accessibility","\u002Freact-nextjs-accessibility-patterns\u002Fnextjs-app-router-a11y","react-nextjs-accessibility-patterns\u002Fnextjs-app-router-a11y\u002Findex",[167,168,174],{"title":163,"path":164,"stem":165},{"title":169,"path":170,"stem":171,"children":172},"Skip Links in Next.js App Router","\u002Freact-nextjs-accessibility-patterns\u002Fnextjs-app-router-a11y\u002Fimplementing-skip-links-in-nextjs-app-router","react-nextjs-accessibility-patterns\u002Fnextjs-app-router-a11y\u002Fimplementing-skip-links-in-nextjs-app-router\u002Findex",[173],{"title":169,"path":170,"stem":171},{"title":175,"path":176,"stem":177,"children":178},"Next.js Dynamic Imports & Keyboard Navigation","\u002Freact-nextjs-accessibility-patterns\u002Fnextjs-app-router-a11y\u002Fnextjs-dynamic-imports-and-keyboard-navigation","react-nextjs-accessibility-patterns\u002Fnextjs-app-router-a11y\u002Fnextjs-dynamic-imports-and-keyboard-navigation\u002Findex",[179],{"title":175,"path":176,"stem":177},{"title":181,"path":182,"stem":183,"children":184},"React Hooks for Accessibility","\u002Freact-nextjs-accessibility-patterns\u002Freact-hooks-for-accessibility","react-nextjs-accessibility-patterns\u002Freact-hooks-for-accessibility\u002Findex",[185,186,192,198],{"title":181,"path":182,"stem":183},{"title":187,"path":188,"stem":189,"children":190},"Building a useAnnouncer Hook for Live Regions","\u002Freact-nextjs-accessibility-patterns\u002Freact-hooks-for-accessibility\u002Fbuilding-a-useannouncer-hook-for-live-regions","react-nextjs-accessibility-patterns\u002Freact-hooks-for-accessibility\u002Fbuilding-a-useannouncer-hook-for-live-regions\u002Findex",[191],{"title":187,"path":188,"stem":189},{"title":193,"path":194,"stem":195,"children":196},"Fixing Focus Trap Issues in React Portals","\u002Freact-nextjs-accessibility-patterns\u002Freact-hooks-for-accessibility\u002Ffixing-focus-trap-issues-in-react-portals","react-nextjs-accessibility-patterns\u002Freact-hooks-for-accessibility\u002Ffixing-focus-trap-issues-in-react-portals\u002Findex",[197],{"title":193,"path":194,"stem":195},{"title":199,"path":200,"stem":201,"children":202},"Making React useEffect Accessible for Screen Readers","\u002Freact-nextjs-accessibility-patterns\u002Freact-hooks-for-accessibility\u002Fmaking-react-useeffect-accessible-for-screen-readers","react-nextjs-accessibility-patterns\u002Freact-hooks-for-accessibility\u002Fmaking-react-useeffect-accessible-for-screen-readers\u002Findex",[203],{"title":199,"path":200,"stem":201},{"title":205,"path":206,"stem":207,"children":208},"Server Components & Client-Side Interactivity","\u002Freact-nextjs-accessibility-patterns\u002Fserver-components-client-side-interactivity","react-nextjs-accessibility-patterns\u002Fserver-components-client-side-interactivity\u002Findex",[209,210],{"title":205,"path":206,"stem":207},{"title":211,"path":212,"stem":213,"children":214},"Accessible Modals in Next.js 14 Server Components","\u002Freact-nextjs-accessibility-patterns\u002Fserver-components-client-side-interactivity\u002Fhandling-accessible-modals-in-nextjs-14-server-components","react-nextjs-accessibility-patterns\u002Fserver-components-client-side-interactivity\u002Fhandling-accessible-modals-in-nextjs-14-server-components\u002Findex",[215],{"title":211,"path":212,"stem":213},{"title":217,"path":218,"stem":219,"children":220},"Testing And Automating Accessibility","\u002Ftesting-and-automating-accessibility","testing-and-automating-accessibility",[221,224,242,260,278,296],{"title":222,"path":218,"stem":223},"Testing & Automating Accessibility","testing-and-automating-accessibility\u002Findex",{"title":225,"path":226,"stem":227,"children":228},"Accessibility Audits with Lighthouse","\u002Ftesting-and-automating-accessibility\u002Faccessibility-audits-with-lighthouse","testing-and-automating-accessibility\u002Faccessibility-audits-with-lighthouse\u002Findex",[229,230,236],{"title":225,"path":226,"stem":227},{"title":231,"path":232,"stem":233,"children":234},"Interpreting Lighthouse Accessibility Scores","\u002Ftesting-and-automating-accessibility\u002Faccessibility-audits-with-lighthouse\u002Finterpreting-lighthouse-accessibility-scores","testing-and-automating-accessibility\u002Faccessibility-audits-with-lighthouse\u002Finterpreting-lighthouse-accessibility-scores\u002Findex",[235],{"title":231,"path":232,"stem":233},{"title":237,"path":238,"stem":239,"children":240},"Setting Lighthouse CI Accessibility Budgets","\u002Ftesting-and-automating-accessibility\u002Faccessibility-audits-with-lighthouse\u002Fsetting-lighthouse-ci-accessibility-budgets","testing-and-automating-accessibility\u002Faccessibility-audits-with-lighthouse\u002Fsetting-lighthouse-ci-accessibility-budgets\u002Findex",[241],{"title":237,"path":238,"stem":239},{"title":243,"path":244,"stem":245,"children":246},"Automated Accessibility Testing with axe-core","\u002Ftesting-and-automating-accessibility\u002Fautomated-accessibility-testing-with-axe-core","testing-and-automating-accessibility\u002Fautomated-accessibility-testing-with-axe-core\u002Findex",[247,248,254],{"title":243,"path":244,"stem":245},{"title":249,"path":250,"stem":251,"children":252},"Catching Color Contrast Failures with axe-core","\u002Ftesting-and-automating-accessibility\u002Fautomated-accessibility-testing-with-axe-core\u002Fcatching-color-contrast-failures-with-axe-core","testing-and-automating-accessibility\u002Fautomated-accessibility-testing-with-axe-core\u002Fcatching-color-contrast-failures-with-axe-core\u002Findex",[253],{"title":249,"path":250,"stem":251},{"title":255,"path":256,"stem":257,"children":258},"Writing Custom axe-core Rules","\u002Ftesting-and-automating-accessibility\u002Fautomated-accessibility-testing-with-axe-core\u002Fwriting-custom-axe-core-rules","testing-and-automating-accessibility\u002Fautomated-accessibility-testing-with-axe-core\u002Fwriting-custom-axe-core-rules\u002Findex",[259],{"title":255,"path":256,"stem":257},{"title":261,"path":262,"stem":263,"children":264},"Component Testing with jest-axe","\u002Ftesting-and-automating-accessibility\u002Fcomponent-testing-with-jest-axe","testing-and-automating-accessibility\u002Fcomponent-testing-with-jest-axe\u002Findex",[265,266,272],{"title":261,"path":262,"stem":263},{"title":267,"path":268,"stem":269,"children":270},"Debugging jest-axe Violations in CI","\u002Ftesting-and-automating-accessibility\u002Fcomponent-testing-with-jest-axe\u002Fdebugging-jest-axe-violations-in-ci","testing-and-automating-accessibility\u002Fcomponent-testing-with-jest-axe\u002Fdebugging-jest-axe-violations-in-ci\u002Findex",[271],{"title":267,"path":268,"stem":269},{"title":273,"path":274,"stem":275,"children":276},"Testing React Components with jest-axe","\u002Ftesting-and-automating-accessibility\u002Fcomponent-testing-with-jest-axe\u002Ftesting-react-components-with-jest-axe","testing-and-automating-accessibility\u002Fcomponent-testing-with-jest-axe\u002Ftesting-react-components-with-jest-axe\u002Findex",[277],{"title":273,"path":274,"stem":275},{"title":279,"path":280,"stem":281,"children":282},"End-to-End Accessibility Testing with Playwright","\u002Ftesting-and-automating-accessibility\u002Fend-to-end-accessibility-testing-with-playwright","testing-and-automating-accessibility\u002Fend-to-end-accessibility-testing-with-playwright\u002Findex",[283,284,290],{"title":279,"path":280,"stem":281},{"title":285,"path":286,"stem":287,"children":288},"Asserting Focus Order in Playwright","\u002Ftesting-and-automating-accessibility\u002Fend-to-end-accessibility-testing-with-playwright\u002Fasserting-focus-order-in-playwright","testing-and-automating-accessibility\u002Fend-to-end-accessibility-testing-with-playwright\u002Fasserting-focus-order-in-playwright\u002Findex",[289],{"title":285,"path":286,"stem":287},{"title":291,"path":292,"stem":293,"children":294},"Keyboard Navigation Tests in Playwright","\u002Ftesting-and-automating-accessibility\u002Fend-to-end-accessibility-testing-with-playwright\u002Fkeyboard-navigation-tests-in-playwright","testing-and-automating-accessibility\u002Fend-to-end-accessibility-testing-with-playwright\u002Fkeyboard-navigation-tests-in-playwright\u002Findex",[295],{"title":291,"path":292,"stem":293},{"title":297,"path":298,"stem":299,"children":300},"Gating Accessibility in CI\u002FCD Pipelines","\u002Ftesting-and-automating-accessibility\u002Fgating-accessibility-in-ci-cd-pipelines","testing-and-automating-accessibility\u002Fgating-accessibility-in-ci-cd-pipelines\u002Findex",[301,302,308],{"title":297,"path":298,"stem":299},{"title":303,"path":304,"stem":305,"children":306},"Accessibility Regression Testing in GitHub Actions","\u002Ftesting-and-automating-accessibility\u002Fgating-accessibility-in-ci-cd-pipelines\u002Faccessibility-regression-testing-in-github-actions","testing-and-automating-accessibility\u002Fgating-accessibility-in-ci-cd-pipelines\u002Faccessibility-regression-testing-in-github-actions\u002Findex",[307],{"title":303,"path":304,"stem":305},{"title":309,"path":310,"stem":311,"children":312},"Failing Pull Requests on axe Violations","\u002Ftesting-and-automating-accessibility\u002Fgating-accessibility-in-ci-cd-pipelines\u002Ffailing-pull-requests-on-axe-violations","testing-and-automating-accessibility\u002Fgating-accessibility-in-ci-cd-pipelines\u002Ffailing-pull-requests-on-axe-violations\u002Findex",[313],{"title":309,"path":310,"stem":311},{"id":315,"title":243,"body":316,"date":1910,"description":1911,"extension":1912,"image":1910,"meta":1913,"modifiedAt":1910,"navigation":767,"noindex":1914,"path":244,"publishedAt":1910,"seo":1915,"stem":245,"updatedAt":1910,"__hash__":1916},"content\u002Ftesting-and-automating-accessibility\u002Fautomated-accessibility-testing-with-axe-core\u002Findex.md",{"type":317,"value":318,"toc":1897},"minimark",[319,323,332,337,394,398,432,435,440,451,454,499,502,537,673,679,681,685,694,961,978,981,1143,1153,1155,1159,1162,1165,1280,1314,1319,1321,1325,1332,1403,1406,1507,1518,1530,1532,1536,1543,1577,1618,1626,1631,1633,1637,1644,1704,1715,1717,1721,1775,1777,1781,1796,1802,1817,1841,1847,1853,1855,1859,1893],[320,321,243],"h1",{"id":322},"automated-accessibility-testing-with-axe-core",[324,325,326,327,331],"p",{},"axe-core is the accessibility rules engine that powers most of the tooling frontend teams already use: browser DevTools panels, Lighthouse's a11y category, jest-axe, and the Playwright accessibility integrations. Understanding the engine directly—rather than the wrappers around it—lets you configure the right WCAG tags, interpret \"incomplete\" results correctly, and avoid the trap of treating a green automated run as proof of conformance. This guide sits under ",[328,329,222],"a",{"href":330},"\u002Ftesting-and-automating-accessibility\u002F"," and explains how axe-core works, how to wire it into React and other framework dev loops, and—critically—what it cannot detect on its own.",[333,334,336],"h3",{"id":335},"what-this-guide-covers","What this guide covers",[338,339,340,357,365,368,384,387],"ul",{},[341,342,343,344,348,349,352,353,356],"li",{},"How the rules engine walks the DOM, builds a partial accessibility tree, and classifies results into ",[345,346,347],"strong",{},"violations",", ",[345,350,351],{},"incomplete",", and ",[345,354,355],{},"passes",".",[341,358,359,360,364],{},"Running ",[361,362,363],"code",{},"@axe-core\u002Freact"," in development for live, render-time auditing.",[341,366,367],{},"Using axe DevTools and the headless API in the browser.",[341,369,370,371,348,374,348,377,348,380,383],{},"Configuring rules and WCAG tags (",[361,372,373],{},"wcag2a",[361,375,376],{},"wcag21aa",[361,378,379],{},"wcag22aa",[361,381,382],{},"best-practice",").",[341,385,386],{},"Handling false positives and \"needs review\" results without disabling rules wholesale.",[341,388,389,390,393],{},"The categories of barriers automation ",[345,391,392],{},"structurally cannot"," catch.",[333,395,397],{"id":396},"target-wcag-22-criteria","Target WCAG 2.2 criteria",[338,399,400,406,416,422],{},[341,401,402,405],{},[361,403,404],{},"4.1.2 Name, Role, Value"," — axe checks that interactive elements expose a computed accessible name and a valid role.",[341,407,408,411,412,415],{},[361,409,410],{},"1.4.3 Contrast (Minimum)"," — the ",[361,413,414],{},"color-contrast"," rule computes ratios from resolved styles.",[341,417,418,421],{},[361,419,420],{},"1.3.1 Info and Relationships"," — structural rules verify labels, headings, list and table semantics.",[341,423,424,427,428,431],{},[361,425,426],{},"4.1.1 Parsing"," — duplicate-id and ARIA-id-reference checks (note ",[361,429,430],{},"4.1.1"," is obsolete in WCAG 2.2, but axe still surfaces the underlying DOM defects).",[433,434],"hr",{},[436,437,439],"h2",{"id":438},"what-axe-core-is-and-how-the-rules-engine-works","What axe-core Is and How the Rules Engine Works",[324,441,442,443,446,447,450],{},"axe-core is a JavaScript library that runs inside the page context. When you call ",[361,444,445],{},"axe.run()",", it does not statically parse your JSX or templates—it inspects the ",[345,448,449],{},"live, rendered DOM"," after the framework has committed it. That distinction matters: server-rendered output, hydration mismatches, and conditionally rendered ARIA all show up exactly as the browser sees them.",[324,452,453],{},"The engine executes in four phases:",[455,456,457,471,479,493],"ol",{},[341,458,459,462,463,466,467,470],{},[345,460,461],{},"Tree collection"," — axe walks the DOM from the configured context (default ",[361,464,465],{},"document","), flattening shadow roots and ",[361,468,469],{},"\u003Ciframe>"," boundaries it can reach, and skipping nodes that are not rendered.",[341,472,473,476,477,356],{},[345,474,475],{},"Accessibility tree resolution"," — for each candidate node it computes the accessible name, role, and relevant state using the same name-computation algorithm browsers use for ",[361,478,404],{},[341,480,481,484,485,488,489,492],{},[345,482,483],{},"Rule + check execution"," — each rule targets a CSS selector, gathers matching nodes, then runs one or more ",[345,486,487],{},"checks"," (small ",[361,490,491],{},"evaluate"," functions returning true\u002Ffalse\u002Fundefined) against them.",[341,494,495,498],{},[345,496,497],{},"Result classification"," — checks are aggregated into one of four buckets.",[324,500,501],{},"The four result types are the single most misread part of axe output:",[338,503,504,509,521,531],{},[341,505,506,508],{},[345,507,347],{}," — at least one check failed. This is a real defect with high confidence. Fix these.",[341,510,511,513,514,517,518,520],{},[345,512,351],{}," (\"needs review\") — a check returned ",[361,515,516],{},"undefined",": the engine could not decide automatically and needs a human. Common with ",[361,519,414],{}," over images, gradients, and overlapping elements.",[341,522,523,525,526,530],{},[345,524,355],{}," — checks succeeded. This only means ",[527,528,529],"em",{},"the things axe can test"," are fine, not that the node is accessible.",[341,532,533,536],{},[345,534,535],{},"inapplicable"," — no nodes matched the rule selector, so it was skipped.",[538,539,546,547,546,551,546,555,546,592,546,620,546,640,546,658],"svg",{"role":540,"ariaLabelledBy":541,"viewBox":544,"style":545},"img",[542,543],"axe-flow-t","axe-flow-d","0 0 760 320","width:100%;height:auto;max-width:760px","\n  ",[548,549,550],"title",{"id":542},"How axe-core processes a page into result buckets",[552,553,554],"desc",{"id":543},"A left-to-right pipeline: the rendered DOM is walked, resolved into a partial accessibility tree, rule checks run against matching nodes, and results are classified into violations, incomplete, passes, and inapplicable.",[556,557,561,562,561,571,561,575,561,578,561,584,561,588,546],"g",{"style":558,"fill":559,"stroke":560},"stroke-width:2","none","currentColor","\n    ",[563,564],"rect",{"x":565,"y":566,"width":567,"height":568,"rx":569,"fill":570},"10","120","130","64","8","var(--primary-soft)",[563,572],{"x":573,"y":566,"width":574,"height":568,"rx":569,"fill":570},"180","150",[563,576],{"x":577,"y":566,"width":567,"height":568,"rx":569,"fill":570},"370",[579,580],"line",{"x1":581,"y1":582,"x2":583,"y2":582},"140","152","178",[579,585],{"x1":586,"y1":582,"x2":587,"y2":582},"330","368",[579,589],{"x1":590,"y1":582,"x2":591,"y2":582},"500","538",[556,593,561,596,561,602,561,606,561,610,561,613,561,617,546],{"style":594,"fill":595},"font-size:14px;text-anchor:middle","var(--text)",[597,598,601],"text",{"x":599,"y":600},"75","148","Rendered",[597,603,605],{"x":599,"y":604},"166","DOM walk",[597,607,609],{"x":608,"y":600},"255","Accessibility",[597,611,612],{"x":608,"y":604},"tree resolve",[597,614,616],{"x":615,"y":600},"435","Rule +",[597,618,619],{"x":615,"y":604},"check run",[556,621,561,622,561,629,561,633,561,637,546],{"style":558,"fill":559,"stroke":560},[563,623],{"x":624,"y":625,"width":626,"height":627,"rx":569,"fill":628},"540","20","200","44","var(--primary)",[563,630],{"x":624,"y":631,"width":626,"height":627,"rx":569,"fill":632},"78","var(--surface)",[563,634],{"x":624,"y":635,"width":626,"height":627,"rx":569,"fill":636},"182","var(--primary-strong)",[563,638],{"x":624,"y":639,"width":626,"height":627,"rx":569,"fill":632},"240",[556,641,561,642,561,647,561,651,561,654,546],{"style":594},[597,643,646],{"x":644,"y":645,"fill":632},"640","47","violations (fail)",[597,648,650],{"x":644,"y":649,"fill":595},"105","incomplete (review)",[597,652,355],{"x":644,"y":653,"fill":632},"209",[597,655,535],{"x":644,"y":656,"fill":657},"267","var(--muted)",[556,659,561,660,561,664,561,667,561,670,546],{"style":558,"fill":559,"stroke":560},[661,662],"path",{"d":663},"M500 130 C 520 90, 520 60, 538 42",[661,665],{"d":666},"M500 145 C 520 120, 520 110, 538 100",[661,668],{"d":669},"M500 165 C 520 190, 520 200, 538 204",[661,671],{"d":672},"M500 178 C 520 230, 520 250, 538 262",[324,674,675,678],{},[345,676,677],{},"The mental model:"," a violation is a confident \"no,\" an incomplete is \"I can't tell—you look,\" and a pass is \"nothing I'm able to measure is wrong here.\" Treating incompletes as passes is how broken color contrast and ambiguous labels ship.",[433,680],{},[436,682,684],{"id":683},"running-axe-corereact-in-development","Running @axe-core\u002Freact in Development",[324,686,687,689,690,693],{},[361,688,363],{}," (the successor to ",[361,691,692],{},"react-axe",") runs axe automatically after every React commit, logging violations to the browser console with the offending node and a docs link. It is the tightest feedback loop available—issues appear as you build the component, not in a separate test run.",[695,696,701],"pre",{"className":697,"code":698,"language":699,"meta":700,"style":700},"language-tsx shiki shiki-themes github-light github-dark","\u002F\u002F src\u002Fmain.tsx — initialise axe ONLY in development\nimport React from 'react';\nimport ReactDOM from 'react-dom\u002Fclient';\nimport App from '.\u002FApp';\n\nif (process.env.NODE_ENV !== 'production') {\n  \u002F\u002F Dynamic import keeps axe out of the production bundle entirely.\n  import('@axe-core\u002Freact').then(({ default: axe }) => {\n    \u002F\u002F (React, ReactDOM, debounce-ms). 1000ms debounce avoids\n    \u002F\u002F re-auditing on every keystroke during rapid re-renders.\n    axe(React, ReactDOM, 1000, {\n      \u002F\u002F Scope to the WCAG tags you actually gate on (see below).\n      runOnly: { type: 'tag', values: ['wcag2a', 'wcag21aa', 'wcag22aa'] },\n    });\n  });\n}\n\nReactDOM.createRoot(document.getElementById('root')!).render(\u003CApp \u002F>);\n","tsx","",[361,702,703,711,732,747,762,769,791,797,837,843,849,864,870,898,904,910,916,921],{"__ignoreMap":700},[704,705,707],"span",{"class":579,"line":706},1,[704,708,710],{"class":709},"sJ8bj","\u002F\u002F src\u002Fmain.tsx — initialise axe ONLY in development\n",[704,712,714,718,722,725,729],{"class":579,"line":713},2,[704,715,717],{"class":716},"szBVR","import",[704,719,721],{"class":720},"sVt8B"," React ",[704,723,724],{"class":716},"from",[704,726,728],{"class":727},"sZZnC"," 'react'",[704,730,731],{"class":720},";\n",[704,733,735,737,740,742,745],{"class":579,"line":734},3,[704,736,717],{"class":716},[704,738,739],{"class":720}," ReactDOM ",[704,741,724],{"class":716},[704,743,744],{"class":727}," 'react-dom\u002Fclient'",[704,746,731],{"class":720},[704,748,750,752,755,757,760],{"class":579,"line":749},4,[704,751,717],{"class":716},[704,753,754],{"class":720}," App ",[704,756,724],{"class":716},[704,758,759],{"class":727}," '.\u002FApp'",[704,761,731],{"class":720},[704,763,765],{"class":579,"line":764},5,[704,766,768],{"emptyLinePlaceholder":767},true,"\n",[704,770,772,775,778,782,785,788],{"class":579,"line":771},6,[704,773,774],{"class":716},"if",[704,776,777],{"class":720}," (process.env.",[704,779,781],{"class":780},"sj4cs","NODE_ENV",[704,783,784],{"class":716}," !==",[704,786,787],{"class":727}," 'production'",[704,789,790],{"class":720},") {\n",[704,792,794],{"class":579,"line":793},7,[704,795,796],{"class":709},"  \u002F\u002F Dynamic import keeps axe out of the production bundle entirely.\n",[704,798,800,803,806,809,811,815,818,822,825,828,831,834],{"class":579,"line":799},8,[704,801,802],{"class":716},"  import",[704,804,805],{"class":720},"(",[704,807,808],{"class":727},"'@axe-core\u002Freact'",[704,810,383],{"class":720},[704,812,814],{"class":813},"sScJk","then",[704,816,817],{"class":720},"(({ ",[704,819,821],{"class":820},"s4XuR","default",[704,823,824],{"class":720},": ",[704,826,827],{"class":820},"axe",[704,829,830],{"class":720}," }) ",[704,832,833],{"class":716},"=>",[704,835,836],{"class":720}," {\n",[704,838,840],{"class":579,"line":839},9,[704,841,842],{"class":709},"    \u002F\u002F (React, ReactDOM, debounce-ms). 1000ms debounce avoids\n",[704,844,846],{"class":579,"line":845},10,[704,847,848],{"class":709},"    \u002F\u002F re-auditing on every keystroke during rapid re-renders.\n",[704,850,852,855,858,861],{"class":579,"line":851},11,[704,853,854],{"class":813},"    axe",[704,856,857],{"class":720},"(React, ReactDOM, ",[704,859,860],{"class":780},"1000",[704,862,863],{"class":720},", {\n",[704,865,867],{"class":579,"line":866},12,[704,868,869],{"class":709},"      \u002F\u002F Scope to the WCAG tags you actually gate on (see below).\n",[704,871,873,876,879,882,885,887,890,892,895],{"class":579,"line":872},13,[704,874,875],{"class":720},"      runOnly: { type: ",[704,877,878],{"class":727},"'tag'",[704,880,881],{"class":720},", values: [",[704,883,884],{"class":727},"'wcag2a'",[704,886,348],{"class":720},[704,888,889],{"class":727},"'wcag21aa'",[704,891,348],{"class":720},[704,893,894],{"class":727},"'wcag22aa'",[704,896,897],{"class":720},"] },\n",[704,899,901],{"class":579,"line":900},14,[704,902,903],{"class":720},"    });\n",[704,905,907],{"class":579,"line":906},15,[704,908,909],{"class":720},"  });\n",[704,911,913],{"class":579,"line":912},16,[704,914,915],{"class":720},"}\n",[704,917,919],{"class":579,"line":918},17,[704,920,768],{"emptyLinePlaceholder":767},[704,922,924,927,930,933,936,938,941,944,947,949,952,955,958],{"class":579,"line":923},18,[704,925,926],{"class":720},"ReactDOM.",[704,928,929],{"class":813},"createRoot",[704,931,932],{"class":720},"(document.",[704,934,935],{"class":813},"getElementById",[704,937,805],{"class":720},[704,939,940],{"class":727},"'root'",[704,942,943],{"class":720},")",[704,945,946],{"class":716},"!",[704,948,383],{"class":720},[704,950,951],{"class":813},"render",[704,953,954],{"class":720},"(\u003C",[704,956,957],{"class":780},"App",[704,959,960],{"class":720}," \u002F>);\n",[324,962,963,964,348,967,969,970,973,974,356],{},"Because it audits the ",[345,965,966],{},"committed DOM",[361,968,363],{}," catches defects that only exist after state changes—an open menu missing ",[361,971,972],{},"aria-expanded",", a dynamically inserted error that lacks an associated label. For those dynamic-state announcements specifically, pair it with the patterns in ",[328,975,977],{"href":976},"\u002Freact-nextjs-accessibility-patterns\u002Fdynamic-content-state-announcements\u002F","dynamic content & state announcements",[324,979,980],{},"For Vue, Svelte, or Angular, use the framework-agnostic API directly in a dev-only effect or route hook:",[695,982,986],{"className":983,"code":984,"language":985,"meta":700,"style":700},"language-ts shiki shiki-themes github-light github-dark","\u002F\u002F dev-audit.ts — works in any framework's client runtime\nimport axe from 'axe-core';\n\nexport async function auditNow(context: Element | Document = document) {\n  const results = await axe.run(context, {\n    runOnly: { type: 'tag', values: ['wcag22aa'] },\n  });\n  \u002F\u002F Group so incompletes are visible, not buried under passes.\n  if (results.violations.length) console.error('a11y violations', results.violations);\n  if (results.incomplete.length) console.warn('a11y needs review', results.incomplete);\n}\n","ts",[361,987,988,993,1007,1011,1048,1070,1083,1087,1092,1117,1139],{"__ignoreMap":700},[704,989,990],{"class":579,"line":706},[704,991,992],{"class":709},"\u002F\u002F dev-audit.ts — works in any framework's client runtime\n",[704,994,995,997,1000,1002,1005],{"class":579,"line":713},[704,996,717],{"class":716},[704,998,999],{"class":720}," axe ",[704,1001,724],{"class":716},[704,1003,1004],{"class":727}," 'axe-core'",[704,1006,731],{"class":720},[704,1008,1009],{"class":579,"line":734},[704,1010,768],{"emptyLinePlaceholder":767},[704,1012,1013,1016,1019,1022,1025,1027,1030,1033,1036,1039,1042,1045],{"class":579,"line":749},[704,1014,1015],{"class":716},"export",[704,1017,1018],{"class":716}," async",[704,1020,1021],{"class":716}," function",[704,1023,1024],{"class":813}," auditNow",[704,1026,805],{"class":720},[704,1028,1029],{"class":820},"context",[704,1031,1032],{"class":716},":",[704,1034,1035],{"class":813}," Element",[704,1037,1038],{"class":716}," |",[704,1040,1041],{"class":813}," Document",[704,1043,1044],{"class":716}," =",[704,1046,1047],{"class":720}," document) {\n",[704,1049,1050,1053,1056,1058,1061,1064,1067],{"class":579,"line":764},[704,1051,1052],{"class":716},"  const",[704,1054,1055],{"class":780}," results",[704,1057,1044],{"class":716},[704,1059,1060],{"class":716}," await",[704,1062,1063],{"class":720}," axe.",[704,1065,1066],{"class":813},"run",[704,1068,1069],{"class":720},"(context, {\n",[704,1071,1072,1075,1077,1079,1081],{"class":579,"line":771},[704,1073,1074],{"class":720},"    runOnly: { type: ",[704,1076,878],{"class":727},[704,1078,881],{"class":720},[704,1080,894],{"class":727},[704,1082,897],{"class":720},[704,1084,1085],{"class":579,"line":793},[704,1086,909],{"class":720},[704,1088,1089],{"class":579,"line":799},[704,1090,1091],{"class":709},"  \u002F\u002F Group so incompletes are visible, not buried under passes.\n",[704,1093,1094,1097,1100,1103,1106,1109,1111,1114],{"class":579,"line":839},[704,1095,1096],{"class":716},"  if",[704,1098,1099],{"class":720}," (results.violations.",[704,1101,1102],{"class":780},"length",[704,1104,1105],{"class":720},") console.",[704,1107,1108],{"class":813},"error",[704,1110,805],{"class":720},[704,1112,1113],{"class":727},"'a11y violations'",[704,1115,1116],{"class":720},", results.violations);\n",[704,1118,1119,1121,1124,1126,1128,1131,1133,1136],{"class":579,"line":845},[704,1120,1096],{"class":716},[704,1122,1123],{"class":720}," (results.incomplete.",[704,1125,1102],{"class":780},[704,1127,1105],{"class":720},[704,1129,1130],{"class":813},"warn",[704,1132,805],{"class":720},[704,1134,1135],{"class":727},"'a11y needs review'",[704,1137,1138],{"class":720},", results.incomplete);\n",[704,1140,1141],{"class":579,"line":851},[704,1142,915],{"class":720},[324,1144,1145,1148,1149,1152],{},[345,1146,1147],{},"How to verify:"," open the console after each interaction. A correctly wired setup logs nothing for clean components and a grouped table for defects. Manually trigger the dynamic states (open the modal, submit the invalid form) and confirm axe re-runs and reports against the ",[527,1150,1151],{},"current"," DOM, not the initial render.",[433,1154],{},[436,1156,1158],{"id":1157},"axe-devtools-and-browser-usage","axe DevTools and Browser Usage",[324,1160,1161],{},"The axe DevTools browser extension is the fastest way to audit a running page without touching code. It runs the same engine, so its findings are identical to your programmatic runs—useful for confirming that a CI failure reproduces in a real browser, and for the \"Inspect\" affordance that highlights the failing node and shows the computed accessible name.",[324,1163,1164],{},"For scripted or one-off checks, run the engine straight from the console on any page that loads axe, or inject it:",[695,1166,1170],{"className":1167,"code":1168,"language":1169,"meta":700,"style":700},"language-js shiki shiki-themes github-light github-dark","\u002F\u002F Paste into the browser console after loading axe-core via a snippet\u002Fbookmarklet\naxe.run({ runOnly: { type: 'tag', values: ['wcag22aa'] } })\n  .then((r) => {\n    console.table(r.violations.map((v) => ({ id: v.id, impact: v.impact, nodes: v.nodes.length })));\n    console.log('needs review:', r.incomplete.map((i) => i.id));\n  });\n","js",[361,1171,1172,1177,1196,1216,1247,1276],{"__ignoreMap":700},[704,1173,1174],{"class":579,"line":706},[704,1175,1176],{"class":709},"\u002F\u002F Paste into the browser console after loading axe-core via a snippet\u002Fbookmarklet\n",[704,1178,1179,1182,1184,1187,1189,1191,1193],{"class":579,"line":713},[704,1180,1181],{"class":720},"axe.",[704,1183,1066],{"class":813},[704,1185,1186],{"class":720},"({ runOnly: { type: ",[704,1188,878],{"class":727},[704,1190,881],{"class":720},[704,1192,894],{"class":727},[704,1194,1195],{"class":720},"] } })\n",[704,1197,1198,1201,1203,1206,1209,1212,1214],{"class":579,"line":734},[704,1199,1200],{"class":720},"  .",[704,1202,814],{"class":813},[704,1204,1205],{"class":720},"((",[704,1207,1208],{"class":820},"r",[704,1210,1211],{"class":720},") ",[704,1213,833],{"class":716},[704,1215,836],{"class":720},[704,1217,1218,1221,1224,1227,1230,1232,1235,1237,1239,1242,1244],{"class":579,"line":749},[704,1219,1220],{"class":720},"    console.",[704,1222,1223],{"class":813},"table",[704,1225,1226],{"class":720},"(r.violations.",[704,1228,1229],{"class":813},"map",[704,1231,1205],{"class":720},[704,1233,1234],{"class":820},"v",[704,1236,1211],{"class":720},[704,1238,833],{"class":716},[704,1240,1241],{"class":720}," ({ id: v.id, impact: v.impact, nodes: v.nodes.",[704,1243,1102],{"class":780},[704,1245,1246],{"class":720}," })));\n",[704,1248,1249,1251,1254,1256,1259,1262,1264,1266,1269,1271,1273],{"class":579,"line":764},[704,1250,1220],{"class":720},[704,1252,1253],{"class":813},"log",[704,1255,805],{"class":720},[704,1257,1258],{"class":727},"'needs review:'",[704,1260,1261],{"class":720},", r.incomplete.",[704,1263,1229],{"class":813},[704,1265,1205],{"class":720},[704,1267,1268],{"class":820},"i",[704,1270,1211],{"class":720},[704,1272,833],{"class":716},[704,1274,1275],{"class":720}," i.id));\n",[704,1277,1278],{"class":579,"line":771},[704,1279,909],{"class":720},[324,1281,1282,1283,1286,1287,348,1290,348,1293,348,1296,1299,1300,86,1302,1304,1305,1307,1308,1310,1311,1313],{},"The ",[361,1284,1285],{},"impact"," field (",[361,1288,1289],{},"minor",[361,1291,1292],{},"moderate",[361,1294,1295],{},"serious",[361,1297,1298],{},"critical",") lets you triage. Treat ",[361,1301,1295],{},[361,1303,1298],{}," ",[361,1306,404],{}," and ",[361,1309,410],{}," failures as release blockers; schedule ",[361,1312,1289],{}," best-practice items.",[324,1315,1316,1318],{},[345,1317,1147],{}," run the extension, then re-run the same tag set via the console snippet on the identical page state—the violation IDs and node counts must match. A mismatch usually means the DOM changed between runs (animation, async data) and you need to freeze that state first.",[433,1320],{},[436,1322,1324],{"id":1323},"configuring-rules-and-wcag-tags","Configuring Rules and WCAG Tags",[324,1326,1327,1328,1331],{},"axe ships ~90 rules, each tagged. The ",[361,1329,1330],{},"runOnly"," option restricts execution to the tags you care about; this is how you keep results aligned with the conformance level you actually claim.",[1223,1333,1334,1347],{},[1335,1336,1337],"thead",{},[1338,1339,1340,1344],"tr",{},[1341,1342,1343],"th",{},"Tag",[1341,1345,1346],{},"Maps to",[1348,1349,1350,1360,1370,1379,1394],"tbody",{},[1338,1351,1352,1357],{},[1353,1354,1355],"td",{},[361,1356,373],{},[1353,1358,1359],{},"WCAG 2.0\u002F2.1\u002F2.2 Level A rules",[1338,1361,1362,1367],{},[1353,1363,1364],{},[361,1365,1366],{},"wcag2aa",[1353,1368,1369],{},"WCAG 2.0 Level AA",[1338,1371,1372,1376],{},[1353,1373,1374],{},[361,1375,376],{},[1353,1377,1378],{},"New AA criteria added in WCAG 2.1 (e.g. reflow, non-text contrast)",[1338,1380,1381,1385],{},[1353,1382,1383],{},[361,1384,379],{},[1353,1386,1387,1388,348,1391,943],{},"New AA criteria added in WCAG 2.2 (e.g. ",[361,1389,1390],{},"2.4.11 Focus Not Obscured",[361,1392,1393],{},"2.5.8 Target Size",[1338,1395,1396,1400],{},[1353,1397,1398],{},[361,1399,382],{},[1353,1401,1402],{},"Strong recommendations not tied to a specific SC",[324,1404,1405],{},"To target a typical \"Level AA, WCAG 2.2\" gate, combine the cumulative A and AA tags:",[695,1407,1409],{"className":983,"code":1408,"language":985,"meta":700,"style":700},"const axeConfig = {\n  runOnly: {\n    type: 'tag',\n    \u002F\u002F Cumulative: A + AA across 2.0, 2.1 and 2.2.\n    values: ['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa'],\n  },\n  rules: {\n    \u002F\u002F Disable a single rule globally only with a documented reason.\n    'region': { enabled: false }, \u002F\u002F landmark requirement handled by app shell\n  },\n};\n",[361,1410,1411,1423,1428,1438,1443,1466,1471,1476,1481,1498,1502],{"__ignoreMap":700},[704,1412,1413,1416,1419,1421],{"class":579,"line":706},[704,1414,1415],{"class":716},"const",[704,1417,1418],{"class":780}," axeConfig",[704,1420,1044],{"class":716},[704,1422,836],{"class":720},[704,1424,1425],{"class":579,"line":713},[704,1426,1427],{"class":720},"  runOnly: {\n",[704,1429,1430,1433,1435],{"class":579,"line":734},[704,1431,1432],{"class":720},"    type: ",[704,1434,878],{"class":727},[704,1436,1437],{"class":720},",\n",[704,1439,1440],{"class":579,"line":749},[704,1441,1442],{"class":709},"    \u002F\u002F Cumulative: A + AA across 2.0, 2.1 and 2.2.\n",[704,1444,1445,1448,1450,1452,1455,1457,1459,1461,1463],{"class":579,"line":764},[704,1446,1447],{"class":720},"    values: [",[704,1449,884],{"class":727},[704,1451,348],{"class":720},[704,1453,1454],{"class":727},"'wcag2aa'",[704,1456,348],{"class":720},[704,1458,889],{"class":727},[704,1460,348],{"class":720},[704,1462,894],{"class":727},[704,1464,1465],{"class":720},"],\n",[704,1467,1468],{"class":579,"line":771},[704,1469,1470],{"class":720},"  },\n",[704,1472,1473],{"class":579,"line":793},[704,1474,1475],{"class":720},"  rules: {\n",[704,1477,1478],{"class":579,"line":799},[704,1479,1480],{"class":709},"    \u002F\u002F Disable a single rule globally only with a documented reason.\n",[704,1482,1483,1486,1489,1492,1495],{"class":579,"line":839},[704,1484,1485],{"class":727},"    'region'",[704,1487,1488],{"class":720},": { enabled: ",[704,1490,1491],{"class":780},"false",[704,1493,1494],{"class":720}," }, ",[704,1496,1497],{"class":709},"\u002F\u002F landmark requirement handled by app shell\n",[704,1499,1500],{"class":579,"line":845},[704,1501,1470],{"class":720},[704,1503,1504],{"class":579,"line":851},[704,1505,1506],{"class":720},"};\n",[324,1508,1509,1510,1513,1514,356],{},"You can also configure per-rule behaviour with ",[361,1511,1512],{},"axe.configure()","—enable best-practice rules selectively, raise a rule's tags, or register project-specific checks. That is its own topic; see ",[328,1515,1517],{"href":1516},"\u002Ftesting-and-automating-accessibility\u002Fautomated-accessibility-testing-with-axe-core\u002Fwriting-custom-axe-core-rules\u002F","writing custom axe-core rules",[324,1519,1520,1522,1523,1526,1527,1529],{},[345,1521,1147],{}," log ",[361,1524,1525],{},"results.testEngine"," and the active rule set; assert in a test that the tag list matches your stated conformance target so a stray ",[361,1528,1330],{}," change can't silently narrow coverage. Manually spot-check one criterion per tag (e.g. tab a 2.2 target-size element) to confirm the gate behaves as configured.",[433,1531],{},[436,1533,1535],{"id":1534},"handling-false-positives-and-incomplete-results","Handling False Positives and \"Incomplete\" Results",[324,1537,1538,1539,1542],{},"True false positives in axe are rare—most \"false positives\" are real defects the author disagrees with, or ",[345,1540,1541],{},"incompletes"," mistaken for failures. Work through them in order:",[455,1544,1545,1565,1571],{},[341,1546,1547,1553,1554,1557,1558,1560,1561,356],{},[345,1548,1549,1550,1552],{},"Read the ",[361,1551,351],{}," entry first."," axe tells you ",[527,1555,1556],{},"why"," it couldn't decide. The ",[361,1559,414],{}," rule returns incomplete when the background is an image, a gradient, or a semi-transparent overlay it can't sample. This is expected behaviour, covered in depth in ",[328,1562,1564],{"href":1563},"\u002Ftesting-and-automating-accessibility\u002Fautomated-accessibility-testing-with-axe-core\u002Fcatching-color-contrast-failures-with-axe-core\u002F","catching color contrast failures with axe-core",[341,1566,1567,1570],{},[345,1568,1569],{},"Reproduce in a frozen DOM state."," Async content and animations cause flaky incompletes. Snapshot the state, then re-run.",[341,1572,1573,1576],{},[345,1574,1575],{},"Prefer narrowing context over disabling rules."," Exclude a known third-party widget by selector rather than turning a rule off everywhere:",[695,1578,1580],{"className":983,"code":1579,"language":985,"meta":700,"style":700},"await axe.run(\n  { exclude: [['#third-party-chat-widget']] }, \u002F\u002F not our DOM; audited separately\n  axeConfig,\n);\n",[361,1581,1582,1594,1608,1613],{"__ignoreMap":700},[704,1583,1584,1587,1589,1591],{"class":579,"line":706},[704,1585,1586],{"class":716},"await",[704,1588,1063],{"class":720},[704,1590,1066],{"class":813},[704,1592,1593],{"class":720},"(\n",[704,1595,1596,1599,1602,1605],{"class":579,"line":713},[704,1597,1598],{"class":720},"  { exclude: [[",[704,1600,1601],{"class":727},"'#third-party-chat-widget'",[704,1603,1604],{"class":720},"]] }, ",[704,1606,1607],{"class":709},"\u002F\u002F not our DOM; audited separately\n",[704,1609,1610],{"class":579,"line":734},[704,1611,1612],{"class":720},"  axeConfig,\n",[704,1614,1615],{"class":579,"line":749},[704,1616,1617],{"class":720},");\n",[455,1619,1620],{"start":749},[341,1621,1622,1625],{},[345,1623,1624],{},"Suppress at the node level, with a paper trail."," If a finding is genuinely a false positive, record it as a reviewed exception in your test harness rather than deleting the rule—so the suppression is visible in review and revisited when the dependency updates.",[324,1627,1628,1630],{},[345,1629,1147],{}," every suppressed or excluded item should have a comment naming the reason and an owner. Manually screen-reader-test anything you excluded—exclusion means \"tested elsewhere,\" not \"ignored.\"",[433,1632],{},[436,1634,1636],{"id":1635},"what-axe-cannot-detect","What axe Cannot Detect",[324,1638,1639,1640,1643],{},"This is the section that prevents false confidence. axe verifies ",[527,1641,1642],{},"machine-checkable"," properties; it cannot judge meaning. Automation reliably catches an estimated 30–40% of WCAG issues. The rest require human testing.",[338,1645,1646,1666,1675,1684,1698],{},[341,1647,1648,1651,1652,1304,1655,1658,1659,1662,1663,383],{},[345,1649,1650],{},"Meaningful alternative text."," axe flags a ",[527,1653,1654],{},"missing",[361,1656,1657],{},"alt",", but ",[361,1660,1661],{},"alt=\"image\""," on a product photo passes every check while telling a screen-reader user nothing (",[361,1664,1665],{},"1.1.1 Non-text Content",[341,1667,1668,1671,1672,383],{},[345,1669,1670],{},"Logical focus order."," axe confirms elements are focusable; it cannot tell whether tabbing through them follows a sensible sequence (",[361,1673,1674],{},"2.4.3 Focus Order",[341,1676,1677,1680,1681,1683],{},[345,1678,1679],{},"Sensible, unambiguous labels."," A button named \"Click here\" or three different \"Edit\" buttons with no distinguishing context pass ",[361,1682,404],{}," but fail real users.",[341,1685,1686,1689,1690,1693,1694,1697],{},[345,1687,1688],{},"Whether an ARIA pattern actually behaves correctly."," axe checks attribute validity, not interaction—a ",[361,1691,1692],{},"role=\"dialog\""," with no focus trap and no ",[361,1695,1696],{},"Escape"," handling passes structurally.",[341,1699,1700,1703],{},[345,1701,1702],{},"Reading order, error recovery, and cognitive load."," Entirely out of scope for any static rules engine.",[324,1705,1706,1707,1710,1711,356],{},"The takeaway: a clean axe run is a ",[345,1708,1709],{},"necessary, not sufficient"," condition. Combine it with keyboard-only walkthroughs and screen-reader testing—see ",[328,1712,1714],{"href":1713},"\u002Fcore-accessibility-principles-for-modern-frameworks\u002Fscreen-reader-compatibility-testing\u002F","screen reader compatibility testing",[433,1716],{},[436,1718,1720],{"id":1719},"common-pitfalls","Common Pitfalls",[455,1722,1723,1729,1735,1747,1757,1769],{},[341,1724,1725,1728],{},[345,1726,1727],{},"Treating \"incomplete\" as \"pass.\""," Needs-review results are the engine asking for help. Surface them in your reporting; never hide them behind a violations-only assertion.",[341,1730,1731,1734],{},[345,1732,1733],{},"Auditing the initial render only."," Most framework defects appear in dynamic states. Audit after interactions, not just on mount.",[341,1736,1737,1740,1741,1743,1744,1746],{},[345,1738,1739],{},"Running with the default tag set and claiming a specific conformance level."," If you gate on \"WCAG 2.2 AA,\" your ",[361,1742,1330],{}," must include ",[361,1745,379],{},"—the defaults won't.",[341,1748,1749,1752,1753,1756],{},[345,1750,1751],{},"Disabling a rule globally to clear one node."," Use ",[361,1754,1755],{},"exclude"," context or a documented node-level exception so coverage stays intact elsewhere.",[341,1758,1759,1762,1763,1765,1766,1768],{},[345,1760,1761],{},"Shipping axe in the production bundle."," Always guard ",[361,1764,363],{}," behind a ",[361,1767,781],{}," check and a dynamic import.",[341,1770,1771,1774],{},[345,1772,1773],{},"Equating a green run with accessibility."," Pair every automated pass with manual keyboard and screen-reader verification before you make conformance claims.",[433,1776],{},[436,1778,1780],{"id":1779},"frequently-asked-questions","Frequently Asked Questions",[324,1782,1783,1786,1787,1789,1790,1792,1793,1795],{},[345,1784,1785],{},"Why does axe report \"needs review\" (incomplete) instead of a pass or fail?","\nSome checks can't be fully automated. When a check's ",[361,1788,491],{}," function returns ",[361,1791,516],{},"—for example, ",[361,1794,414],{}," over a background image or gradient it can't sample—axe records the node as incomplete and asks a human to verify. Incompletes are not passes; treat them as a to-do list for manual review.",[324,1797,1798,1801],{},[345,1799,1800],{},"Is a clean axe-core run enough to claim WCAG 2.2 AA conformance?","\nNo. Automated rules reliably catch roughly 30–40% of WCAG issues—the machine-checkable ones. Meaningful alt text, logical focus order, unambiguous labels, and correct interactive behaviour all require manual keyboard and screen-reader testing. A green run is necessary but not sufficient evidence.",[324,1803,1804,1807,1809,1810,1812,1813,356],{},[345,1805,1806],{},"What's the difference between @axe-core\u002Freact and jest-axe?",[361,1808,363],{}," runs in the browser after every React commit and logs to the console for live development feedback. jest-axe runs the same engine inside unit tests against rendered component output and fails the test on violations. Use ",[361,1811,363],{}," while building and jest-axe to gate components in CI; see ",[328,1814,1816],{"href":1815},"\u002Ftesting-and-automating-accessibility\u002Fcomponent-testing-with-jest-axe\u002F","component testing with jest-axe",[324,1818,1819,1822,1823,1826,1827,348,1829,348,1831,352,1833,1835,1836,1838,1839,356],{},[345,1820,1821],{},"How do I scope axe to a specific WCAG version and level?","\nPass ",[361,1824,1825],{},"runOnly: { type: 'tag', values: [...] }",". For WCAG 2.2 AA, include the cumulative tags ",[361,1828,373],{},[361,1830,1366],{},[361,1832,376],{},[361,1834,379],{},". Omitting ",[361,1837,379],{}," silently drops the 2.2-specific criteria like ",[361,1840,1393],{},[324,1842,1843,1846],{},[345,1844,1845],{},"Can axe-core test framework code directly, or only the rendered output?","\nOnly the rendered DOM. axe runs in the page context and inspects what the browser has committed after hydration—it never reads your JSX, templates, or component source. This is why it catches hydration mismatches and conditionally rendered ARIA that static linters miss.",[324,1848,1849,1852],{},[345,1850,1851],{},"Should I disable a noisy rule to get a green build?","\nAlmost never globally. Prefer excluding a specific context (e.g. a third-party widget by selector) or recording a documented, reviewed node-level exception. Disabling a rule everywhere removes coverage from your own components too, and the regression it was catching will eventually ship.",[433,1854],{},[436,1856,1858],{"id":1857},"related-guides","Related guides",[338,1860,1861,1866,1871,1877,1883,1888],{},[341,1862,1863,1865],{},[328,1864,222],{"href":330}," — the guide.",[341,1867,1868,1870],{},[328,1869,261],{"href":1815}," — gate components in unit tests.",[341,1872,1873,1876],{},[328,1874,279],{"href":1875},"\u002Ftesting-and-automating-accessibility\u002Fend-to-end-accessibility-testing-with-playwright\u002F"," — audit full pages and flows.",[341,1878,1879,1882],{},[328,1880,297],{"href":1881},"\u002Ftesting-and-automating-accessibility\u002Fgating-accessibility-in-ci-cd-pipelines\u002F"," — turn audits into release blockers.",[341,1884,1885,1887],{},[328,1886,249],{"href":1563}," — interpret contrast results and incompletes.",[341,1889,1890,1892],{},[328,1891,255],{"href":1516}," — extend the engine with project-specific checks.",[1894,1895,1896],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":700,"searchDepth":713,"depth":713,"links":1898},[1899,1900,1901,1902,1903,1904,1905,1906,1907,1908,1909],{"id":335,"depth":734,"text":336},{"id":396,"depth":734,"text":397},{"id":438,"depth":713,"text":439},{"id":683,"depth":713,"text":684},{"id":1157,"depth":713,"text":1158},{"id":1323,"depth":713,"text":1324},{"id":1534,"depth":713,"text":1535},{"id":1635,"depth":713,"text":1636},{"id":1719,"depth":713,"text":1720},{"id":1779,"depth":713,"text":1780},{"id":1857,"depth":713,"text":1858},null,"Use axe-core to find real WCAG violations in modern framework UIs—how the rules engine works, integrating @axe-core\u002Freact, configuring rule tags, and automation's limits.","md",{},false,{"title":243,"description":1911},"Ro3YDlt1P-E0T0N-v4awc6gD1RKQujVu1MKykZx_mng",[1918,1957,1958,2021],{"title":5,"path":6,"stem":7,"children":1919},[1920,1921,1924,1927,1933,1939,1948,1954],{"title":10,"path":6,"stem":11},{"title":13,"path":14,"stem":15,"children":1922},[1923],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":1925},[1926],{"title":19,"path":20,"stem":21},{"title":25,"path":26,"stem":27,"children":1928},[1929,1930],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":1931},[1932],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":1934},[1935,1936],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":1937},[1938],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":1940},[1941,1942,1945],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":1943},[1944],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":1946},[1947],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":1949},[1950,1951],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":1952},[1953],{"title":73,"path":74,"stem":75},{"title":79,"path":80,"stem":81,"children":1955},[1956],{"title":79,"path":80,"stem":81},{"title":85,"path":86,"stem":87},{"title":89,"path":90,"stem":91,"children":1959},[1960,1961,1967,1979,1991,1994,2003,2015],{"title":94,"path":90,"stem":95},{"title":97,"path":98,"stem":99,"children":1962},[1963,1964],{"title":97,"path":98,"stem":99},{"title":103,"path":104,"stem":105,"children":1965},[1966],{"title":103,"path":104,"stem":105},{"title":109,"path":110,"stem":111,"children":1968},[1969,1970,1973,1976],{"title":109,"path":110,"stem":111},{"title":115,"path":116,"stem":117,"children":1971},[1972],{"title":115,"path":116,"stem":117},{"title":121,"path":122,"stem":123,"children":1974},[1975],{"title":121,"path":122,"stem":123},{"title":127,"path":128,"stem":129,"children":1977},[1978],{"title":127,"path":128,"stem":129},{"title":133,"path":134,"stem":135,"children":1980},[1981,1982,1985,1988],{"title":133,"path":134,"stem":135},{"title":139,"path":140,"stem":141,"children":1983},[1984],{"title":139,"path":140,"stem":141},{"title":145,"path":146,"stem":147,"children":1986},[1987],{"title":145,"path":146,"stem":147},{"title":151,"path":152,"stem":153,"children":1989},[1990],{"title":151,"path":152,"stem":153},{"title":157,"path":158,"stem":159,"children":1992},[1993],{"title":157,"path":158,"stem":159},{"title":163,"path":164,"stem":165,"children":1995},[1996,1997,2000],{"title":163,"path":164,"stem":165},{"title":169,"path":170,"stem":171,"children":1998},[1999],{"title":169,"path":170,"stem":171},{"title":175,"path":176,"stem":177,"children":2001},[2002],{"title":175,"path":176,"stem":177},{"title":181,"path":182,"stem":183,"children":2004},[2005,2006,2009,2012],{"title":181,"path":182,"stem":183},{"title":187,"path":188,"stem":189,"children":2007},[2008],{"title":187,"path":188,"stem":189},{"title":193,"path":194,"stem":195,"children":2010},[2011],{"title":193,"path":194,"stem":195},{"title":199,"path":200,"stem":201,"children":2013},[2014],{"title":199,"path":200,"stem":201},{"title":205,"path":206,"stem":207,"children":2016},[2017,2018],{"title":205,"path":206,"stem":207},{"title":211,"path":212,"stem":213,"children":2019},[2020],{"title":211,"path":212,"stem":213},{"title":217,"path":218,"stem":219,"children":2022},[2023,2024,2033,2042,2051,2060],{"title":222,"path":218,"stem":223},{"title":225,"path":226,"stem":227,"children":2025},[2026,2027,2030],{"title":225,"path":226,"stem":227},{"title":231,"path":232,"stem":233,"children":2028},[2029],{"title":231,"path":232,"stem":233},{"title":237,"path":238,"stem":239,"children":2031},[2032],{"title":237,"path":238,"stem":239},{"title":243,"path":244,"stem":245,"children":2034},[2035,2036,2039],{"title":243,"path":244,"stem":245},{"title":249,"path":250,"stem":251,"children":2037},[2038],{"title":249,"path":250,"stem":251},{"title":255,"path":256,"stem":257,"children":2040},[2041],{"title":255,"path":256,"stem":257},{"title":261,"path":262,"stem":263,"children":2043},[2044,2045,2048],{"title":261,"path":262,"stem":263},{"title":267,"path":268,"stem":269,"children":2046},[2047],{"title":267,"path":268,"stem":269},{"title":273,"path":274,"stem":275,"children":2049},[2050],{"title":273,"path":274,"stem":275},{"title":279,"path":280,"stem":281,"children":2052},[2053,2054,2057],{"title":279,"path":280,"stem":281},{"title":285,"path":286,"stem":287,"children":2055},[2056],{"title":285,"path":286,"stem":287},{"title":291,"path":292,"stem":293,"children":2058},[2059],{"title":291,"path":292,"stem":293},{"title":297,"path":298,"stem":299,"children":2061},[2062,2063,2066],{"title":297,"path":298,"stem":299},{"title":303,"path":304,"stem":305,"children":2064},[2065],{"title":303,"path":304,"stem":305},{"title":309,"path":310,"stem":311,"children":2067},[2068],{"title":309,"path":310,"stem":311},1781785523930]