Skip to content
easeweb
Search topics

Icons and images drawn only in CSS, with no text

A plan comparison table draws its ticks and dashes as CSS background images in empty cells, so a screen reader reads every cell as blank and its user can't tell which plan includes what.

WCAG 2.2
SC 1.1.1ASC 1.3.1A
Required by
Section 508 · EN 301 549 (EU)
Broken <td class="no"></td>
The accessibility problem illustrated: No vision:Does Shop come with gift cards?
01

The problem

Screen reader users can't tell which plan includes which feature, because every cell of the comparison table is read as blank.

02

Try it

Compare the table with what a screen reader reads in it.

The panel shows what a screen reader reads going down the Shop column. Does Shop come with a custom domain? With gift cards?

Broken Can you fix this?

BrokenTicks and dashes as background images in empty cells
StarterShopPro
Card payments
Custom domain
Gift cards

Screen reader says:

  1. Card payments, blank
  2. Custom domain, blank
  3. Gift cards, blank

A recreated demo. The broken version is intentionally inaccessible.

03

The fix

Give every tick and dash words in the HTML, hidden or visible, or make it an img with alt text.

  1. 1What a CSS image shows is also in the HTML: as words or as an img with alt.
  2. 2No data cell is left empty when it shows something.
  3. 3Keep CSS images for decoration, which can disappear without losing anything.
Before Ticks and dashes as background images in empty cells
<table class="plans">  <thead>    <tr>      <td></td>      <th scope="col">Starter</th>      <th scope="col">Shop</th>      <th scope="col">Pro</th>    </tr>  </thead>  <tbody>    <tr>      <th scope="row">Card payments</th>      <td class="yes"></td>      <td class="yes"></td>      <td class="yes"></td>    </tr>    <tr>      <th scope="row">Custom domain</th>      <td class="no"></td>      <td class="yes"></td>      <td class="yes"></td>    </tr>    <tr>      <th scope="row">Gift cards</th>      <td class="no"></td>      <td class="no"></td>      <td class="yes"></td>    </tr>  </tbody></table>
After

A hidden word in each cell

<table class="plans">  <thead>    <tr>      <td></td>      <th scope="col">Starter</th>      <th scope="col">Shop</th>      <th scope="col">Pro</th>    </tr>  </thead>  <tbody>    <tr>      <th scope="row">Card payments</th>      <td class="yes"><span class="visually-hidden">Included</span></td>      <td class="yes"><span class="visually-hidden">Included</span></td>      <td class="yes"><span class="visually-hidden">Included</span></td>    </tr>    <tr>      <th scope="row">Custom domain</th>      <td class="no"><span class="visually-hidden">Not included</span></td>      <td class="yes"><span class="visually-hidden">Included</span></td>      <td class="yes"><span class="visually-hidden">Included</span></td>    </tr>    <tr>      <th scope="row">Gift cards</th>      <td class="no"><span class="visually-hidden">Not included</span></td>      <td class="no"><span class="visually-hidden">Not included</span></td>      <td class="yes"><span class="visually-hidden">Included</span></td>    </tr>  </tbody></table>

Fixing this with an AI coding assistant? Get this guide as Markdown, or get every guide in your coding agent.

04

Design ideas

A comparison can stay clean and still be read.

Three more ways to show what a plan includes, and two popular ones whose answer lives only in the stylesheet. The notes say what a screen reader gets from each.

In the design file

  • Write the words each mark stands for next to it in the file, such as “Included” and “Not included”.
  • Draw every cell or line with a mark or a value, and never leave a cell empty to mean “no”.
  • Draw a missing feature with its own mark, not only greyed out or struck through.
  • Draw the marks at 3:1 or more against the page, with the ratios written beside them.

Passes

  • ShopPro
    Staff accounts2Unlimited
    Storage5 GB100 GB
    Real values instead of ticks Where a feature has an amount, the cell says it, “2” or “Unlimited”, in plain text. Nothing needs a key, and a screen reader reads each value with its row.
  • ShopPro
    Custom domain
    Gift cards
    Filled and hollow dots, named A quieter mark than a tick: a filled green dot, #1e7b34 at 5.33:1, and a hollow grey ring, #70757a at 4.65:1. Each is an image in the cell named “Included” or “Not included”.
  • Shop

    • Included:Card payments
    • Included:Custom domain
    • Not included:Gift cards
    Choose Shop
    Plan card that marks what is missing The pricing card lists every feature, with a tick or a cross before each, and a missing one in a softer grey, #6b7280 at 4.83:1. Each mark has its words, so the cross is read as “Not included”.

Fails

  • Shop

    • Card payments
    • Custom domain
    • Gift cards
    Choose Shop
    Missing features greyed and struck throughWCAG 1.3.1 A popular card look, but the only sign that gift cards are missing is the grey and the line through them, both from the stylesheet. Screen readers with their usual settings read the line like the others, as if it were included.
  • ShopPro
    Support
    Setup help
    Support level drawn as CSS starsWCAG 1.1.1 Stars cut from a mask image look crisp at any size, but the cells hold nothing, so “Support” is read with no level at all. Give the row its level in words, such as “Priority”, or make the stars one image named “3 out of 3”.
05

Verify the fix

4 checks, no mouse.

Test with: Screen reader, Styles off

0 of 4 checked

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat whenever the pricing table's template or its stylesheet changes.

Limits

This is a starting pattern, not a guarantee for every application. Paths, class names and the plans are placeholders. Screen readers read table cells in slightly different ways, so test with the assistive technology your users have. The same rule covers SVG and img icons used the same way; see SVG icons with no name, or read when they should be silent and emoji read out by their names, not their meaning. Test the complete journey with your target assistive technology.

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.

Keep this fixed as your code changes.

Turn guides like this into rules your coding agents and CI follow, then have the result retested with a keyboard and screen readers.

How retests work