Vince Aggrippino

Web Development

I like to talk about HTML, CSS, JavaScript, TypeScript and Accessibility.

Newest blog post:

Accessible Animated Star Rating

Showing the animation of selecting 5 stars

I saw a post on Mastodon where the author, Marius Gundersen, used CSS to create a 0 to 5 star rating widget with an animated gradient and no JavaScript. It's really neat, but the code is hard to read and it's completely inaccessible.

JavaScript is Essential

There seems to be a growing sentiment within the Web Development community that JavaScript should be eliminated at all costs. Well, the cost is often accessibility. And accessibility is essential for all websites. Don't take my word for it, though. As detailed by Adrian Roselli, CSS-only Widgets Are Inaccessible and a key takeaway from Sara Soueidan's experience learning and teaching accessibility is that JavaScript is imperative for creating truly accessible custom interactive components.

Accessible Animated Star Rating

I really liked the animated-star demo. I respect Marius for creating something I wouldn't have even thought of and I respect the anti-JS perspective even though I don't fully support it. But I wanted an accessible version. So, I decided to make my own:

Differences

This is all based on an unmodified fork I made of the original because people often fix or improve things right after sharing an initial version. Besides that, I'm kinda slow and I worried that it would change before I finished writing this 😅

The Original

In the original animated-star demo, the stars were formed by a large blob of encoded SVG code in a CSS background-image and a different blob to define the clip-path of the gradient. It's long, unnecessarily complex, and would be difficult to reproduce without copying and pasting. The stars are hard-coded to be exactly 24px and changing that would require complex changes to both the SVG and clip-path.

Although the stars are interactive components of the page, they aren't in the HTML at all. The HTML for the stars is just radio buttons that aren't even visible on the page. Combining :has() with the squiggle selector (subsequent-sibling combinator) makes it look like more than one star is selected, but there's really only ever one radio button checked. It's really clever, but it means that assistive technology has no way to tell how many stars are selected.

The user can select between 0 and 5 stars, but the widget shows seven stars and the purpose of those extra two isn't immediately clear. The first one is rendered on a row by itself and doesn't do anything when it's clicked. For each selected star one fifth of this star is filled in. The second extra star is never filled in and clicking on it allows the user to select zero stars.

The other five stars would usually represent a rating. Clicking on an empty star selects it and those that precede it. The only way to deselect a star is to click on the star before it.

Assistive technologies such as screen readers work best if every visual component of the page is present in the HTML and has an accessible name. That's not as esoteric as it might seem. Properly written HTML has built-in accessible names. For example, radio buttons should have labels that give them accessible names.

When I test this page with a screen reader (NVDA), it just says "radio button not checked" 5 times and "radio button checked" once. It doesn't say anything about stars. In this context, that's totally fine. Marius was only doing a neat conic-gradient() animation and it does that very well. I'm gonna steal that idea for my version, but I'm gonna focus on accessibility.

My Version

Each star in my demo is a toggle button. They're laid out with flexbox and sized with relative units so they can shrink and grow to match user preferences and the space provided. The star shapes themselves are inline SVG elements simple enough to reproduce by hand even with minimal SVG experience.

I use the same animation technique as Marius, but my gradient is applied to a pseudo-element. It can't be applied directly to the star because SVG doesn't support conic gradients and shapes don't have background properties (they use fill).

Like the original, the user can select between 0 and 5 stars, but there aren't any extra stars. To deselect a star, just click on it. Clicking a selected star that isn't on the end will deselect all the stars after it. The stars each have an offset transition-delay so that they seem to be selected sequentially.

All of the stars are present in the HTML and visually hidden button text provides them with accessible names that identify them as "One Star", "Two Stars", and so on. There's also a live region that allows assistive technology to announce changes in the current rating.

When I test my version with a screen reader, it reads each of the stars, which ones are pressed, and the current rating.

Summary

If you're a hobbyist or enthusiast, either choice is really fine. They both look nice and work well. I think the CSS-only version might earn you a little more geek cred.

If you're a professional, though, there's no choice. Accessibility is legally mandated and that requires JavaScript. An inaccessible website could subject you or your employer to fines or lawsuits. Again, don't take my word for it. Just do a web search for "accessibility lawsuits".

If you do use JavaScript, you may as well take advantage of the additional capabilities; like I did with the offset transition-delay. Just don't go overboard. Poorly implemented JavaScript can really ruin both the performance and accessibility of an otherwise excellent website. The key concepts are progressive enhancement and graceful degradation.