Skip to content
Article

When Good ARIA Goes Quiet

We were improving an existing e‑commerce storefront, not greenfield HTML. We leaned on ARIA and checker tools. Then VoiceOver agreed with us in one browser and ignored the price in another, and later did the opposite in the cart. The lesson: bad ARIA can be worse than no ARIA.

TLDR

  • On a live-site retrofit we added ARIA first; a sale-price block looked correct until VoiceOver disagreed across Safari and Chrome.
  • The same markup failed in opposite directions on the product page and in the cart—one happy browser test was not enough.
  • No ARIA is better than bad ARIA: plain text in the DOM (visually hidden) replaced role="group" and a single aria-label.

Retrofitting a live site: no ARIA is better than bad ARIA

We were improving an existing e‑commerce storefront, not writing pages from scratch. Our first move was the familiar one: ARIA attributes, grouped labels, aria-hidden on prices we treated as decorative. Scanners looked happier; we felt productive. We had not yet lived the line from the WAI-ARIA Authoring Practices Guide—no ARIA is better than bad ARIA—until a price block proved it.

When the browsers disagreed

On a sale product detail page, VoiceOver skipped the price in Safari and read it in Chrome. In the browser where it worked, we could not reproduce the report, so we moved on. Weeks later the shopping cart used the same idea with the opposite result: silent in Chrome, spoken in Safari.

That flip was the clue. We had not stabilized accessibility; we had tuned how each browser built the accessibility tree, and the trees did not match.

We were late to a familiar problem. Browser interop work has tracked for years that the same DOM can produce different trees in Safari and Chrome; leaning on aria-label on generic wrappers has long been unreliable, with WebKit alone spending years adjusting how labeled groups surface to assistive technology.

What we had written

Sale prices need words, not only strikethrough. We wrapped both amounts in one labeled group and hid the visible figures with aria-hidden.

Before (problematic):

<div
  class="price-row"
  role="group"
  aria-label="Price reduced from $129.99 to $99.99"
>
  <strong aria-hidden="true">$99.99</strong>
  <span class="strike" aria-hidden="true">$129.99</span>
</div>

On paper it looked right: one role="group", one aria-label that stated the full sale, and aria-hidden on the figures so we would not double-announce. Automated scanners were happy with the attributes.

The problem was what assistive technology actually received. A plain <div> is not a native control, and browsers do not expose aria-label on that kind of wrapper the same way in every engine. We had also removed the only literal price text from the tree with aria-hidden, so the label was the entire announcement. When a browser failed to surface that group name, there was nothing left to read—not the sale amount, not the regular amount.

That is what our testers hit: silence on the product page in Safari and silence in the cart in Chrome, while the other browser in each pair read the price fine. The markup had not given users a stable fallback.

What we do now

We dropped role="group" and the single composite aria-label. The sale and regular amounts live as real strings in the document—available to assistive technology, hidden only visually—and the red price and strikethrough figures are presentation, marked aria-hidden so screen readers do not hear them twice.

After (stable across our tests):

<div class="price-row">
  <span class="sr-only">Sale price: $99.99.</span>
  <strong aria-hidden="true">$99.99</strong>
  <span class="sr-only">Regular price: $129.99.</span>
  <span class="strike" aria-hidden="true">$129.99</span>
</div>

In Safari and Chrome with VoiceOver, we hear “Sale price” and “Regular price” as separate phrases, each with its amount. The trailing periods add a small pause between them. Sighted shoppers still see the same layout. We reused the pattern anywhere sale pricing appears.

That is the first rule of ARIA use: HTML for the meaning, not extra roles on a <div>.

The lesson: native first, less ARIA, fewer surprises

The mistake was treating ARIA and audit tools as the main work. They are support: they can point at gaps and fill holes HTML cannot, but they should not replace ordinary markup when HTML already carries the meaning. Our unstable price was a reminder that the less ARIA you lean on for simple content, the more similar behavior you tend to get across browsers—each browser builds the accessibility tree differently; native elements are the part they agree on most often.

What we reach for now is the native path first: real text in the DOM, semantic structure, visually hidden copy only when the design hides the words sighted users see. Cross-browser checks with the same screen reader stay important, but they validate a pattern we already trust—not rescue a clever aria-label that only holds up in one engine.

I am happier with that approach—not because ARIA is evil, but because our ARIA made the experience unreliable in a way that was painfully hard to see until the browsers disagreed in opposite directions.

If we can start with a fresh site

On a retrofit you learn this after the fact. On a new initiative, project managers and designers can bake accessibility in from the wireframe phase: call out sale vs regular price in the spec, not only strikethrough and color, so engineering never has to invent a one-line ARIA fix under deadline.

Drag to pan. Use +/− or Ctrl/Cmd + scroll to zoom. Pinch to zoom on touch devices.

Drag to pan. Use +/− or Ctrl/Cmd + scroll to zoom. Pinch to zoom on touch devices.