Updated
—
3 min read
I've been building Featurable, a review & testimonial management SaaS.
So I've been working with reviews and star rating systems a lot. So here's a brief tutorial on a pure CSS (Tailwind CSS only) five-star rating component that changes upon hovering stars in an interactive manner.
Here is a preview what we'll be creating:
Our goal is when a star is hovered, all stars to the left are also highlighted.
To do this, we'll create a CSS class like so:
css
/* Use this if you want by default all stars to be filled yellow */
.star:hover .star,
.star:hover ~ .star .star-icon {
@apply fill-gray-400;
}
/* Use this if you want by default all stars to be filled gray */
.star-container {
direction: rtl; /* we need to reverse direction to fill all stars to the left on hover */
}
.star:hover ~ .star .star-icon,
.star:hover .star-icon {
@apply fill-amber-400;
}
The first CSS selector will have all stars "filled" by default, and when hovering all stars to the left will become filled.
The element1~element2
operator selects every element2
that is preceded by element1
.
The second CSS selector will have all stars "empty" by default, and when hovering all stars to the left will become filled.
Now we can use this class to render the interactive star rating in React:
jsx<div className="star-container flex items-center space-x-1">
{
Array.from(Array(5)).map((_, i) => {
return (
<button key={i} className="star">
<StarIcon
className={"star-icon h-6 w-6 fill-amber-500"}
/>
</button>
);
})
}
</div>
Now we'll have this:
Then we can create the rest of the user feedback popup...
We want to animate this component to "slide down" like a toast notification.
Let's add this animation to our tailwind.config.js
:
javascriptconst config = {
theme: {
extend: {
keyframes: {
"slide-down": {
from: {
transform: "translateY(-100%)",
},
to: {
transform: "translateY(0)",
},
},
},
animation: {
"slide-down": "slide-down 0.3s ease-out",
},
},
},
};
Great! Now we can set up our full component, combining the slide down effect with the interactive star rating.
I'll call this component ReviewPopup.tsx
:
jsx"use client";
const ReviewPopup: React.FC<{
isOpen: boolean,
handleClose: () => void,
}> = ({ isOpen, handleClose }) => {
return (
<div
className={"fixed top-6 left-1/2 -translate-x-1/2"}
>
<div
className={clsx(
"bg-white p-3 shadow border border-solid border-gray rounded-md",
isOpen ? "animate-slide-down" : "opacity-0"
)}
>
<p className="font-bold text-base">
How are you enjoying our product?
</p>
<p className="mt-1 text-xs text-center text-gray-500">
Use the stars below to rate your experience.
</p>
<div className="mt-2 flex items-center justify-center">
{Array.from(Array(5)).map((_, i) => (
// Stars go here
))}
</div>
<div className="mt-2 flex items-center justify-center">
<button
onClick={handleClose}
className="text-sm mx-auto text-gray-500 "
>
Maybe later
</button>
</div>
</div>
</div>
);
};
I want it the popup to be centered horizontally (using translateX
), but our animation also uses translateY
.
Since we don't want animate-slide-down
to overwrite our horizontal translate, I'm using two outer div
containers.
And that's it!
Now we have a fancy pure CSS star rating component that is interactive and highlights stars upon hovering.
Enjoy! You can see the component in action on Featurable dashboard.
Meet the Author
Ryan Chiang
Hello, I'm Ryan. I build things and write about them. This is my blog of my learnings, tutorials, and whatever else I feel like writing about.
See what I'm building →.
Thanks for reading! If you want a heads up when I write a new blog post, you can subscribe below: