Tailwind CSS Interactive Star Rating (On Hover)

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:

TailwindCSS Star Rating

Creating the interactive star rating

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:

TailwindCSS Star Rating

Then we can create the rest of the user feedback popup...

Animating the slide down

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.

Creating the component

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!

Wrapping up

Now we have a fancy pure CSS star rating component that is interactive and highlights stars upon hovering.

TailwindCSS Star Rating

Enjoy! You can see the component in action on Featurable dashboard.

Ryan Chiang

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:

2024

2023

© 2023 Ryan Chiang|ryanschiang.com