<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Manichandra Sajjanapu</title>
    <description>The latest articles on DEV Community by Manichandra Sajjanapu (@manichandra_sajjanapu).</description>
    <link>https://dev.to/manichandra_sajjanapu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4018474%2Fec24cd2d-36fa-4df8-a3a4-9fc01a448f6e.png</url>
      <title>DEV Community: Manichandra Sajjanapu</title>
      <link>https://dev.to/manichandra_sajjanapu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/manichandra_sajjanapu"/>
    <language>en</language>
    <item>
      <title>One ARIA bug, a million apps: meet aria-reach</title>
      <dc:creator>Manichandra Sajjanapu</dc:creator>
      <pubDate>Mon, 06 Jul 2026 23:26:59 +0000</pubDate>
      <link>https://dev.to/manichandra_sajjanapu/one-aria-bug-a-million-apps-meet-aria-reach-86h</link>
      <guid>https://dev.to/manichandra_sajjanapu/one-aria-bug-a-million-apps-meet-aria-reach-86h</guid>
      <description>&lt;p&gt;Most accessibility tools audit &lt;em&gt;your application&lt;/em&gt;. But in a modern JavaScript app, the accessibility of what ships is mostly decided one layer down — in the &lt;strong&gt;shared component libraries&lt;/strong&gt; you import: the UI kit, the rich-text editor, the media player, the form framework.&lt;/p&gt;

&lt;p&gt;That layer is where accessibility bugs &lt;em&gt;scale&lt;/em&gt;. A single misconfigured &lt;code&gt;aria-*&lt;/code&gt; attribute in a popular library can propagate into downstream apps that import it. A wrong &lt;code&gt;aria-live&lt;/code&gt; value in a media player can affect sites that embed it. The flip side is the opportunity: &lt;strong&gt;a confirmed upstream fix can benefit many consumers as they adopt the corrected release.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;aria-reach&lt;/code&gt; is a small open-source analyzer built around that idea. It scans for ARIA anti-patterns in component libraries (and in any live page), attributes findings to their likely upstream source, and — the part I haven't seen elsewhere — &lt;strong&gt;ranks them by reach&lt;/strong&gt;, so you spend effort where it helps the most assistive-technology users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; aria-reach
aria-reach scan src/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The four classes of anti-pattern
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;aria-reach&lt;/code&gt; organizes findings into a four-class taxonomy. Each class is grounded in a real, concrete defect pattern from a widely used library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class I — Decorative Noise Injection.&lt;/strong&gt; Decorative elements leak into the accessibility tree and get announced as content. Classic case: breadcrumb separators (&lt;code&gt;/&lt;/code&gt;, &lt;code&gt;›&lt;/code&gt;) read aloud between every link. The fix is &lt;code&gt;aria-hidden="true"&lt;/code&gt; on the decorative node.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class II — Live-Region Urgency Miscalibration.&lt;/strong&gt; Routine, non-urgent updates announced with &lt;code&gt;aria-live="assertive"&lt;/code&gt; (or &lt;code&gt;role="alert"&lt;/code&gt;), which &lt;em&gt;interrupts&lt;/em&gt; whatever the screen reader is currently saying. A media player that re-announces description text on every update becomes unusable. Routine status belongs in a &lt;strong&gt;polite&lt;/strong&gt; live region (&lt;code&gt;aria-live="polite"&lt;/code&gt; / &lt;code&gt;role="status"&lt;/code&gt;); assertive is for genuinely time-critical alerts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class III — Widget Role Contract Violations.&lt;/strong&gt; A widget breaks the W3C role contract that assistive tech relies on. Two common shapes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A calendar day cell using &lt;code&gt;aria-pressed&lt;/code&gt; — so a screen reader announces &lt;em&gt;"button pressed"&lt;/em&gt; instead of &lt;em&gt;"selected."&lt;/em&gt; A day in a grid is a &lt;strong&gt;selection&lt;/strong&gt;, so it needs &lt;code&gt;aria-selected&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A custom dropdown built from &lt;code&gt;div&lt;/code&gt;s with no &lt;code&gt;role="listbox"&lt;/code&gt;/&lt;code&gt;role="option"&lt;/code&gt;, no &lt;code&gt;aria-selected&lt;/code&gt;, and no keyboard support — invisible to AT as a selectable list.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Class IV — Async State Desynchronization.&lt;/strong&gt; The accessibility state and the actual state drift apart across async boundaries — e.g., a form submit firing before async validators resolve, so the user is told the form is valid when it isn't.&lt;/p&gt;

&lt;p&gt;Each rule maps to a specific WAI-ARIA 1.2 / WCAG 2.1 success criterion (mostly SC 4.1.2 &lt;em&gt;Name, Role, Value&lt;/em&gt; and SC 4.1.3 &lt;em&gt;Status Messages&lt;/em&gt;), and each is grounded in a real upstream contribution.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes it different: reach scoring
&lt;/h2&gt;

&lt;p&gt;Finding ARIA issues is the easy part; &lt;strong&gt;prioritizing&lt;/strong&gt; them is the hard part. axe-core and friends give you a flat list per page. &lt;code&gt;aria-reach&lt;/code&gt; weights each finding by a &lt;strong&gt;Library Reach Index&lt;/strong&gt; — roughly &lt;em&gt;weekly npm downloads × estimated downstream deployments&lt;/em&gt; — so a defect in a library pulled millions of times a week outranks a one-off in your own code. You fix the things that touch the most people first.&lt;/p&gt;

&lt;p&gt;It also &lt;strong&gt;attributes&lt;/strong&gt; findings to their likely origin (PrimeNG, Angular Material, Quill, Video.js, USWDS, MUI, …), so a finding becomes an &lt;em&gt;upstream&lt;/em&gt; fix opportunity — the force multiplier — rather than a local patch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two ways to scan
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Static&lt;/strong&gt; — templates, including Angular inline templates extracted from &lt;code&gt;.ts&lt;/code&gt;/&lt;code&gt;.js&lt;/code&gt; with line numbers mapped back to source. It understands Angular binding syntax (&lt;code&gt;[attr.aria-hidden]="expr"&lt;/code&gt; counts as handled, and unknowable bound values are never false-flagged):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aria-reach scan src/            &lt;span class="c"&gt;# .html + inline Angular templates&lt;/span&gt;
aria-reach scan src/ &lt;span class="nt"&gt;--json&lt;/span&gt;     &lt;span class="c"&gt;# machine-readable, for CI&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI exits &lt;code&gt;1&lt;/code&gt; on any error-severity finding, so it can gate a CI job. As a worked example, scanning PrimeNG's own library source surfaces &lt;strong&gt;172 candidate findings across 51 component files&lt;/strong&gt; — a useful map of where to look (you still validate a sample by hand; the goal is to point effort, not to auto-merge).&lt;/p&gt;

&lt;p&gt;Drop it into CI with the GitHub Action — it fails the build on error-severity findings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;manichandra/aria-reach@v0.1.2&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src&lt;/span&gt;              &lt;span class="c1"&gt;# fail-on-error: false for report-only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Runtime&lt;/strong&gt; — the runtime-detectable rules (Classes I–III) run against the rendered DOM of &lt;em&gt;any&lt;/em&gt; app (React, Vue, Angular, vanilla — at runtime it's all DOM). Recognized DOM fingerprints provide heuristic, likely-origin labels that require confirmation. Paste the built browser bundle into a DevTools console, or load the browser extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;ariaReach&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;     &lt;span class="c1"&gt;// grouped report in the console&lt;/span&gt;
&lt;span class="nx"&gt;ariaReach&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// counts by class + reach&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Try it (and break it)
&lt;/h2&gt;

&lt;p&gt;It's MIT-licensed, Node ≥ 18, and archived with a DOI so it's citable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;npm:&lt;/strong&gt; &lt;code&gt;npm install -g aria-reach&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/manichandra/aria-reach" rel="noopener noreferrer"&gt;https://github.com/manichandra/aria-reach&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI:&lt;/strong&gt; GitHub Action — &lt;code&gt;uses: manichandra/aria-reach@v0.1.2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser:&lt;/strong&gt; the DevTools extension — &lt;code&gt;&amp;lt;!-- add Chrome Web Store URL after approval --&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zenodo (DOI):&lt;/strong&gt; &lt;a href="https://doi.org/10.5281/zenodo.20681179" rel="noopener noreferrer"&gt;https://doi.org/10.5281/zenodo.20681179&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's the reference implementation of an ARIA anti-pattern taxonomy I'm writing up in a paper (under review; preprint to follow). If you maintain a component library, I'd love a scan result or a counter-example that breaks a rule — open an issue or a PR. The most useful thing you can do is point it at a library you depend on, confirm the highest-reach finding, and propose the fix upstream.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built and maintained on personal time. Feedback and contributions welcome.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>githubactions</category>
    </item>
  </channel>
</rss>
