Search topics
Smiley-face ratings announced only as "button"
A feedback card asks how a suggestion was with five faces, each a button whose only content is a drawing hidden from assistive technology and whose title is empty, so every face is announced as "button", and the words "Very bad" and "Very good" sit apart under the ends, tied to no face.
<button><svg aria-hidden="true"> The problem
Screen reader users hear five buttons with no names and can't tell which face means what, and voice control users can't name a face to press it, so they can't answer, or send an answer they didn't mean.
A recipe site shows a suggested recipe, and under it a small card asks "How is this suggestion?" Five round faces sit in a row, from a scowl to a broad grin, with "Very bad" under the first and "Very good" under the last. Faces are quicker to answer than a form, and a row of them fits in a corner of the page. A click on a face sends the answer.
Tab into the card with a screen reader on. It says "button", then "button" again, five times, once for each face. Nothing says which face is which, or which end is the good one. Read from the top, the card is "How is this suggestion?", then "button" five times, and only then "Very bad" and "Very good", as two lines of text after all the buttons, tied to neither end. A voice control user says "click Very good", and nothing happens: no button has that name.
Each face is a real button, so Tab reaches it and Enter presses it. What it lacks is a name. A browser builds a button's name from its aria-labelledby, then its aria-label, then a label tied to it, then the text inside it, and as a last resort from its title. Here the only thing inside is the face drawing, hidden with aria-hidden="true", and the title is there but empty, so every source comes up empty and the button is announced by its role alone. An empty title suggests a shared icon-button component: it has a place for a name, and the card left it blank.
That fails three criteria. 4.1.2 Name, Role, Value: each face is a control with a role and no name, so software can't say what pressing it does. WCAG lists this as failure F68. 1.1.1 Non-text Content: the faces are pictures that carry the answer, and a picture that works as a control needs a text alternative that says its purpose. Here "Bad", "Okay" and "Good" exist only as the shape of a mouth. 1.3.1 Info and Relationships: the layout shows that the question above governs the five faces, and that "Very bad" belongs to the first face and "Very good" to the last. In the code, the question is a div tied to nothing, the faces aren't grouped under it, and the two words sit in a div of their own after all the buttons. That is why they are read last, attached to no face.
Short feedback cards come at the end of something: a video, a support chat, a suggestion. When the faces have no names, people who can't see them skip the question or press a face at random, so their answers are either missing or a guess, and the site reads the faces' meaning only from people who can see them.
2 of 10 groups affected
- No vision (affected)
- Low vision (not affected)
- Colour vision (not affected)
- No hearing (not affected)
- Hard of hearing (not affected)
- No speech (not affected)
- Motor (affected)
- Reach, strength (not affected)
- Cognitive (not affected)
- Seizures (not affected)
- Without vision Each face is read as "button", five times in a row, and "Very bad" and "Very good" come only after the last one, tied to neither end, so which way the scale runs and what each face means are both lost; pressing one sends a guess.
- Limited manipulation Voice control users press a button by saying its name. With no names, "click Very good" does nothing, and they fall back to numbered overlays.
axe reports each face as button-name: a button whose only content is hidden with aria-hidden, and whose title is empty, has no name. EaseWeb's audit runs axe and reports the five faces as axe:button-name. That is as far as a scan goes. It can't tell whether a name gives the value: aria-label="Frowning face", "face-2" from the icon's file name, or "1" all pass. Nor can it tell that the question is tied to nothing, or that the end words are left loose after the buttons. Asked to clear the finding, AI coding tools can name the faces after their drawings; add aria-label to the hidden svg instead of the button, which leaves the button with no name, because a hidden element adds nothing to it; or name only the two ends, from the words under them, and leave the middle three blank. Tab through the faces with a screen reader and check that each one says its value.
Try it
Press Tab to move through the faces, and listen to what each one is called.
The panel shows what a screen reader says as focus lands on a face, and the whole card read from the top. Which face is “Very good”, and where are those words read?
Broken Can you fix this?
Screen reader says:
(Tab to a face)
Reading the card from the top:
- How is this suggestion?
- button
- button
- button
- button
- button
- Very bad
- Very good
The fix
Name each face by its value in words, and make the question the name of the group.
- 1Name every face by its value in words, with text inside its button.
- 2Group the faces under the question, as a fieldset's legend.
- 3Put words shown beside a face inside its button, where they are its name.
<form class="survey" action="/feedback" method="post"> <div> <div class="question">How is this suggestion?</div> <div class="faces"> <button class="face" name="answer" value="1" title=""> <svg aria-hidden="true"> <use href="#face-very-bad"></use> </svg> </button> <button class="face" name="answer" value="2" title=""> <svg aria-hidden="true"> <use href="#face-bad"></use> </svg> </button> <button class="face" name="answer" value="3" title=""> <svg aria-hidden="true"> <use href="#face-okay"></use> </svg> </button> <button class="face" name="answer" value="4" title=""> <svg aria-hidden="true"> <use href="#face-good"></use> </svg> </button> <button class="face" name="answer" value="5" title=""> <svg aria-hidden="true"> <use href="#face-very-good"></use> </svg> </button> </div> <div class="ends"> <div>Very bad</div> <div>Very good</div> </div> </div></form>The values in the buttons, the design kept
<form class="survey" action="/feedback" method="post"> <fieldset> <legend class="question">How is this suggestion?</legend> <div class="faces"> <button class="face" name="answer" value="1"> <svg aria-hidden="true"> <use href="#face-very-bad"></use> </svg> <span class="end">Very bad</span> </button> <button class="face" name="answer" value="2"> <svg aria-hidden="true"> <use href="#face-bad"></use> </svg> <span class="visually-hidden">Bad</span> </button> <button class="face" name="answer" value="3"> <svg aria-hidden="true"> <use href="#face-okay"></use> </svg> <span class="visually-hidden">Okay</span> </button> <button class="face" name="answer" value="4"> <svg aria-hidden="true"> <use href="#face-good"></use> </svg> <span class="visually-hidden">Good</span> </button> <button class="face" name="answer" value="5"> <svg aria-hidden="true"> <use href="#face-very-good"></use> </svg> <span class="end">Very good</span> </button> </div> </fieldset></form>Each button holds only a drawing hidden from assistive technology and an empty title, so none of the five has a name. The question and the two words are separate divs, tied to no button: on screen they frame the row, and in the code they are text before and after it.
The card looks as it was drawn: the same faces, and the same two words under the ends. Each button now holds its value as text, so a screen reader says "Very bad, button" through to "Very good, button", and voice control presses a face when its word is said. The two end words have moved inside the first and last buttons, still drawn under them, so the words on screen are those buttons' names and are no longer read loose after the row. The middle three have their words in visually hidden text. The question is the legend of a fieldset around the faces, so screen readers typically name the group when Tab moves focus into it, with something like "How is this suggestion?, group". The empty title is gone: it named nothing.
Both pass all three criteria. The values in the buttons keep the card exactly as drawn, and suit a design that has no room to grow. A word under every face changes the resting look, and gives the meaning of each face to everyone who reads the card: someone who finds faces hard to read, someone who sees the row through a magnifier a face at a time, someone unsure what the middle faces mean on this site. Choose it when the card has the room; on a narrow phone, check in the design file that "Very good" fits under its face, or draw it on two lines.
These faces send the answer the moment one is pressed, so they are buttons: one press, one answer, as the real card behaves. When the scale is one question in a longer form that is sent later, or the answer can be changed before it is sent, build it as a group of radio buttons instead, as star ratings that keyboards and screen readers skip shows for stars; the faces are drawn over the radio buttons the same way. The keys differ with the build. Tab moves from one face button to the next, and Enter or Space sends that answer; a radio group is one Tab stop, and the arrow keys move the choice. That is why a radio group must not send on its own: the arrow keys check each face they pass, so a keyboard user moving from "Very bad" to "Good" would send "Very bad" on the first press. A radio group needs a button to send it.
Filling in the empty title would also name each button, since browsers fall back on it when nothing else gives a name, and it passes 4.1.2. Many browsers show its tooltip only when a mouse rests on the face, so sighted people on a keyboard or a touch screen don't see the words. Use it only when the component takes nothing else. An aria-label on each button names it the same way hidden text does; icon-only buttons with no accessible name compares the two. Whichever is used, it goes on the button: an aria-label on an svg with aria-hidden="true" is ignored.
A fieldset can group buttons as well as fields. Where the layout can't take one, an element with role="group" and aria-labelledby pointing at the question does the same job. Name the faces in the order they are drawn, from bad to good, so the names run the same way as the row. The words of the scale are the site's to choose: if the card asks "How useful was this?", the names are "Not useful" to "Very useful", the same words as the ends.
Fixing this with an AI coding assistant? Get this guide as Markdown, or get every guide in your coding agent.
Design ideas
Faces, pills or thumbs, as long as every answer has its word.
Four ways to draw a quick feedback question. Try each with Tab: three give every answer a word, and one leaves a screen reader to read out the pictures.
In the design file
- Write a word for every face, from “Very bad” to “Very good”, even where only the two ends show one. Screen readers and voice control use those words.
- Write the question above the answers, as the name of the group.
- Draw an answer with its focus ring, and the chosen answer too if it can change before it is sent.
- Draw the row at a phone’s width, with every word that shows still fitting.
Passes
- Faces, the chosen one named Five faces from very bad to very good, each named for what it means, and the chosen one’s word written under the row, so nobody has to read a face. The choice can change until the form is sent. The chosen face takes an amber tint and a #b45309 ring, 5.02:1 on white.
- Pills with a face and a word Five rounded pills, each a small face beside its word, in a row that wraps onto two lines on a phone. A press sends the answer. The words name each pill, and the #6b7280 outline, 4.83:1 on white, marks its edge.
- Two thumbs, each with its word When two answers are enough: a thumb down with “Not for me” and a thumb up with “Good pick”, in outlined buttons. The words carry the meaning, so nobody has to work out which way a thumb points.
Fails
- Emoji aloneWCAG 1.1.1 Five emoji, 😠 to 😄, look friendly and need no icon set, but each button’s name is the emoji’s own, such as “angry face”, which describes the picture, not the answer it stands for.
Verify the fix
4 checks, no mouse.
Test with: Screen reader, Speech input, Keyboard, Touch
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the card or its icon component changes.
Limits
This is a starting pattern, not a guarantee for every application. The CSS is a sketch: check the size of the faces, the space between them and their focus ring against your own design. Such a card may also have a close button drawn as an X with no name; that is the failure covered in icon-only buttons with no accessible name. Whether the thanks that replaces the faces after an answer is heard is covered in status messages that are shown but never announced. Faces told apart only by colour, such as a red, a yellow and a green circle, are a different failure, left to the topic on colour alone, whose guide is in progress. Test the whole card with a screen reader, voice control and a phone.
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.
References
- WCAG Understanding: name-role-value (opens in a new tab)
- WCAG Understanding: non-text-content (opens in a new tab)
- WCAG Understanding: info-and-relationships (opens in a new tab)
- WCAG Technique F68 (opens in a new tab)
- w3.org: accname-1.2 (opens in a new tab)
- ARIA APG: names-and-descriptions (opens in a new tab)
- developer.mozilla.org: fieldset (opens in a new tab)
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.