Try this one on your own product page. Turn on a screen reader, tab or arrow your way down to the star rating under a product title, and listen for what gets announced. On a lot of sites, the answer is nothing. Not "4.5 out of 5 stars," not "rating," not even "image" — total silence, as if the widget doesn't exist. Five little SVG or icon-font stars that carry real information for every sighted visitor are, to a screen reader user, a gap in the page.
This is a genuinely common bug, and the good news is it has two clean, well-documented fixes depending on what kind of star widget you're dealing with. A read-only "4.5 stars from 200 reviews" display needs one treatment. An interactive "leave your rating" input needs another. Mixing them up — or skipping both — is how you end up with a rating system half your visitors can see and none of your screen reader users can.
The Stat: WCAG SC 1.1.1 Non-text Content (Level A) requires that a purely visual star-rating graphic have a text alternative conveying the same information — such as "4.5 out of 5 stars" — or it is invisible to a screen reader. (Source: W3C Web Content Accessibility Guidelines)
Why the stars go silent
Most star widgets are built from one of two ingredients: a row of SVG icons, or a row of icon-font glyphs (think a <span class="icon-star icon-star-filled"> repeated five times). Both are visual-only by default. An SVG with no role, no title, and no accessible name is either ignored outright or, worse, announced as a meaningless "image, image, image, image, image" — five separate unlabeled graphics with no indication of what they collectively mean. An icon font is even quieter: it's just a styled character or a background image, and unless something explicitly labels it, assistive tech has nothing to read.
Visually, the widget is doing its job. A sighted shopper glances at four and a half filled stars out of five and instantly understands the product is well-reviewed. Nothing about that glance requires reading a number — the shape of "mostly full row of stars" carries the meaning on its own. That's exactly the problem SC 1.1.1 Non-text Content exists to catch: information conveyed purely through a visual graphic needs an equivalent text alternative, because a graphic by itself isn't accessible to someone who can't see it.
The fix depends on whether your stars are something a user reads or something a user sets.
Case 1: the read-only display rating
This is the "4.5 out of 5 stars, 200 reviews" widget under a product title or review summary — informational, not interactive. Nobody clicks on it; it just reports a value within a fixed range. That's precisely the shape of the ARIA Authoring Practices Guide's Meter pattern, which is documented for exactly this: a read-only numeric value inside a known minimum and maximum.
A minimal, correct implementation looks like this:
<div role="img" aria-label="Rated 4.5 out of 5 stars">
<svg aria-hidden="true">...five star shapes...</svg>
</div>
Or, using the more semantically precise meter role when the surrounding markup supports it:
<div role="meter" aria-valuenow="4.5" aria-valuemin="0" aria-valuemax="5"
aria-label="Product rating: 4.5 out of 5 stars">
<svg aria-hidden="true">...five star shapes...</svg>
</div>
Either pattern works for SC 1.1.1 as long as the announced text states the actual value in plain language. The critical move in both versions is aria-hidden="true" on the decorative SVG itself — the visual stars stay on screen for sighted users, but a screen reader skips straight past the graphic and reads only the label that describes what the graphic shows.
Case 2: the interactive rating input
A "leave your review" widget is a different animal entirely — it's a form control, not a display. A user needs to know there are five selectable options, which one (if any) is currently selected, and be able to move between them with a keyboard. That's a mutually-exclusive selection from a fixed set, which is exactly what the ARIA Authoring Practices Guide's Radio Group pattern is built for.
In practice, that means treating each star as a radio option inside a labeled group, not as five independent buttons or, worse, five <div>s with click handlers and no role at all:
<div role="radiogroup" aria-label="Rate this product">
<button role="radio" aria-checked="false" aria-label="1 star">★</button>
<button role="radio" aria-checked="false" aria-label="2 stars">★★</button>
<button role="radio" aria-checked="false" aria-label="3 stars">★★★</button>
<button role="radio" aria-checked="true" aria-label="4 stars">★★★★</button>
<button role="radio" aria-checked="false" aria-label="5 stars">★★★★★</button>
</div>
Following the full Radio Group pattern also means arrow-key navigation moves focus between the five options, only the checked option (or the first, if none is checked yet) sits in the tab order, and aria-checked updates the moment a user picks a value — not just visually, but in the accessibility tree too.
Three mistakes that sneak in even after you know the fix
Once a team knows to add labels, a few subtler mistakes tend to show up in their place.
Announcing the fraction instead of the meaning. aria-label="4.5 stars" is fine, but aria-label="90%" on a rating widget forces a screen reader user to do math a sighted user never has to do — nobody looks at a row of stars and thinks "90 percent." Keep the announced text in the same units your sighted users see: stars out of a maximum, not a converted percentage.
Labeling the row but not the count. "4.5 out of 5 stars" is a good label for the rating itself, but if there's a review count next to it ("(200 reviews)"), that count is often a separate visual element with no text alternative of its own — or worse, it's the only accessible content, while the star value silently drops out. Both pieces of information need to reach a screen reader, ideally in one combined label like "Rated 4.5 out of 5 stars, based on 200 reviews," so nothing gets lost between the two visual elements.
Building the interactive version out of five unrelated buttons. A rating input made of five separate <button> elements with no radiogroup wrapper technically has accessible names on each star, but a screen reader user tabbing through them hears five disconnected buttons with no sense that they're mutually exclusive options in a single control, and no arrow-key shortcut between them. The wrapping radiogroup role, plus a group-level label like "Rate this product," is what turns five buttons into one coherent control — matching what a sighted user actually experiences as a single five-point scale, not five independent switches.
A quick checklist for either case
- Decide first: is this widget read-only display, or a selectable input? The two need different roles — don't reuse one pattern for both.
- For display ratings, hide the decorative graphic with
aria-hidden="true"and put the real value ("4.5 out of 5 stars") in anaria-labelor visible text. - For input ratings, structure the five options as a labeled
radiogroupper the ARIA Authoring Practices Guide's Radio Group pattern, with working arrow-key navigation. - Never rely on color or fill percentage alone — a half-filled star needs to resolve to a spoken number, not just a visual fraction.
- Test both versions with an actual screen reader after shipping; a role that's technically present but mis-wired is easy to miss in a code review.
| Widget type | Correct pattern | What gets announced |
|---|---|---|
| Read-only display (e.g., product summary) | role="img" label or role="meter" |
"Rated 4.5 out of 5 stars" |
| Interactive input (e.g., leave a review) | role="radiogroup" of radio options |
"Rate this product, 4 stars, radio button, checked, 4 of 5" |
| Neither (unlabeled SVG or icon font) | — | Nothing, or "image, image, image..." |
Getting this right matters for more than compliance box-checking — a shopper deciding between two products by ear needs the rating just as much as a shopper deciding by eye, and a review form that silently refuses to tell a screen reader user which star they just picked is a form some percentage of visitors will simply abandon.
If you've never actually listened to your own rating widget with a screen reader, it's worth five minutes today — and if you'd rather have a systematic pass instead of spot-checking page by page, you can find silent widgets on your own site with a free scan and get a concrete list of what to fix first. For the deeper spec language behind all of this, WCAG 1.1.1 Non-text Content is the reference worth bookmarking.
