DEV Community

Cover image for Star-rating using Unicode stars
Andrew Bone
Andrew Bone

Posted on

Star-rating using Unicode stars

You might not have known this but over the past week there has been a shadow war taking place right here, on Dev, ok it's not that serious but a few people have been released posts about how to make star rating components with html and css (and JS if you need it), the only rule is the component must be accessible.

The articles that have participated so far are;

If you want to get involved feel free to make a post in a similar vain to this and I'm sure we'll be happy to look at what you've made and give some feedback.

Right, let's get on with my post. The three versions I mentioned so far are all great but they all draw their own stars using CSS, which is fine, but we have stars in unicode already!! We have a filled star (★) and, I think that's all we'll need.

Why is accessibility important?

When you build something for the web, or in fact for any platform, your main aim to solve a problem, that problem maybe just getting information into the hands of the masses or it might be something bigger like managing restaurant bookings. As the aim is to solve problems it's not great to create a whole host of new problems for people that may not be able to access your platform in a 'normal' manor. Because of this the WCAG (Web Content Accessibility Guidelines) were created meaning the web can be utilised by as many people as possible.

The code

My HTML is not too dissimilar to @inhuofficial's this is because, honestly, their method was pretty much how I do star ratings in the real world. One place where we do vary though is CSS because, as I mentioned, everyone used svgs and gradients to make their stars but we'll be taking a different route. A place I use time and time again is w3 and for star ratings this tutorial.

The 'star' html

Each one of my stars is made up of 5 lines of html the input holds the 'state' of which star is selected and the label makes a clickable area which we can style to look like out star. You'll noticed the label and input are connected using the input's id.

There is also a span with the class hide-visually this is a class that allows screen readers to read the contents but hides it from sight so a someone not using a screen reader won't see it.

<input name="rating" value="1" type="radio" id="rating1">
<label for="rating1">
  <span class="hide-visually">1 Star</span>
  <span aria-hidden="true" class="star"></span>
</label>
Enter fullscreen mode Exit fullscreen mode

The 'star' css

The hide-visually class is used to make something invisible to sighted users but still have it be read by screen readers.

The actual stars are written directly into the HTML like text but we can use CSS to change them, if we were wanting to change the symbol when a star is 'active' we could use a ::before pseudo class.

.star-rating>label {
  -webkit-text-stroke: 2px black;
}

.star-rating input:checked~label>span.star,
.star-rating>label:hover~input~label>span.star{
  color: #fff;
}

.star-rating span.star,
.star-rating:hover>input+label>span.star,
.star-rating>input:checked+label>span.star,
.star-rating>input~label:hover>span.star{
  color: gold;
}

.star-rating>input~label:hover>span.star{
  color: orange;
}
Enter fullscreen mode Exit fullscreen mode

Putting it together

Each set of stars is wrapped in a fieldset this is done to make listening for changes easier, you can listen for changes once per fieldset rather than on each input.

I've also included a 0 star input and label but they're hidden. If we wanted we could show the 0 star and give it a clear icon to imply pressing it clears all stars.

There are also some other styles to make all the stars layout nicely but I don't think these styles are that interesting and you can change them if you wanted to make your own version.

The result

The joy of using unicode is we can just swap out the star with any supported shape.

Signing off

And that's it, any questions or feedback feel free to leave it in the comments below and be sure to check out the other posts in the 'war'.

For anyone who's a beginner and just looking at how to do things is it helpful to see several different people solve the same problem in their own ways each explaining why they did it that way?

Thanks for reading 🦄❤️🦄🧙‍♂️🧙‍♂️🧠🦄🧙‍♂️🧠🦄

Latest comments (22)

Collapse
 
cormacmaherie profile image
Cormac Maher

I wanted to turn a star rating into a Likert Scale, so I created a smiley ttf and used that, one of the big advantages was I could use css to style it:

cormacmaher.com/Likert-Scale-icon-...

Collapse
 
devanks profile image
Devan Sambasivan

I like the unicode usage. But making it support half stars like how @madsstoumann does in their implementation would be hard right?
I can think of having maybe two stars in one place and hiding one of them vertically based on fraction.

Collapse
 
link2twenty profile image
Andrew Bone

Yeah, half star will be difficult. I think you're right that you'd hide half of a star but doing this will double the amount of HTML required too.

Collapse
 
cormacmaherie profile image
Cormac Maher

Use ten stars, then use clip-path and nth-child(odd) for to position them, that should work fairly well, but yes, it would be extra HTML

Collapse
 
hasnaindev profile image
Muhammad Hasnain

Honestly, I'm not even here for the stars but just for the star wars between you four guys.

Collapse
 
allthecode profile image
Simon Barker

This is great, I used unicode stars for a exactly this a few years ago on my shopify e-commerce 😀

Collapse
 
link2twenty profile image
Andrew Bone

Thank you, it's good to know people are using techniques like this out in the real web ☺️

Collapse
 
link2twenty profile image
Andrew Bone

For people interested I converted this into a react component too

It's basically the exact same code but with some React nicety added for instance you can say how many stars you'd like and React will handle all the HTML for you

<StarRating max={15} defaultValue={5} />
Enter fullscreen mode Exit fullscreen mode
Collapse
 
mellen profile image
Matt Ellen • Edited

Why put the star character in the CSS rather than the HTML?

Collapse
 
link2twenty profile image
Andrew Bone

Good point, it was in the before initially because I was changing between different characters (which you can only do in a before) but I edited the code and that was left in as legacy.

Collapse
 
mellen profile image
Matt Ellen

Ah, I see. Thanks.

Thread Thread
 
link2twenty profile image
Andrew Bone

I've updated the article to not include the before stuff now, thanks for pointing it out.

Collapse
 
grahamthedev profile image
GrahamTheDev • Edited

This is great, just swap the opacity: 0.01 trick for a proper screen reader only class and maybe steal my text-shadow trick to give the stars a darker border (so contrast is high enough for a control at 3:1) and this gets an A++!

I actually think yours behaves better than mine too!

Have all of the ❤🦄 and bookmark love I can give!

Collapse
 
link2twenty profile image
Andrew Bone

I've made a slight edit, I use text-stroke though rather than the shadow trick.

Collapse
 
grahamthedev profile image
GrahamTheDev • Edited

Oh I just noticed something else (minor tweak) your stars need to have aria-hidden=true instead of role=presentation as they are still screen reader accessible. Presentation basically says “this has no role” (think of it like turning it into a div or span if it was button for example) but doesn’t remove it from the accessibility tree. Not sure how I missed that!

Thread Thread
 
link2twenty profile image
Andrew Bone

Fixed

Collapse
 
link2twenty profile image
Andrew Bone
Collapse
 
madsstoumann profile image
Mads Stoumann • Edited

Chrome does not support range-progress (like Firefox), but a hack using box-shadow can be used. Not fully tried and tested, but here is my “single input non-JS”-version:

Collapse
 
link2twenty profile image
Andrew Bone

That's actually awesome!!! The web standards people really should standardise those props.

Collapse
 
afif profile image
Temani Afif

actually the war started with this post: dev.to/lapstjup/implementing-a-sta...

Collapse
 
grahamthedev profile image
GrahamTheDev

Yeah he posted that shortly after, and now Andrew has joined the fight.

People on dev.to are going to be so confused if they only come here every once in a while!

Collapse
 
link2twenty profile image
Andrew Bone

@inhuofficial 's post is 2 hours older than that one 😉
But yeah there was mention of that post