惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Securelist
C
CERT Recently Published Vulnerability Notes
Forbes - Security
Forbes - Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
L
LINUX DO - 最新话题
The Hacker News
The Hacker News
Google Online Security Blog
Google Online Security Blog
SecWiki News
SecWiki News
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
The Last Watchdog
The Last Watchdog
S
Schneier on Security
T
Troy Hunt's Blog
N
News | PayPal Newsroom
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Schneier on Security
Schneier on Security
P
Privacy & Cybersecurity Law Blog
T
Tor Project blog
T
Threatpost
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
A
Arctic Wolf
S
Secure Thoughts
P
Proofpoint News Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Security Latest
Security Latest
Scott Helme
Scott Helme
Security Archives - TechRepublic
Security Archives - TechRepublic
Latest news
Latest news
PCI Perspectives
PCI Perspectives
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Hacker News - Newest:
Hacker News - Newest: "LLM"
L
LINUX DO - 热门话题
P
Palo Alto Networks Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
G
GRAHAM CLULEY
V2EX - 技术
V2EX - 技术
Google DeepMind News
Google DeepMind News
Project Zero
Project Zero
V
Vulnerabilities – Threatpost
T
Threat Research - Cisco Blogs
Webroot Blog
Webroot Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
N
News and Events Feed by Topic
TaoSecurity Blog
TaoSecurity Blog
大猫的无限游戏
大猫的无限游戏
T
Tenable Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
Visual Studio Blog
H
Hacker News: Front Page
Simon Willison's Weblog
Simon Willison's Weblog
AWS News Blog
AWS News Blog

Ahmad Shadeed

Better fluid sizing with round() Use Cases for Field Sizing The Basics of Anchor Positioning Item Flow CSS Relative Colors Balancing Text In CSS Should masonry be part of CSS grid? CSS display contents CSS Grid Areas CSS Cap Unit An Interactive Guide to CSS Container Queries CSS :has() Interactive Guide CSS Nesting UX in DevTools CSS Nesting Future CSS: State Container Queries Rebuilding a comment component with modern CSS Conditional CSS with :has and :nth-last-child CSS Text balancing with text-wrap:balance CSS Masking Do we need CSS flex-wrap detection? My CSS Wishlist Conditional CSS CSS Style Queries Inside the mind of a frontend developer: Article layout Inside the mind of a frontend developer: Hero section CSS container queries are finally here The CSS behind Figma First Look At The CSS object-view-box Property Learn CSS Subgrid CSS :has Parent Selector Aligning Content In Different Wrappers Flexbox Dynamic Line Separator Hello, CSS Cascade Layers Building UI Components With SVG and CSS A Deep CSS Dive Into Radial And Conic Gradients Defensive CSS Building Real-life Components: Facebook Messenger Conditional Border Radius In CSS CSS Container Query Units Aligning a Button Label Vertically Comparing Design Mockups To Code Result Using HSL Colors In CSS Custom Scrollbars In CSS Let CSS Container Queries For Designers The State of CSS Cross-Browser Development Overflow Issues In CSS Inspect Element As A Way To Increase Your Curiosity Handling Text Over Images in CSS Digging Into CSS Logical Properties Clipping Scrollable Areas On The inline-start Side Understanding Clip Path in CSS The Art of Building Real-life Components Handling Short And Long Content In CSS CSS Scroll Snap A Deep Dive Into CSS Grid minmax() CSS Variables 101 Finding The Root Cause of a CSS Bug Learn CSS centering How to detect browser support for Flexbox Gap CSS Mistakes While On Autopilot Digging Into the Flex Property Understanding CSS Multiple Backgrounds Aligning Logo Images in CSS Grid for layout, Flexbox for components Colors in CSS Thinking About The In-between Design Cases min(), max(), and clamp() CSS Functions Image Techniques On The Web Everything About Auto in CSS Learn Box Alignment Let Learn CSS Positioning Intrinsic Sizing In CSS CSS Grid Template Areas In Action Hiding Elements On The Web Creating a Variable Color Font From Scratch Building a Football Ticket With CSS and SVG Blending Modes in CSS CSS Variables With Inline Styles Implementing Dark Mode For My Website Rebuilding Apple Music Header in HTML & CSS Accessible Checkbox Layout Flickering On Browser Resize Enhancing The Clickable Area Size Custom Underlines with SVG Part 3: The Process of Implementing A UI Design From Scratch Part 2: The Process of Implementing A UI Design From Scratch Building An Old Nav Design CSS Flexbox: 5 Real World Use Cases I Used CSS Inline Flex For The First Time The Process of Implementing A UI Design From Scratch Common CSS Issues For Front-End Projects Handling Long and Unexpected Content in CSS How to Build Web Form Layouts With CSS Grid Grid Layout Ah-ha Moment Enhancing Our Components with CSS :empty Building Resizeable Components with Relative CSS Units CSS Writing Mode The Journey of Learning Front End Web Development on a Daily Basis
Star Rating: An SVG Solution
Ahmad Shadeed · 2021-08-17 · via Ahmad Shadeed

The Layout Maestro

I spent years teaching CSS layout on this blog. I put everything I know into The Layout Maestro course: 70+ lessons and 150+ interactive examples that teach you how to think CSS layouts, not just memorize syntax.

Get the course

For websites and platforms that provide users with content or reviews, it’s important to include a star rating. Recently, I needed to implement a star rating component for a project and the requirements needed were as below:

  • Performant (Doesn’t include images)
  • Resizable
  • Accessible
  • Partial star (e.g: 3.5 or 3.2) dynamically
  • Easy to maintain with CSS

I thought about giving SVG a try, and I wasn’t disappointed. In this article, I will go through the solution and how it works for different scenarios.

Dear reader, if you’re looking for solutions other than SVG, I recommend reading this article on CSS Tricks by Alfred Genkin.

Introduction

Before we dive in, I want to put you in context and show you the requirements that I’m looking for, and all the cases that we need to account for.

Our focus will be on making a star that can be customized by changing its fill or stroke, sizing it, and the ability to show a half star.

Let’s dive in.

The basic markup

First, you need to pick a star shape and save it as an SVG so we can use it in the browser.

<svg
  width="32"
  height="32"
  xmlns="http://www.w3.org/2000/svg"
  viewBox="0 0 32 32"
>
  <path d="..." />
</svg>

In the browser, this will render a black star with a size of 32px for width and height. Sounds good.

Accessibility

Keep in mind that you need to include a written rating with an aria-label attribute so users with screen readers can access this information.

<p aria-label="Rating is 4.5 out of 5">
  <svg
    width="32"
    height="32"
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 32 32"
  >
    <path d="..." />
  </svg>
</p>

How to reuse the SVG

We can either copy the SVG markup above five times, or extract the path data and save it somewhere, and then reusing it without code duplication. Let’s do that!

First, we need to create an SVG with zero width and height, so it won’t reserve a space.

<svg
  width="0"
  height="0"
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink"
>
  <!-- Content -->
</svg>

Within that SVG, we need to include the path data within a <symbol> element. According to MDN:

The symbol element is used to define graphical template objects which can be instantiated by a <use> element.

What’s goes inside the <symbol> is the same content for the icon. Also, it’s important to add an id so we can reference that symbol later.

<svg width="0" height="0" xmlns="http://www.w3.org/2000/svg">
  <symbol
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 32 32"
    id="star"
  >
    <path d="..." />
  </symbol>
</svg>

With that setup, we can now reuse the star symbol with the <use> element. The idea is that you need to use the id as a value of href attribute.

<p class="c-rate">
  <svg class="c-icon" width="32" height="32">
    <use href="#star"></use>
  </svg>
  <svg class="c-icon" width="32" height="32">
    <use href="#star"></use>
  </svg>
  <svg class="c-icon" width="32" height="32">
    <use href="#star"></use>
  </svg>
  <svg class="c-icon" width="32" height="32">
    <use href="#star"></use>
  </svg>
  <svg class="c-icon" width="32" height="32">
    <use href="#star"></use>
  </svg>
</p>

Styling the star

Now that we have a list of stars, let’s get into the CSS part. I defined the yellow and grey colors for the star.

<p class="c-rate">
  <svg class="c-icon active" width="32" height="32">
    <use href="#star"></use>
  </svg>
  <svg class="c-icon active" width="32" height="32">
    <use href="#star"></use>
  </svg>
  <svg class="c-icon active" width="32" height="32">
    <use href="#star"></use>
  </svg>
  <svg class="c-icon active" width="32" height="32">
    <use href="#star"></use>
  </svg>
  <svg class="c-icon" width="32" height="32">
    <use href="#star"></use>
  </svg>
</p>
.c-icon {
  --star-active: #fece3c;
  --star-inactive: #6c6962;
  fill: var(--star-inactive);
}

.c-icon.active {
  fill: var(--star-active);
}

Considering the above markup and styles, the result will be something like the following.

Partial rating

Thankfully, we have two great solutions when it comes to SVG. The first one is using <masks> and the second one is with SVG gradients.

Half star with SVG masks

The goal of using masks is to emulate the effect of erasing a part of the star and painting the other part with a translucent color. To do that, we need the help of SVG masks.

In the figure above, we have a square and a star. What we want is the intersection of them, which will result in half a star.

Here is how to do that with SVG:

  1. Create a reusable SVG template.
  2. Add a <mask> element with a rectangle positioned at x=50%.
  3. Apply the mask to the star shape.
<!-- The reusable SVG template -->
<svg viewBox="0 0 32 32" id="star">
  <defs>
    <mask id="half">
      <rect x="50%" y="0" width="32" height="32" fill="white" />
    </mask>
    <symbol viewBox="0 0 32 32" id="star">
      <path d="..." />
    </symbol>
  </defs>
</svg>

<!-- Example of a usage -->
<svg class="c-icon" width="32" height="32" viewBox="0 0 32 32">
  <use href="#star" mask="url(#half)" fill="green"></use>
</svg>

The result will be half a star, just like in the previous figure.

The question is, how we can show a translucent star when it’s being masked? Well, thanks to SVG, we can include multiple elements in the <mask>.

With another element, the SVG <mask> will look like this:

<mask id="half">
  <rect x="0" y="0" width="32" height="32" fill="white" />
  <rect x="50%" y="0" width="32" height="32" fill="black" />
</mask>

In masks, a white element represents what we want to show, and a black element represents what we want to hide. When combined, we can create a cut-out effect.

Consider the following figure that explains each element of the mask in a visual way.

Notice that the white rectangle is positioned at the 0,0 point, while the black one is positioned at 50%,0. Here is how it look visually:

Do you see what happened? The part that is scribbled represents the final result, which is half a star. Now, you might be thinking about, how we can add another translucent star to make it more clear?

Well, by using a lighter color than pure black, we will get a translucent effect. That means the area that is currently hidden will have a light shade of the star color.

<mask id="half">
  <rect x="0" y="0" width="32" height="32" fill="white" />
  <rect x="50%" y="0" width="32" height="32" fill="grey" />
</mask>

Let’s recap with the full SVG markup.

<svg style="width: 0; height: 0;" viewBox="0 0 32 32">
  <defs>
    <mask id="half">
      <rect x="0" y="0" width="32" height="32" fill="white" />
      <rect x="50%" y="0" width="32" height="32" fill="grey" />
    </mask>

    <symbol viewBox="0 0 32 32" id="star">
      <path d="..." />
    </symbol>
  </defs>
</svg>

<p class="c-rate">
  <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32">
    <use href="#star" mask="url(#half)" fill="green"></use>
  </svg>
  <!-- 4 more stars -->
</p>

With that, we have a star with a partial fill. What’s awesome with that solution is that we don’t need to provide two shades of color. The mask will do the work.

Demo

Half star with SVG gradients

Let’s get into the second solution for a partial star. While searching, I got inspired by this answer on Stackoverflow.

Similar to the mask, we need to define a gradient in the <defs> element.

<svg style="width: 0; height: 0;" viewBox="0 0 32 32">
  <defs>
    <linearGradient id="half" x1="0" x2="100%" y1="0" y2="0">
      <stop offset="50%" stop-color="#f7efc5"></stop>
      <stop offset="50%" stop-color="#fed94b"></stop>
    </linearGradient>
  </defs>
</svg>

Notice that we have two color stops, the first represents the first half, and the second one represents the light shade. In this solution, we need to manually provide two colors.

Here is how we can use the gradient.

<p class="c-rate">
  <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32">
    <use href="#star" fill="url(#half)"></use>
  </svg>
</p>

Demo

Outlined style

We want a variation that can be customized to include outlined stars. From a first glance, it seems simple. Let’s dig into the details.

Outlined style for the SVG mask solution

To add a stroke, all we need is to add stroke to the SVG element. This will work fine for full stars. However, for partial ones, it will be cut-off because of the mask.

To work around that, we need to have another star shape just for the outline. We can do that by duplicating the <use> element and removing the mask from it.

<p class="c-rate">
  <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32">
    <use href="#star" mask="url(#half)" fill="green"></use>
    <use href="#star" fill="none" stroke="grey"></use>
  </svg>
</p>

Notice that we have two <use> elements. The one with the mask, and the one with a stroke only. That’s how an outline style can be done with SVG masks.

Demo

Outlined style for the SVG gradient solution

For the gradient solution, we don’t need to duplicate the icon since there is no mask at all. What we need to do is to add a stroke and it’s done.

<svg style="width: 0; height: 0;" viewBox="0 0 32 32">
  <defs>
    <linearGradient id="half" x1="0" x2="100%" y1="0" y2="0">
      <stop offset="50%" stop-color="#f7efc5"></stop>
      <stop offset="50%" stop-color="#fed94b"></stop>
    </linearGradient>
  </defs>
</svg>

<p class="c-rate">
  <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32">
    <use href="#star" fill="url(#half)" stroke="grey"></use>
  </svg>
</p>

Demo

Sizing

By using CSS variables and making sure that the SVG has the right viewBox attribute, we can easily size them.

.c-icon {
    width: var(--size, 24px)
    height: var(--size, 24px);
}

.c-icon--md {
    --size: 40px;
}

.c-icon--lg {
    --size: 64px;
}

I hope you enjoyed the article. Thanks for reading!