<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Giriprasad P]]></title><description><![CDATA[Giriprasad P]]></description><link>https://adaguard.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6ad66ab0cbae0ccccb7f1735/5364a2d7-d62b-4c5c-9856-8bf8d41ee871.png</url><title>Giriprasad P</title><link>https://adaguard.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 27 Oct 2026 22:09:40 GMT</lastBuildDate><atom:link href="https://adaguard.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[4 Accessibility Mistakes AI Coding Assistants Make, and How to Catch Them]]></title><description><![CDATA[Clickable divs, aria-labels that don't match the visible text, placeholder labels, removed focus outlines — the WCAG criterion each fails and how to catch…]]></description><link>https://adaguard.hashnode.dev/4-accessibility-mistakes-ai-coding-assistants-make-and-how-to-catch-them</link><guid isPermaLink="true">https://adaguard.hashnode.dev/4-accessibility-mistakes-ai-coding-assistants-make-and-how-to-catch-them</guid><category><![CDATA[AI]]></category><category><![CDATA[#WCAG]]></category><category><![CDATA[React]]></category><category><![CDATA[keyboard]]></category><dc:creator><![CDATA[Giriprasad Patil]]></dc:creator><pubDate>Wed, 21 Oct 2026 15:29:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ad66ab0cbae0ccccb7f1735/634b89d2-0845-4a5c-8e2b-3a32d647839b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>AI coding assistants are good at producing working components quickly. They learned from a huge amount of public code, and public code is full of accessibility mistakes — so the same mistakes come back in generated components, looking clean and passing a quick visual review.</p>
<p>Here are four that show up often, the WCAG criterion each one fails, and how to catch them before they merge.</p>
<h2>1. A clickable <code>div</code> with no keyboard support (WCAG 2.1.1, 4.1.2)</h2>
<p>Ask for "a clickable card" or "a custom button" and you'll often get this:</p>
<pre><code class="language-jsx">// Works with a mouse. A keyboard user can't reach it or activate it.
&lt;div className="btn" onClick={save}&gt;Save&lt;/div&gt;
</code></pre>
<p>Adding <code>role="button"</code> alone doesn't fix it: the element still isn't in the tab order and doesn't respond to Enter or Space.</p>
<pre><code class="language-jsx">// Fix: use the element that already does all of this.
&lt;button type="button" className="btn" onClick={save}&gt;Save&lt;/button&gt;
</code></pre>
<p>A <code>&lt;button&gt;</code> is focusable, activates on Enter and Space, and is announced as a button. Reach for <code>role</code> and <code>tabIndex</code> only when no native element fits.</p>
<h2>2. An <code>aria-label</code> that doesn't match the visible text (WCAG 2.5.3)</h2>
<p>Assistants like adding <code>aria-label</code> "for accessibility", often with a more descriptive phrase than the button shows:</p>
<pre><code class="language-html">&lt;!-- Visible text "Buy now"; accessible name "Add product to basket" --&gt;
&lt;button aria-label="Add product to basket"&gt;Buy now&lt;/button&gt;
</code></pre>
<p>The <code>aria-label</code> replaces the visible text as the accessible name. A voice-control user who says "click Buy now" gets nothing, because no control is called "Buy now". WCAG 2.5.3 (Label in Name) requires the accessible name to contain the visible label.</p>
<pre><code class="language-html">&lt;!-- Fix: let the visible text be the name --&gt;
&lt;button&gt;Buy now&lt;/button&gt;
</code></pre>
<p>If you need more context for screen reader users, keep the visible words at the start of the name: <code>aria-label="Buy now: Linen shirt"</code>.</p>
<h2>3. A placeholder used as the label (WCAG 3.3.2, 1.3.1)</h2>
<pre><code class="language-html">&lt;input type="email" placeholder="Email address"&gt;
</code></pre>
<p>The placeholder disappears as soon as someone starts typing, it's usually low contrast, and it isn't a reliable label. Give every input a visible, associated label:</p>
<pre><code class="language-html">&lt;label for="email"&gt;Email address&lt;/label&gt;
&lt;input id="email" type="email" autocomplete="email"&gt;
</code></pre>
<h2>4. Focus outlines removed (WCAG 2.4.7)</h2>
<p>Generated CSS often includes a reset like this:</p>
<pre><code class="language-css">*:focus { outline: none; }
</code></pre>
<p>It removes the only sign of where a keyboard user is on the page. If the default ring doesn't fit the design, replace it rather than removing it, and keep it to keyboard focus:</p>
<pre><code class="language-css">:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}
</code></pre>
<h2>Catching these in review</h2>
<p>Each check below catches a different subset, so use more than one:</p>
<ul>
<li><strong>A linter in the editor.</strong> <code>eslint-plugin-jsx-a11y</code> flags click handlers on non-interactive elements and inputs without labels while you type.</li>
<li><strong>Prefer native elements in prompts and review.</strong> Asking the assistant for "a <code>&lt;button&gt;</code>" or "a native <code>&lt;dialog&gt;</code>" gets better output than "a clickable element".</li>
<li><strong>A keyboard pass.</strong> Tab through the new component. If you can't reach or operate it, it fails.</li>
<li><strong>A check of the rendered page.</strong> Mistakes 1, 2 and 4 depend on the rendered page — computed names, focus styles, what's focusable. ADAGuard loads the page in a real browser and checks those, including label-in-name. <a href="/free-accessibility-scanner">Scan a page free</a>.</li>
</ul>
<p>Avoid "fix it at runtime" scripts as the safety net. A script that edits the page in each visitor's browser leaves the source unchanged, so every mistake is still in the code the assistant learned from your repo. See <a href="/accessibility-overlay-trap-legal-risk">why overlays don't fix accessibility</a>.</p>
<p>AI assistants are useful here too: once a failure is found, they're good at applying a known fix across many files. The part they can't do is notice that a component they just wrote doesn't work with a keyboard. That takes a test.</p>
]]></content:encoded></item><item><title><![CDATA[A Perfect Automated Accessibility Score Doesn't Test These 4 Things]]></title><description><![CDATA[An automated accessibility score checks the page as loaded. Four failures that only appear after interaction, with the WCAG criterion and code fix.]]></description><link>https://adaguard.hashnode.dev/a-perfect-automated-accessibility-score-doesn-t-test-these-4-things</link><guid isPermaLink="true">https://adaguard.hashnode.dev/a-perfect-automated-accessibility-score-doesn-t-test-these-4-things</guid><category><![CDATA[webdev]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[React]]></category><category><![CDATA[Accessibility]]></category><dc:creator><![CDATA[Giriprasad Patil]]></dc:creator><pubDate>Tue, 20 Oct 2026 06:03:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ad66ab0cbae0ccccb7f1735/67b70eeb-8a8c-4123-aea8-910736af45df.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A green 100 on an automated accessibility audit is a good sign. It means the page has no failures that a rule engine can find in the page as it first loads: images without alt text, inputs without labels, low-contrast text, invalid ARIA.</p>
<p>It doesn't mean a keyboard or screen reader user can get through the page. Automated rules run against one snapshot — one theme, one viewport, before anyone clicks anything — and many WCAG failures only exist after an interaction. Here are four that a perfect score routinely misses, with the criterion each one fails and the fix.</p>
<h2>1. Focus that goes nowhere when a dialog closes (WCAG 2.4.3)</h2>
<p>A dialog that opens and closes correctly for a mouse user can strand a keyboard user. When the dialog unmounts, the element that had focus is gone, and the browser drops focus back to the top of the document. A screen reader user who pressed "Add to cart" now hears the page from the beginning.</p>
<p>A rule engine looking at the page as loaded never opens the dialog, so it never sees this.</p>
<pre><code class="language-jsx">// Remember what opened the dialog, and return focus there when it closes.
function useReturnFocus(open) {
  const opener = useRef(null);
  useEffect(() =&gt; {
    if (open) {
      opener.current = document.activeElement;
    } else if (opener.current) {
      opener.current.focus();
      opener.current = null;
    }
  }, [open]);
}
</code></pre>
<p>While the dialog is open, keep focus inside it — the native <code>&lt;dialog&gt;</code> element opened with <code>showModal()</code> does that for you, and closes on Escape. More patterns: <a href="/focus-management-modal-close">managing focus after a modal closes</a>.</p>
<h2>2. Status messages nobody hears (WCAG 4.1.3)</h2>
<p>"Added to cart", "3 results", "Email already in use" — messages that appear after an action are visible to sighted users instantly. A screen reader announces them only if they're rendered into a live region that was already in the page before the text changed.</p>
<pre><code class="language-html">&lt;!-- In the page from the start, empty --&gt;
&lt;div role="status" aria-live="polite" id="cart-status"&gt;&lt;/div&gt;
</code></pre>
<pre><code class="language-js">// Later: change only its text
document.getElementById('cart-status').textContent = 'Added to cart';
</code></pre>
<p>A live region created at the same moment as its message is often not announced. Rendering the container on load and updating its text is the reliable pattern. Full walkthrough: <a href="/dynamic-content-updates-screen-readers-aria-live">announcing dynamic content to screen readers</a>.</p>
<h2>3. Menus that only open on hover (WCAG 2.1.1)</h2>
<p>A navigation submenu revealed with <code>:hover</code> works for a mouse and does nothing for a keyboard. The links inside exist in the HTML, so a check of the loaded page finds nothing wrong — but a keyboard user can never reach them.</p>
<pre><code class="language-html">&lt;li&gt;
  &lt;button type="button" aria-expanded="false" aria-controls="menu-products"&gt;Products&lt;/button&gt;
  &lt;ul id="menu-products" hidden&gt;
    &lt;li&gt;&lt;a href="/shirts"&gt;Shirts&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/li&gt;
</code></pre>
<p>Toggle <code>aria-expanded</code> and <code>hidden</code> from the button's click handler, close the menu on Escape and return focus to the button. A button gets Enter and Space handling for free. Full pattern: <a href="/multi-level-navigation-accessibility">accessible multi-level navigation</a>.</p>
<h2>4. Contrast in the theme the scan didn't load (WCAG 1.4.3)</h2>
<p>An automated scan sees whichever theme the page renders by default. If that's the light theme, the dark theme is never measured — and colours that pass on white often fail on a dark surface.</p>
<p>Measured with the WCAG contrast formula:</p>
<table>
<thead>
<tr>
<th>Text</th>
<th>Background</th>
<th>Ratio</th>
<th>4.5:1 for body text</th>
</tr>
</thead>
<tbody><tr>
<td><code>#475569</code> (slate 600)</td>
<td><code>#FFFFFF</code></td>
<td>7.58:1</td>
<td>Passes</td>
</tr>
<tr>
<td><code>#475569</code></td>
<td><code>#0B0F19</code></td>
<td>2.53:1</td>
<td>Fails</td>
</tr>
<tr>
<td><code>#2563EB</code> (blue 600)</td>
<td><code>#FFFFFF</code></td>
<td>5.17:1</td>
<td>Passes</td>
</tr>
<tr>
<td><code>#2563EB</code></td>
<td><code>#0F172A</code></td>
<td>3.45:1</td>
<td>Fails</td>
</tr>
</tbody></table>
<p>The same tokens, a different background, a different result. Check every theme's pairs on purpose — the <a href="/tools/color-contrast-checker">color contrast checker</a> takes any two colours — and see <a href="/dark-mode-accessibility-wcag-contrast">dark mode and WCAG contrast</a> for a palette that works.</p>
<h2>Testing what a score can't see</h2>
<p>None of this makes automated checks less useful: they catch the most common failures on every page, quickly, every time you deploy. Pair them with the checks that need interaction:</p>
<ul>
<li><strong>A keyboard pass.</strong> Unplug the mouse. Tab through every flow, open and close every dialog and menu, and watch where focus goes.</li>
<li><strong>A screen reader pass</strong> on the flows that matter most — sign-up, checkout, search — with VoiceOver, NVDA or Narrator, all free.</li>
<li><strong>Each theme and breakpoint</strong> you ship, not only the default.</li>
</ul>
<p>ADAGuard runs some interaction checks in a real browser — it presses Tab to find keyboard traps, and measures whether a focused element is hidden behind a sticky header — but no automated tool covers everything above. <a href="/free-accessibility-scanner">Scan a page free</a> to see what it finds, and <a href="/wcag-coverage">what it tests automatically</a> is listed criterion by criterion.</p>
]]></content:encoded></item><item><title><![CDATA[A Dark Mode Palette That Passes WCAG: The Contrast Values, Checked]]></title><description><![CDATA[A dark mode palette with every WCAG contrast ratio measured - body text, muted text, links and 3:1 input borders - and the light-mode colours that fail.]]></description><link>https://adaguard.hashnode.dev/dark-mode-palette-wcag-contrast-values</link><guid isPermaLink="true">https://adaguard.hashnode.dev/dark-mode-palette-wcag-contrast-values</guid><category><![CDATA[CSS]]></category><category><![CDATA[webdev]]></category><category><![CDATA[Accessibility]]></category><category><![CDATA[Design]]></category><dc:creator><![CDATA[Giriprasad Patil]]></dc:creator><pubDate>Mon, 19 Oct 2026 19:24:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ad66ab0cbae0ccccb7f1735/d3f15adb-9cd1-46dc-85c0-9fe1d1a3bd95.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Color contrast is the number one accessibility failure on the web. According to WebAIM's Million analysis, 83.6% of all websites fail WCAG contrast requirements on at least one element. Adding a dark mode toggle to your site doesn't fix that statistic — for most teams, it creates an entirely new set of contrast failures that no one checked because the light mode tested fine.</p>
<p>Test individual color pairs with the <a href="/tools/color-contrast-checker">color contrast checker</a>; for code-level fixes in React apps, see the <a href="/accessibility-checker/react">React accessibility checker</a>.</p>
<p>The ada compliance color checker problem in 2026 is this: most teams test contrast for one theme. They pick colors for light mode, run them through a contrast ratio tool, confirm they pass 4.5:1, and ship. Then they add a dark mode using CSS custom properties, invert a few background variables, and assume the same colors will work in reverse. They rarely do.</p>
<h2>What WCAG Actually Requires for Contrast</h2>
<p>WCAG 2.1 Level AA sets two minimum contrast ratios that apply to every color combination on your site — in every theme:</p>
<p><strong>1.4.3 Contrast (Minimum):</strong> At least 4.5:1 for normal text (under 18pt regular or 14pt bold). At least 3:1 for large text (18pt+ regular or 14pt+ bold). This ratio applies to text against its immediate background.</p>
<p><strong>1.4.11 Non-text Contrast:</strong> At least 3:1 for user interface components (buttons, form inputs, focus indicators, icons that convey meaning) against adjacent colors.</p>
<p><strong>Both thresholds apply independently to every theme.</strong> A color pair that achieves 5.2:1 in light mode and 2.8:1 in dark mode is non-compliant. The dark mode version fails 1.4.3 regardless of the light mode result. If you offer a dark mode, you must run an ada compliance color checker against your dark theme separately.</p>
<h2>Why Dark Mode Creates New Contrast Failures</h2>
<p>The instinct when building dark mode is to invert: swap your white background for a dark background, keep your brand colors, change your text from dark to light. This works visually for most colors. It fails accessibility for many.</p>
<p><strong>Pure black is too harsh, saturated colors "vibrate."</strong> Accessibility specialists consistently recommend against using pure black (#000000) as a dark mode background. The extreme contrast between pure black and white text (21:1) can cause eye fatigue and halation effects for users with certain visual impairments. Industry standard dark backgrounds use very dark grays (#121212, #1e1e1e) rather than true black.</p>
<p>More importantly, fully saturated brand colors — the bright reds, oranges, and yellows that pop on a white background — often fail contrast requirements on dark backgrounds. A bright orange (#FF6B00) achieves 4.8:1 on white (#FFFFFF), passing AA. The same orange achieves only 3.1:1 on dark gray (#121212), failing AA for normal text. Same hex value, opposite compliance outcome depending on theme.</p>
<p><strong>Semantic states lose meaning in dark mode.</strong> Error states (red borders, red text), success states (green badges), warning indicators (yellow icons) — these are color-coded signals that may pass contrast in light mode and fail in dark mode. Because they appear infrequently (only on form errors or validation), they're often the last thing tested. They're also the most legally significant: a form that cannot communicate an error state to a low-vision user is a direct barrier to completing a task.</p>
<h2>The <code>prefers-color-scheme</code> Problem</h2>
<p>Developers who implement dark mode via CSS <code>prefers-color-scheme</code> — the media query that automatically switches the theme based on the user's OS setting — often never see the dark version in their browser during development. Their browser is in light mode. They build, test, and ship in light mode. The dark mode exists in the CSS and is served to users who prefer it, but no one on the team ran an ada compliance color checker against it.</p>
<p>A minimal but meaningful test workflow:</p>
<pre><code class="language-css">/* In your browser DevTools — Forces dark mode for testing */
@media (prefers-color-scheme: dark) {
  /* Your dark mode tokens go here */
}
</code></pre>
<p>In Chrome DevTools → Rendering tab → Emulate CSS media feature <code>prefers-color-scheme</code> → set to <code>dark</code>. This lets you view the dark theme without changing your OS setting, enabling contrast testing in the browser.</p>
<h2>What to Test and Where the Failures Hide</h2>
<table>
<thead>
<tr>
<th>UI Element</th>
<th>Light Mode Risk</th>
<th>Dark Mode Risk</th>
<th>WCAG Criterion</th>
</tr>
</thead>
<tbody><tr>
<td>Body text</td>
<td>Low (if initially tested)</td>
<td>Medium — inverted palette may drop ratio</td>
<td>1.4.3</td>
</tr>
<tr>
<td>Placeholder text</td>
<td>High — often set to gray at 3:1 or less</td>
<td>Higher — same gray looks different on dark bg</td>
<td>1.4.3</td>
</tr>
<tr>
<td>Disabled button text</td>
<td>Medium — often intentionally low contrast</td>
<td>High — dark mode inversion makes it worse</td>
<td>1.4.3 (Note 1 exception applies but check)</td>
</tr>
<tr>
<td>Form input borders</td>
<td>Medium — light gray borders on white fail 1.4.11</td>
<td>High — dark mode border colors frequently fail</td>
<td>1.4.11</td>
</tr>
<tr>
<td>Focus indicator (outline)</td>
<td>High — default browser outline removed by CSS</td>
<td>Very High — dark mode often has no visible focus</td>
<td>2.4.7 / 2.4.11</td>
</tr>
<tr>
<td>Error state text</td>
<td>Low if checked</td>
<td>High — red on dark bg often fails</td>
<td>1.4.3</td>
</tr>
<tr>
<td>Icon color</td>
<td>Medium — icon-only elements need 3:1</td>
<td>High — icon color may be inverted incorrectly</td>
<td>1.4.11</td>
</tr>
<tr>
<td>Link text color</td>
<td>Low if initially blue</td>
<td>Medium — dark mode link color often under-contrasted</td>
<td>1.4.3</td>
</tr>
<tr>
<td>CTA button</td>
<td>Low if checked</td>
<td>Medium — brand-color button on dark bg often fails</td>
<td>1.4.3 + 1.4.11</td>
</tr>
</tbody></table>
<h2>Testing Both Themes Systematically</h2>
<p>The correct approach is to run your ada compliance contrast checker against both themes separately. For web surfaces, use the same tool you'd use for light mode — but capture the dark mode state.</p>
<p>For automated scanning, ADAGuard at <a href="https://www.adaguard.io">adaguard.io</a> scans your site's live-rendered DOM including JavaScript-applied styles. If your dark mode is applied via a class toggle (e.g., <code>class="dark"</code> on ``), you can share the dark-mode URL or state with a scanner that supports URL parameters. ADAGuard's ada compliance color checker functionality is part of its full 23-category scan, covering contrast ratios across the rendered page alongside form labels, alt text, ARIA, and focus indicators.</p>
<p>For manual review, use the browser DevTools color picker or tools like the Colour Contrast Analyser desktop app to spot-check your most common text/background combinations in both themes.</p>
<h2>Building Contrast Compliance Into Your Design System</h2>
<p>The highest-leverage fix is upstream: define accessible dark mode color tokens at the design system level, not per-component.</p>
<p>Rather than allowing any designer to use any color on any dark background, pre-define an approved palette for dark mode that has been verified against 4.5:1 for text and 3:1 for UI components. Document which combinations are approved, which are prohibited, and which require special consideration (large-text only, icon-only with text label). Build this into your Figma variable library or design tokens spec so that the contrast requirements are enforced at the design stage, not discovered in a post-launch ada compliance color checker audit.</p>
<p>The EAA has been in enforcement since June 28, 2025, and color contrast is one of the most easily detected violations — regulators and plaintiff attorneys can run automated checks against your dark theme just as easily as your light theme.</p>
<h2>The 30-Second Fix</h2>
<p>Run an ADAGuard scan right now. Paste your URL at <a href="https://www.adaguard.io">adaguard.io</a> — no signup required — and get a full WCAG 2.2 AA report including color contrast analysis in under 30 seconds. ADAGuard checks both contrast thresholds (1.4.3 and 1.4.11) across your live-rendered page, plus 20 other check categories covering ARIA, form labels, alt text, and focus indicators. See exactly which contrast failures exist on your current theme — then run the same scan on your dark mode state before a regulator or plaintiff attorney does it for you.</p>
]]></content:encoded></item></channel></rss>