|
| 1 | +Read the [Full Article on Medium](https://medium.com/javascript-in-plain-english/how-to-create-an-animated-star-rating-with-just-css-4df50286ea4b?source=friends_link&sk=5184575c98b541f0bd1b920d607b2416) |
| 2 | + |
| 3 | +## Educational Objectives: |
| 4 | +1. Explain the difference between a CSS Selector and a CSS combinator |
| 5 | +2. Compare & Contrast the "Adjacent sibling combinator" and the "General sibling combinator" |
| 6 | +3. Demonstrate how to use flex-direction and hover to imitate a previous sibling combinator |
| 7 | +4. Demonstrate how to use clip-path to create a star |
| 8 | + |
| 9 | +Today I will go over a simple but surprisingly powerful way to use previous child selector CSS "Hack" to build a Star Rating Component. |
| 10 | + |
| 11 | +## So how do we build a Star Rating Component with only CSS? |
| 12 | +Keep scrolling down to find out, in the mean time, I wanted to provide a little background on the context of this article... |
| 13 | +Beware. |
| 14 | + |
| 15 | +``` |
| 16 | +There will be "humor". It may or may not be taken out later... |
| 17 | + |
| 18 | +This article is part of an ongoing educational series that will be turned into an Open Source Book and therefore is considered a "living article" subject to change. If you have question, want to contribute or just wanna chat about the content, leave a comment! |
| 19 | + |
| 20 | +If you have a find a bug, typo, DM me on twitter @HansOnConsult or contribute directly to this book by submitting an issue: |
| 21 | +https://github.com/HansUXdev/OSS-Books |
| 22 | +``` |
| 23 | + |
| 24 | +## CSS Basics in a single GIF |
| 25 | +I like my posts to be beginner friendly, without sacrificing advanced content. So here is a gif by Umar Hansa that goes over some of the most basic rule around css. Try out the [full link here](http://apps.workflower.fi/vocabs/css/en). |
| 26 | + |
| 27 | + |
| 28 | + |
| 29 | +## What we'll be building today |
| 30 | + |
| 31 | + |
| 32 | +## I wrote some code, a description in code comments and made a gif... So I guess, that’s all folks. |
| 33 | + |
| 34 | +**I’m just joking**, what am I the JavaScript Teacher? We can do a better job than that... So let’s dive in: |
| 35 | + |
| 36 | +## How is this a CSS "Hack"? |
| 37 | +No it’s not a "hack" as in, a violation of the computer fraud act, type of hack. That’s **illegal** and we are law abiding citizens here. |
| 38 | + |
| 39 | +It’s a just a creative way to problem solve by flipping the problem entirely upside down, doing the exact opposite of requirements and then presenting our lie to the user flawlessly because it doesn’t matter to them how you did it. |
| 40 | + |
| 41 | +``` |
| 42 | +If that’s not a CSS "hack", it’s certainly a magic star... |
| 43 | +``` |
| 44 | + |
| 45 | +## Star Rating With CSS Only: |
| 46 | +Let’s break this down in terms of the project requirements and CSS selectors & combinators available to see if this is even possible. |
| 47 | + |
| 48 | +``` |
| 49 | +Spoiler, we cheat... and that’s why its a "hack". |
| 50 | +``` |
| 51 | + |
| 52 | +**So what exactly are the requirements of a star rating?** |
| 53 | + |
| 54 | +Generally when you see this coding challenge it goes something like this: |
| 55 | + |
| 56 | +The Rating component consists of 5 stars, with each star represented by an element and held in a parent container. |
| 57 | + |
| 58 | +1. When the star element is clicked or in this case hovered on, the star, should be change to an "active" color and all starts before it should be updated to do the same. |
| 59 | +2. Also, the stars after the desired rating should not have an active color. |
| 60 | + |
| 61 | +**Problem Solving with CSS Selectors, Display types & Combinators** |
| 62 | + |
| 63 | +The CSS isn’t a programming language, it’s a "style sheet language used for describing the presentation of a document written in a markup language like HTML". |
| 64 | + |
| 65 | +``` |
| 66 | +In other words, we cant tell the browser what to do. |
| 67 | +Instead, we can only tell the browser how to present the markup. |
| 68 | +``` |
| 69 | + |
| 70 | +Using pseudo selectors like :hover, :focus, :focus-within, and :checked we can mimic the behavior of clicking and touching an element in the document object model (DOM) or more simply, the rendered HTML. |
| 71 | + |
| 72 | + |
| 73 | +Ok great, that we can accomplish the requirement #2 easy enough. But what about requirement #1. |
| 74 | +Well... this is why we have to "cheat" because there is no way to really target a **previous sibling**, we can only ever target the parent container, the child and their relatives in a cascading manner. |
| 75 | + |
| 76 | +``` |
| 77 | +That’s why it’s called CASCADING Style Sheets, not ascending style sheet. |
| 78 | +``` |
| 79 | + |
| 80 | +That also means we can’t technically accomplish either one of these, but we can do the exact opposite of both and present it, as if we were, in fact accomplishing them... So yeah, it’s definitely cheating and it’s going to be fun and hopefully make you appreciate CSS more. |
| 81 | + |
| 82 | +## Rewriting the requirements to the rules of CSS |
| 83 | +1. When the star element is hovered on, the star, should be change to an "active" color and all starts after it should be updated to do the same. |
| 84 | +2. Also, the stars before the desired rating cannot be styled, so we throw a childish fit and flip the damn thing up side down and reverse the order. |
| 85 | +3. Lie to the user (and reader) and say it’s a Previous-Child Selector. |
| 86 | + |
| 87 | +Since we can, "describe the presentation of a document", |
| 88 | +that includes how we present the order of the elements. |
| 89 | +There several ways we can do this but I’m only going to cover two of them and the second is an anti-pattern and you should avoid using it. It’s only used to manually, demonstrate another way of describing what is happening. |
| 90 | + |
| 91 | +### Option 1: Display Flex & flex-direction |
| 92 | + |
| 93 | +Because I want to show it as a row, we’ll apply flex-direction: row-reverse; to the class of .star-rating to reverse the order of every child element. |
| 94 | +The DOM doesn’t change. We just change how we present it. |
| 95 | +So enough our document, or HTML is written as the following: |
| 96 | +```html |
| 97 | +<div class="star-rating"> |
| 98 | + <div class="star star-1"></div> |
| 99 | + <div class="star star-2"></div> |
| 100 | + <div class="star star-3"></div> |
| 101 | + <div class="star star-4"></div> |
| 102 | + <div class="star star-5"></div> |
| 103 | +</div> |
| 104 | +``` |
| 105 | +The CSS applied in the .star-rating class, will reverse the order of every div. |
| 106 | + |
| 107 | +Then we’ll change the color of the star. |
| 108 | +To do this I’m going to use :hover to change the color on the selected star and we’ll use the CSS General sibling combinator ~ instead of the Adjacent sibling combinator, +. If we used the adjacent combinator, we’d have to write: |
| 109 | + |
| 110 | +```scss |
| 111 | +.star-rating .star:hover, // the selected star |
| 112 | +.star-rating .star:hover+.star, // the sibling after it |
| 113 | +.star-rating .star:hover+.star+.star, // and so on |
| 114 | +.star-rating .star:hover+.star+.star+.star, // and so on |
| 115 | +.star-rating .star:hover+.star+.star+.star+.star, // and so on |
| 116 | +.star-rating .star:hover+.star+.star+.star+star+.star // and so on.. |
| 117 | +{ |
| 118 | + background-color: red; |
| 119 | +} |
| 120 | +``` |
| 121 | + |
| 122 | +That’s to much typing, I have a plate & 8 screws in my hand so it hurt to type. |
| 123 | +Plus it’s just bad CSS, so instead, we’ll simply write the following: |
| 124 | +```css |
| 125 | +.star-rating .star:hover, |
| 126 | +.star-rating .star:hover ~ .star{ |
| 127 | + background-color: red; |
| 128 | +} |
| 129 | +``` |
| 130 | +That’s much easier. |
| 131 | +But it’s important to remember that we are not really styling the previous siblings, we’re styling the siblings after the star we select and just reversing how it is presented to the user. |
| 132 | + |
| 133 | +That’s what makes this a CSS HACK |
| 134 | +### Option 2: Using SCSS, grid, grid-area and grid-template |
| 135 | + |
| 136 | +Since we’re already cheating, YOLO, let’s use SCSS which compiles down to CSS. Please don’t use code like this in production. It’s just another way of describing how to present the stars in reverse order in a more semantic way. |
| 137 | +Because I’m lazy I’ll use SCSS to provide some actually programmatic feature to generate my end user CSS. |
| 138 | +```scss |
| 139 | +// DO USE THIS IN CSS, it wont work because it's SCSS. |
| 140 | +@for $i from 1 through 5 { |
| 141 | + .star-#{$i} { |
| 142 | + grid-area: star-#{$i} ; |
| 143 | + } |
| 144 | +} |
| 145 | +This complies down to the following CSS: |
| 146 | +.star-1 {grid-area: star-1;} |
| 147 | +.star-2 {grid-area: star-2;} |
| 148 | +.star-3 {grid-area: star-3;} |
| 149 | +.star-4 {grid-area: star-4;} |
| 150 | +.star-5 {grid-area: star-5;} |
| 151 | +``` |
| 152 | + |
| 153 | +That we have 5 useless classes that describe the area of each star, we’ll manually rearrange them using grid-template |
| 154 | +```scss |
| 155 | +.star-rating-grid { |
| 156 | + display: grid; |
| 157 | + /* This is what flex-direction: row-reverse is doing */ |
| 158 | + grid-template: |
| 159 | + 'star-5 ... star-4 ... star-3 ... star-2 ... star-1'; |
| 160 | +} |
| 161 | +``` |
| 162 | +Cheating with CSS is fun, doing it properly with react on the other hand is also fun & more practical. |
| 163 | + |
| 164 | +### Advanced Use Cases: SCSS Mixin |
| 165 | +If you are a bit new to CSS than I’d recommend skipping this part until you pick up SCSS. For those more daring, curious or experienced... |
| 166 | + |
| 167 | +I mad a fun little mix in for you all to use. You can also remove the :hover part for what ever you want. |
| 168 | +```scss |
| 169 | +@mixin previousSibling($parent, $child, $sibling) { |
| 170 | + #{$parent} { |
| 171 | + display: flex; |
| 172 | + flex-direction: row-reverse; |
| 173 | + #{$child}:hover ~ #{$sibling }{ |
| 174 | + @content; |
| 175 | + } |
| 176 | + } |
| 177 | +} |
| 178 | +@include previousSibling(".star-rating", ".star", "*") { |
| 179 | + // Do something |
| 180 | + background-color: red; |
| 181 | +} |
| 182 | +``` |
| 183 | + |
| 184 | +### Educations Resources: |
| 185 | +Methods for Star Ratings: https://css-tricks.com/five-methods-for-five-star-ratings/ |
| 186 | +Is there a Previous Sibling Combinator? |
| 187 | +Flex Direction: |
| 188 | +Adjacent sibling combinator |
| 189 | +General sibling **combinator** |
0 commit comments