# Old prices and removed words shown only by a strike-through

Source: https://easeweb.dev/learn/struck-through-prices
Topics: Page structure > Struck-through, inserted and highlighted text says what it means (WCAG 1.3.1)
The fix: Write the words beside the prices, either on the page ("Was $40, Now $28") or as visually hidden text that keeps the design. Marking the old price with s passes the meaning to assistive technology as well, but on its own it is read out only by screen readers that report deleted text.
Test with: screen reader, styles off
References: https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html https://www.w3.org/WAI/WCAG22/Techniques/failures/F2 https://www.w3.org/WAI/WCAG22/Techniques/general/G117 https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-s-element https://www.w3.org/TR/html-aam-1.0/

This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.

## What happens

A shop's product list shows a linen tote on sale. Under its name, $40 is drawn small and grey with a line through it, and $28 sits beside it in bold. Anyone who can see the card knows at once that the tote was $40 and is now $28.

A screen reader user moving through the list hears "Linen tote, $40, $28". The line through $40 is drawn by CSS and isn't part of the text, so both prices arrive alike. The next product, an enamel mug, is read as "Enamel mug, $18", so the first card simply has two prices, and only a guess, or adding it to the basket, says which one applies.

## Why it fails

Success criterion 1.3.1, Info and Relationships, is level A. Information that the presentation conveys, such as which price is the old one, must also be determinable from the code or available in the text. W3C's failure F2 describes exactly this: a change in how text looks carries meaning, with no markup or text to carry it too.

The line-through is a CSS text decoration, not markup. In Chromium's accessibility tree, a span with `text-decoration: line-through` holding "$40" is plain text, the same as "$28" beside it. The line is passed on only as text formatting, as bold is, and NVDA, for one, doesn't report font attributes such as strikethrough at its default settings. A screen reader can only read what it is given. W3C's sufficient technique G117 is to convey the same information in text, and that is what the first two fixes here do.

## Who is affected

Online shoppers compare prices on the list page, not only in the basket. When the sale is on the product card and nowhere else, the card is where the difference has to be heard.

## What automation and AI miss

The HTML is valid, the grey of the old price has enough contrast, and axe has no rule for text decoration. EaseWeb's audit reports a price drawn with a line through it by CSS alone, with no word around it, shown or hidden, that says it is the old one (`ew:struck-price-not-marked`). It recognises prices by their currency, so it leaves struck-through to-do items alone, and it can't tell whether other styling, such as bold, colour or a highlight, carries a meaning that needs words.

## Before: the old price struck through by CSS

```html
<li class="product">
  <img src="linen-tote.jpg" alt="">
  <h3>Linen tote</h3>
  <p class="price">
    <span class="old">$40</span>
    <span class="now">$28</span>
  </p>
</li>
```

```css
.price .old {
  color: #6b6f7b;
  text-decoration: line-through;
}
```

The line is only in the CSS, so the text of the price is "$40 $28". Nothing in the markup or the words says which of the two is the old one.

## After: write was and now on the page

```html
<li class="product">
  <img src="linen-tote.jpg" alt="">
  <h3>Linen tote</h3>
  <p class="price">
    Was <span class="old">$40</span>
    Now <span class="now">$28</span>
  </p>
</li>
```

```css
.price .old {
  color: #6b6f7b;
  text-decoration: line-through;
}
```

Two short words say what the line says. A screen reader reads "Was $40, Now $28", a read-aloud tool reads the same, and a sighted shopper who doesn't know what the line means reads it too. The line stays, so the card looks much as it did.

## After: hidden words that keep the design

```html
<li class="product">
  <img src="linen-tote.jpg" alt="">
  <h3>Linen tote</h3>
  <p class="price">
    <span class="visually-hidden">Was</span>
    <span class="old">$40</span>
    <span class="visually-hidden">, now</span>
    <span class="now">$28</span>
  </p>
</li>
```

```css
.price .old {
  color: #6b6f7b;
  text-decoration: line-through;
}
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
```

The card looks exactly as designed, and the words are in the text, so a screen reader reads "Was $40, now $28". The hidden class keeps the words in the accessibility tree while drawing nothing; `display: none` or `aria-hidden` would hide them from screen readers too. Sighted shoppers who don't know the convention still meet only the line.

## After: the s element, when the design can't take words

```html
<li class="product">
  <img src="linen-tote.jpg" alt="">
  <h3>Linen tote</h3>
  <p class="price">
    <s class="old">$40</s>
    <span class="now">$28</span>
  </p>
</li>
```

```css
.price .old {
  color: #6b6f7b;
  text-decoration: line-through;
}
```

HTML's `s` element marks text that is no longer accurate, such as an old price, and Chromium passes it to assistive technology with the role `deletion`. So the meaning is now in the markup, not only the styling. Whether a screen reader says anything about it depends on the screen reader and its settings, and where it doesn't, the card is still read as "$40, $28". It doesn't help read-aloud tools or sighted readers either. Use it with one of the word fixes, not on its own.

## Which option to use

All three put the meaning somewhere other than the styling. Use the visible words: they give every reader the same information, including people who see the line but don't know what it means, for the cost of two short words. Use hidden words when the design really can't take them; screen readers get the same result. Add `s` to either if you like, but don't rely on it alone, since its effect depends on the screen reader.

## Implementation decisions

Use `s` for a price that is no longer accurate, and `del` and `ins` for edits to a document, such as tracked changes or a suggested rewrite. They are different things: `del` says the text was removed, `s` that it no longer applies. Tracked changes have the same problem as prices, twice over: read aloud, the removed and the added words run together. Put hidden words such as "removed:" and "added:" at each change, as the tracked-changes sample does in the design ideas of [AI rewrite buttons that replace text with no preview or undo](/learn/ai-rewrites).

Don't carry the words in an `aria-label` on the `p` or a `span`. Those elements can't be named, and many screen readers skip the label; see [aria-label on divs and spans that screen readers often skip](/learn/aria-label-on-generic).

When the whole card is one link, the link's name is made of its text, so it includes the prices. The words then become part of the name, "Linen tote, Was $40, Now $28", which is what you want.

Keep the old price readable. Grey for the old price is fine at 4.5:1 or more: #6b6f7b on white is 5.02:1. A grey below 4.5:1 fails on its own terms; see [low-contrast text on brand colours, photos and hover states](/learn/text-contrast).

In a list of many products, write the words the same way every time, from one price component, so a screen reader user hears one pattern.

## Verify the fix

1. Read a product on sale with a screen reader, line by line, and confirm you hear which price was and which is now, before the next product starts.
2. Turn styles off, or open the page in a browser's reader view, and confirm the old price is still told apart from the price charged.
3. Where `s` or `del` is used, read it with each screen reader you support, at its default settings, and note what each one says about it.

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the price component or the shop's theme changes.

## Limits

This is a starting pattern, not a guarantee for every page. The example is an old price; the same goes for other text whose look carries meaning, such as a highlight that marks a match, bold that marks the one important word, or text in red that says something is wrong: say it in words as well. A `blockquote` used only to indent text, so that it is announced as a quotation it isn't, is another failure of the same criterion (F43), not covered here. Test the product list, the product page and the basket with the people and tools your users rely on.

---

Get this guide in your coding agent: MCP https://easeweb.dev/mcp (setup at https://easeweb.dev/services/developers)
