In the past, creating custom components required complex combinations of HTML, CSS, and JavaScript. However, the advancement of CSS in recent years, enables us to build many components using just HTML and CSS -leveraging the logic already built into the browsers. Why reinvent the wheel when we can reuse most of it?
Simple components like checkboxes, radio buttons, and toggle switches can be created with HTML and CSS while relying on the browser for functionality. But we are not limited to simple components. More complex components can also be achieved this way.
In this article, we'll explore how to build a star rating system using a single HTML and just one JavaScript command.
A star rating component is essentially a range of values from which users can select one. Variations may include 5 values (one per star) or 10 values (allowing half-stars), but the idea remains the same - users can select one value, and only one.
HTML provides an input type designed for ranges, which we can use as the base for our component:
<input type="range">
As it stands, this input isn't very useful. We need to define some attributes based on our design specifications:
Based on the specifications above, the HTML will be like this:
<input type="range" min="0.5" max="5" step="0.5" value="2.5">
This component is a range input () that allows users to select a value between 0.5 (min="0.5") and 5 stars (max="5") in increments of 0.5 (step="0.5"). The initial value is set to 2.5 stars (value="2.5").
Setting the minimum value of 0.5 instead of 0 may seem unusual, but there's a practical reason for it. Allowing a 0-star review would create 11 potential values, but the range visually represents 10 values (10 half stars), creating a mismatch between the clickable areas and the stars in the range. This design choice ensures better usability and simplifies the implementation later.
This issue would be partially solved by creating the component using 11 radio buttons. But that would raise new issues in design (how do we select the 0 value using the mouse?), usability (should we mimic the native behavior of a range input or the native behavior of the radio buttons?), and accessibility (how do we manage the focus for the component as a whole?)
These are great questions for another tutorial on how to create a rating component using radio buttons. For this tutorial on how to create the component using a single HTML element, and I opted for a minimum value of 0.5 to avoid these complexities.
We'll add some tweaks later, but this code works as a solid starting point. Without any CSS, it visually resembles a standard range input:
Next, we'll add a few more attributes. While they may not seem significant at the moment, they will be important later:
The final code will look like this (formatted for readability):
<input type="range">
In the next section, we will refine this rule a little bit. We will have to define styles for Chrome/Safari and for Firefox, and that will bring some repetition. We can streamline the process by using custom properties to store values and apply them across both styles.
The track will occupy the entire size of the element, and then we will use a gradient to color the parts we need.
We don't need to worry about the width –it will occupy the whole width of the element–, but he height is a different story. While Chrome and Firefox make the track's height to match the container, Safari does not. So, we will have to explicitly indicate a height of 100%.
Next we want to define the colored area. We will leverage the --val and --size custom properties that we created earlier. We'll set a linear-gradient from left to right that changes colors at the point indicated by the --val property:
<input type="range" min="0.5" max="5" step="0.5" value="2.5">
We'll move this gradient to another custom property in the parent element. This allows us to reuse the value for Chrome/Safari and Firefox –as I mentioned before… and will likely mention later.
With this, we have a rectangle with a darker area representing the selected value. The black area changes as we click or slide over the rectangle, which is the desired functionality, yet it lacks the visuals. We need CSS masks.
I won't deny it, the following part is ugly. I chose to go all-in with CSS, without relying on external images or inline SVGs. You could simplify the code by using any of those options.
The following code is for star-shaped rating component, but we could easily change the shape (e.g., to circles), by changing the mask.
We'll use a set of conic gradients to clip a five-point star using CSS masks. It takes into account the size of the range input so, after the mask is repeated horizontally, we will get five stars:
<input type="range">
As with the linear gradient above, we'll apply this mask in the styles for both Chrome/Safari and Firefox. To avoid code repetition, we'll define it in a custom property within the parent element.
The final code will look like this:
<input type="range" min="0.5" max="5" step="0.5" value="2.5">
Notice how the code for Webkit and Firefox is nearly identical. A feature like mixins in CSS would be incredibly helpful in situations like this –although having a single supported standard would be even better.
We also added some styles (print-color-adjust: exact) to ensure that the component is printed exactly as it appears on the screen. This is useful when working with backgrounds, as they are not typically printed by default.
In the case of this star-rating system, the thumb is not particularly important. The visual effect is achieved using the track itself. So, we'll hide the thumb from view.
We can do it by setting its opacity to zero:
<input type="range" min="0.5" max="5" step="0.5" value="2.5" > <h2> The CSS </h2> <p>Styling range inputs can be tricky–but not excessively complex. Unfortunately, it requires a lot of repetition due to lack of support and standardization, so we must use vendor prefixes and browser-specific pseudo-classes for the different elements of the component:</p>
And, of course, we'll need to apply some specific styles for each browser, as they don't style the component consistently. For example, we'll need to set up heights on Safari or remove a pesky border on Firefox.
From here, the next steps are as follows:
Hiding the thumb is optional and it will depend on the type of component you are building. It makes sense to hide the thumb in this star-rating system. However, in a user-satisfaction component, the thumb may be useful. You can explore different demos at the end of this article.
The first step will be removing the default appearance of the range input. This can be done that by setting the the appearance:none property. All modern browsers support it, but we may want to add the vendor-prefixed versions, so it's compatible with older browsers too.
Since we have five stars, it makes sense to set the width to five times the height. aspect-ratio: 5/1 could handle this, but some browsers still have inconsistent support, so we'll "hard code" the size using a custom property.
Additionally, we want to remove the border. Firefox applies a default border to the ranges, and removing it ensures a more consistent styling across browsers.
.star-rating { --size: 2rem; height: var(--size); width: calc(5 * var(--size)); appearance: none; border: 0; }
You may have noticed that I didn't use CSS nesting in the code snippets above (while I used it in the demos below). This is because nesting is relative new and comes with some limitations: many older browsers don't support it, and also some modern browsers struggle with non-standard pseudo-elements –I reported a bug on WebKit about this behavior on Safari.
They say a picture is worth a thousand words. So, here are some examples of input ranges that can be coded using a single HTML element.
Let's start with the star-rating component described in this article:
Next, a colorful example. It has an atypical shape and it requires styling all the parts of a range input: the range in itself, the track, and the thumb:
Finally, a favorite of mine: an animated, single element user-satisfaction component. Pick any of the different faces and they will move based on selection:
There are many different ways to code this component. I have done it using HTML and CSS only (with one inline JavaScript command), but you could also use images or more JavaScript to prevent that ugly inlining.
The key idea is that, with minimal HTML and CSS changes, you can adjust the specifications, and your star-rating system will behave slightly differently or look completely different.
The component can also be recreated using multiple radio buttons, which would eliminate the need for the JavaScript line and some CSS. It is a valid approach. It would require additional code to ensure accessibility and replicate the default behavior that comes out-of-the-box with an input range. Some may find this approach easier, and it is certainly doable.
That's one of the things I love most about software development: there are many different approaches and options, each with its pros and cons, and all are beautifully achievable.
I hope you enjoyed the article. Keep coding!
The above is the detailed content of Single HTML Element Star Rating Component. For more information, please follow other related articles on the PHP Chinese website!