Making WordPress.org

Opened 4 months ago

Last modified 7 weeks ago

#8276 new defect (bug)

Profile Page: The plugin's star rating is not being read correctly by screen readers

Reported by: wildworks Owned by:
Priority: normal Milestone:
Component: Profiles Keywords: has-patch
Cc:

Description

In the plugin section of the profile page, ratings are displayed with stars next to the plugin title link. While these ratings are understandable to sighted users, they are not accessible to users who rely on screen readers. When the title link is focused, my NVDA reads it as follows:

Plugin Name       heading
Plugin Name Block Extension  link  visited

Please note that meaningless characters      are being read.

<h3>
  <a href="">Plugin Name</a>
  <div class="wporg-ratings" title="4 out of 5 stars" style="color:var(--wp--preset--color--pomegrade-1, #e26f56);">
    <span class="dashicons dashicons-star-filled"></span>
    <span class="dashicons dashicons-star-filled"></span>
    <span class="dashicons dashicons-star-filled"></span>
    <span class="dashicons dashicons-star-filled"></span>
    <span class="dashicons dashicons-star-empty"></span>
  </div>
</h3>

This HTML should be modified as follows:

<h3>
  <a href="/">Plugin Name</a>
  <div class="wporg-ratings" aria-label="4 out of 5 stars" style="color:var(--wp--preset--color--pomegrade-1, #e26f56);">
    <span class="dashicons dashicons-star-filled" aria-hidden="true"></span>
    <span class="dashicons dashicons-star-filled" aria-hidden="true"></span>
    <span class="dashicons dashicons-star-filled" aria-hidden="true"></span>
    <span class="dashicons dashicons-star-filled" aria-hidden="true"></span>
    <span class="dashicons dashicons-star-empty" aria-hidden="true"></span>
  </div>
</h3>
  • Use the aria-label attribute instead of the title attribute.
  • Add the aria-hidden attribute to the stars.

Attachments (1)

plugin-name-focus.png​ (94.7 KB ) - added by wildworks 4 months ago.

Download all attachments as: .zip

Change History (8)

This ticket was mentioned in ​PR #663 on ​WordPress/wordpress.org by ​@joedolson.


4 months ago
#1

  • Keywords has-patch added

Partially fixes https://meta.trac.wordpress.org/ticket/8276 by adding aria-hidden to the ratings star output.

However, the container already has an aria-label. This is run through wp_kses_post at a few points, but I wouldn't have expected that to remove aria-label, so I'm not sure why it's gone.

In block contexts, this is rendered via wporg/ratings-stars, but I have no idea where that is.

#2 @joedolson
4 months ago

Maybe partially resolved by ​https://github.com/WordPress/wordpress.org/pull/663; but that markup does include aria-label, and it's not clear to me how it relates to the block used for ratings, so I'm not really sure.

​@wildworks commented on ​PR #663:


4 months ago
#3

I think this PR likely aims to fix the review content on the plugin page.

https://github.com/user-attachments/assets/5175b092-8f55-430c-b03a-b6bc87557441

In this section, the rating is outside the heading, so even if the title link is focused, screen readers apparently don't read out the rating.

<div class="header-top">
  <div class="wporg-ratings" aria-label="3 out of 5 stars" data-title-template="%s out of 5 stars" data-rating="3" style="color:#ffb900;">
    <span class="dashicons dashicons-star-filled"></span>
    <span class="dashicons dashicons-star-filled"></span>
    <span class="dashicons dashicons-star-filled"></span>
    <span class="dashicons dashicons-star-empty"></span>
    <span class="dashicons dashicons-star-empty"></span>
  </div>
  <h3 class="review-title">
    <a class="url" href="https://wordpress.org/support/topic/good-direction-but-still-lacks-flexibility/">Good Direction, But Still Lacks Flexibility</a>
  </h3>
</div>

Perhaps the solution is to inject screen reader text into the heading?

<div class="header-top">
  <div class="wporg-ratings" aria-label="3 out of 5 stars" data-title-template="%s out of 5 stars" data-rating="3" style="color:#ffb900;">
    <span class="dashicons dashicons-star-filled"></span>
    <span class="dashicons dashicons-star-filled"></span>
    <span class="dashicons dashicons-star-filled"></span>
    <span class="dashicons dashicons-star-empty"></span>
    <span class="dashicons dashicons-star-empty"></span>
  </div>
  <h3 class="review-title">
    <span class="screen-reader-text">3 out of 5 stars</span>
    <a class="url" href="https://wordpress.org/support/topic/good-direction-but-still-lacks-flexibility/">Good Direction, But Still Lacks Flexibility</a>
  </h3>
</div>

Additionally, I found that to fix the original ticket, we will likely need to make the fix in a different repository. Here it is.

​https://github.com/WordPress/wporg-theme-directory/blob/af069c6691ef7e7d93903015f13833b47ad2980e/source/wp-content/plugins/wporg-ratings/wporg-ratings.php#L221-L243

#4 @wildworks
4 months ago

​https://github.com/WordPress/wporg-theme-directory/pull/163 should fix this issue, but I'm not sure how to test it locally.

​@joedolson commented on ​PR #663:


4 months ago
#5

So many different places these things are kept... :D

​@wildworks commented on ​PR #663:


4 months ago
#6

This PR should resolve the original issue. ​https://github.com/WordPress/wporg-theme-directory/pull/163

#7 @softglaze
7 weeks ago

Reproduction Report

Checking the current state of both markup paths on production, because this ticket covers two and they are not in the same condition.

Environment

  • Browser: Chrome 151.0.7922.174 (64-bit)
  • OS: Windows 11 Pro N, build 26200
  • Display: 200% scaling, devicePixelRatio 2
  • Date: 22 August 2026
  • Method: read the rendered attributes on the live pages. No screen reader was used.

Steps taken

  1. Loaded a profile page with a rated plugin and enumerated every .wporg-ratings element and its .dashicons children.
  2. Loaded a plugin page with reviews and did the same.
  3. For each, recorded aria-label, title, role, whether every star carries aria-hidden="true", whether the container sits inside a heading, and its parent element.
const els = [...document.querySelectorAll('.wporg-ratings')];
console.table(els.map(el => ({
  label: el.getAttribute('aria-label'),
  title: el.getAttribute('title'),
  role:  el.getAttribute('role'),
  starsHidden: [...el.querySelectorAll('.dashicons')].every(s => s.getAttribute('aria-hidden') === 'true'),
  inHeading: !!el.closest('h1,h2,h3,h4'),
  parent: el.parentElement.tagName + '.' + (el.parentElement.className || '').split(' ')[0],
})));

Results

Profile page, container is a <span class="pp-rating">:

aria-label:        "5 out of 5 stars"
title:             null
role:              "img"
stars aria-hidden: all five are "true"
inside a heading:  no

Plugin page, container is a <div class="header-top"> inside .plugin-reviews:

aria-label:        "5 out of 5 stars"
title:             null
role:              null
stars aria-hidden: none of them
inside a heading:  no

Additional Notes

The two paths are in different states on production today, which may account for some of the difficulty in this thread.

The profile path appears fixed. It now carries role="img", an aria-label in place of the title, and aria-hidden="true" on all five stars, none of which were present in the original report. It is also no longer nested inside the <h3>. That is consistent with WordPress/wporg-theme-directory#163 having landed, since @wildworks identified that repository as the source for this path.

The plugin review path is unchanged. PR #663 in WordPress/wordpress.org, which adds the aria-hidden, is still open. All nine checks pass, no reviewer is assigned, and there has been no activity since 3 June.

One observation on the review path beyond the missing aria-hidden. The aria-label there sits on a plain <div> with no role, whereas the profile path carries role="img". A generic element does not reliably take an accessible name, so on the review path the label may not be announced at all, while the five unlabelled star spans still are. That is separate from the screen-reader-text approach @wildworks suggested on #663 and may be worth weighing alongside it.

I read rendered attributes only and did not test with a screen reader, so please treat the announcement behaviour as inference rather than observation.

Note: See TracTickets for help on using tickets.