Accessible Sale Prices: Why a Struck-Through Price Alone Fails Screen Reader Users
Quick answer: a struck-through original price next to a sale price is a relationship —
“this price replaced that one” — conveyed entirely through visual styling. MDN’s own reference
for the <s> element states
plainly that “the presence of the s element is not announced by most screen reading technology
in its default configuration.” A screen reader user tabbing past <s>$49.99</s> $34.99 typically
hears “$49.99 $34.99” — two numbers, no indication which is which. WCAG SC 1.3.1 Info and
Relationships (Level A)
requires that relationship be programmatically determinable, and the fix isn’t a different HTML
element — it’s pairing whatever element you use with visually hidden text that states the
relationship in words.
This article covers technical accessibility practice, not legal advice — it explains what WCAG 2.1 AA requires for sale-price display and how to meet it, not whether a specific implementation carries legal risk.
A strikethrough is a visual convention, not an announced one
Sighted shoppers learn the strikethrough-price convention early: a line through a number means
“this used to be the price; it isn’t anymore.” Nothing about that convention transfers
automatically to a screen reader. The <s> and <del> elements exist specifically to carry this
“struck-out” meaning in markup rather than CSS alone — the HTML Accessibility API
Mappings map <del> to a deletion role and <ins> to an
insertion role — but having a correct semantic mapping and having assistive technology actually
announce it are two different things. MDN’s guidance on <s> is direct about the gap: “not
announced by most screen reading technology in its default configuration.” Swapping a plain
<span class="strikethrough"> for a semantically-correct <s> or <del> is still good practice
— it’s the right element for the content — but by itself, it does not solve the announcement
problem this article is about.
What WCAG actually requires
SC 1.3.1 Info and Relationships (Level A) is the criterion that governs this. Its Intent section states the principle generally: “information and relationships that are implied by visual or auditory formatting are preserved when the presentation format changes” — meaning a screen reader, a braille display, or a high-contrast mode all need access to the same relationship a sighted mouse user sees. The Understanding doc’s own worked example is close to this exact situation, one level simpler: “An on-line catalog may indicate prices using a larger font colored red. A screen reader or person who cannot perceive red, still has the information about the price as long as it is preceded by the currency symbol.” That example covers a single price’s legibility. A sale display adds a second layer on top: it’s not just “can a screen reader read this number,” it’s “can a screen reader tell which of these two numbers is which” — a relationship between two pieces of content, not the content of either one alone.
A related, narrower failure shows up when a site skips the strikethrough convention entirely and signals “this is the discounted price” using color alone — red text, a highlighted background, no strikethrough and no label. SC 1.4.1 Use of Color (Level A) covers this directly: “color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element.” The Understanding doc’s own list of failure patterns names almost this exact case — “‘error is shown in red’” as an example of information conveyed by color differences alone. A sale price shown only in red, with no strikethrough on the original and no text label, fails the same way.
What real screen-reader testing shows
Web Axe’s “Strikethrough Accessibility” article — a named, checkable practitioner source, not an anonymous SEO write-up — ran the actual comparison across NVDA, JAWS, VoiceOver, and TalkBack on four markup approaches:
- A bare
<s>or<del>element, no ARIA, no extra text. Only NVDA and TalkBack picked up and announced the strikethrough semantic; JAWS and VoiceOver did not. <s role="deletion">, an explicit ARIA role added on top. Nearly identical results, with VoiceOver on iOS also passing this time — still not a reliable cross-tool result.- Visually hidden text stating the relationship in words (e.g., “Original price,” “Sale price”) alongside the visible strikethrough. Every screen reader tested passed.
- CSS-generated content via
::before/::afterpseudo-elements announcing “start of stricken text” / “end of stricken text” — the technique MDN itself documents as a fallback. This worked in some tools but produced ambiguous output in NVDA and broken output in JAWS.
The pattern across all four scenarios is consistent: relying on the element’s built-in semantics, with or without an explicit ARIA role, is not reliable across the tools people actually use. Visually hidden text was the only approach that passed everywhere it was tested.
The fix: visually hidden text, not a different element
The working pattern pairs the visible strikethrough with hidden text that states the relationship explicitly, so a screen reader announces something a shopper can act on instead of two bare numbers:
<p class="price">
<span class="visually-hidden">Original price: </span>
<del>$49.99</del>
<span class="visually-hidden">Now: </span>
<ins class="price-sale">$34.99</ins>
</p>
Sighted shoppers still see exactly what they see today — a struck-through number and a highlighted
one. A screen reader now announces “Original price: $49.99. Now: $34.99.” instead of two adjacent,
unexplained dollar amounts. <del>/<ins> are still the right elements to use for their own
sake — the hidden text is what makes the relationship reliably announced, not the elements’
implicit roles.
The same fix applies to the color-only case: adding a small, real (not aria-hidden) text label —
“Sale,” or a visually hidden “discounted price:” — next to a red price satisfies SC 1.4.1 without
requiring any redesign of the visual treatment.
Don’t overdo it
MDN’s own guidance includes a caution worth carrying over directly: “some people who use screen readers deliberately disable announcing content that creates extra verbosity. Because of this, it is important to not abuse this technique and only apply it in situations where not knowing content has been struck out would adversely affect understanding.” A price comparison is exactly that kind of situation — knowing which number is current changes what a shopper does next. This isn’t license to wrap every visual distinction on a page in hidden labels; it’s specific to cases where the distinction carries real decision-relevant meaning, the way a discount does.
How this differs from two things Quietramp has already covered
This is a different failure than the one in Quietramp’s images of text
article, which covers sale badges built as a
flattened image graphic instead of live text — that article names <del> as the right element
to use once the badge is rebuilt as real text, but doesn’t cover whether that element’s meaning
actually reaches a screen reader once it’s there. It’s also different from the strikethrough
mention in Quietramp’s checkout and cart roundup
article, which flags “was $49.99”
purely as a low-contrast text problem under SC 1.4.3 — a page can have perfect contrast on both
prices and still fail the criterion this article covers, because contrast and semantic
announcement are two independent requirements.
Can an automated scanner catch this?
No. A direct check of axe-core’s own rule
descriptions
turns up no rule targeting strikethrough text, <del>/<ins> semantics, or price relationships at
all. That tracks with what the failure actually is: the markup can be perfectly valid HTML — a
correctly nested <del> and <ins>, no ARIA errors, no missing attributes — and still fail to
communicate anything, because the gap is in what a screen reader chooses to announce by default,
not in anything a rule-based parser can detect from the markup alone.
FAQ
Does using <del> and <ins> instead of <span> fix this on its own?
No. They’re the semantically correct elements to reach for, and worth using, but Web Axe’s own
testing found bare <del>/<s> — with or without an explicit ARIA role — inconsistently
announced across NVDA, JAWS, VoiceOver, and TalkBack. Visually hidden text is what made every
tool tested pass.
Do I need to add hidden text to every visual style change on the page, not just prices? No — MDN’s own guidance is to reserve this for cases where missing the distinction would change a user’s understanding or decision, which a price change clearly does. Applying it indiscriminately adds verbosity some screen reader users specifically try to avoid.
Is <s> or <del> the better choice for a former price?
<del> is generally the closer semantic fit — the HTML
spec describes it as marking
a “removal from the document,” which matches a superseded price more precisely than <s>’s more
general “no longer accurate” meaning. Either is acceptable paired with the visually hidden text
fix; neither is a substitute for it.
Get a full audit that checks this by hand
Whether a struck-through price is actually announced with enough context to be useful is a screen-reader behavioral question, not something a markup scanner resolves on its own — it has to be listened to. Quietramp’s audits pair an automated axe-core scan with a manual screen-reader review that catches exactly this kind of gap, delivered as a prioritized, developer-actionable PDF report. See a real sample report or check pricing — $890 one-time, $99/month for ongoing re-checks after fixes ship.
This article is general accessibility information, not legal advice. Meeting WCAG 2.1 AA does not resolve ADA or any other legal compliance question, and nothing here should be read as a compliance guarantee. Consult qualified counsel for legal risk questions.
This article was drafted with AI assistance and reviewed by a person for accuracy before publication.