## Re-Creating The Pop-Out Hover Effect With Modern CSS (Part 2) — Smashing Magazine

13 min TIME read

Share on Twitter, LinkedIn

In a <img> tag, and in the process, we witnessed how CSS masks, CSS variables, trigonometric functions, and @property could be combined to achieve the final result. The same techniques will be combined to create a different shape for the frame in this article. The idea is to apply the concepts in a new context and gain another view of how trigonometric functions can influence the way we mask elements in CSS. In a previous article , Temani Afif PERSON demonstrated modern CSS features used to re-create an older demo had made in the past. That older demo? It was a fancy hover effect where an avatar pops out of a circle on hover. The challenge was to create the effect using only thetag, and in the process, we witnessed how CSS masks, CSS variables, trigonometric functions, andcould be combined to achieve the final result. The same techniques will be combined to create a different shape for the frame in this article. The idea is to apply the concepts in a new context and gain another view of how trigonometric functions can influence the way we mask elements in CSS.

The last time we met, I demonstrated how newer CSS features — particularly trigonometric functions — can be leveraged to accomplish a “pop-out” hover effect. This is what we made together:

We are going to redo the demo but with a different shape. Rather than the rounded floral pattern for the frame that the avatar pops out of, we’ll make a starburst pattern instead.

We are going to rely on the same concepts to create this effect, so you will definitely want to read the first ORDINAL part of this little series before continuing. This is more of an opportunity to practice what we learned in a new context. We will still use CSS masks to “draw” the shape with gradients, trigonometric functions, and custom properties.

Drawing The Shape

Creating a starburst shape is a relatively “easy” thing we can create in CSS. People have accomplished it for years DATE with a combination of pseudo-elements with transforms. An updated approach is to use the clip-path property to draw a polygon that forms the shape.

Or, we could simply head over to my online generator to save us the time of drawing it ourselves.

Even the rotation is possible with clip-path polygon() :

See the Pen [Starburst rotating images](https://codepen.io/t_afif/pen/dywNWJp) by Temani Afif PERSON . See the Pen Starburst rotating images by Temani Afif PERSON .

Unfortunately, none of these methods will help us here. In the last article, we learned that the top half CARDINAL of the image needs to overflow the frame in order for the “pop-out” to work. So, we had to get clever and combine mask for the frame’s bottom half CARDINAL and background for its top half CARDINAL .

That means we are unable to rely solely on a clip-path approach. Our solution will rely on mask as we did before, but this time, the mask configuration will be a little more difficult as we will work with a conic-gradient and the mask-composite property to draw the shape. The mask-composite property is probably one you don’t reach for very often, so it will be fun to put it to work on a practical example.

We can define the shape with three CARDINAL parameters:

The number of spikes (we’ll call this N );

); The radius of the big circle, illustrated in green (we’ll call this R );

); The radius of the small circle illustrated in blue (this will be R – d ).

For the sake of simplicity, I will define d as a percentage of R — R – (R * p) — where p ORG is a number in the range [ 0 1 CARDINAL ] . So, in the end, we are left with three CARDINAL variables, N ORG , R , and p .

If you look closely at the shape, you can see it is a series of triangular shapes that are cut out of a large circular shape. That is exactly how we are going to tackle this challenge. We can create triangles with conic-gradient and then cut them out of the circle with the mask-composite property. Getting a circle is pretty easy using border-radius: 50% PERCENT .

The number of conic gradients is equal to the number of triangles in the pattern. Each gradient can use nearly the same configuration, where the difference between them is how they are rotated. That means the gradient’s code will look something like this:

conic- gradient(from PERSON -1*angle at {position}, #000 MONEY

2*angle MONEY , #0000 0 MONEY );

Thankfully, the position we calculated in the last article is similar enough to the point that we can rely on it here as well:

50% PERCENT + ( 50% PERCENT * (1 – p)) * cos(360deg * i/N) 50% PERCENT + ( 50% PERCENT * (1 – p)) * sin(360deg * i/N)

Again, N is the number of triangles, and p controls the radius of the small circle. R is equal to 50% PERCENT , so the position can also be expressed like this:

R + (R * (1 – p)) * cos(360deg * i/N) R + (R * (1 – p)) * sin(360deg * i/N)

We need to resort to some geometry to determine the value of angle . I will skip the boring math for the sake of brevity, but please feel free to leave a comment if you’re interested in the formula, and I will be glad to give you more details.

angle = atan(sin(180deg/N)/(p – 1 + cos(180deg/N)))

Now, we need to loop through all of that as many times as there are triangles in the pattern. So, we will do what we did in the last article and switch from vanilla CSS to Sass PERSON so we can take advantage of Sass PERSON loops.

The following snippet selects the one CARDINAL element in the HTML, <img> , and loops through the conic gradients for as many triangles we set ( $n: 9 CARDINAL ). The output of that loop is saved as another variable, $m , that is applied to the CSS ORG mask .

$n: 9 CARDINAL ; /* number of spikes */ img { –r: 160px; /* radius */ –p: 0.25 CARDINAL ; /* percent */ –angle: atan(sin(180deg/#{$n}) / ( var(–p ORG ) – 1 + cos(180deg/#{$n}))); width: calc(2 * var(–r)); aspect-ratio: 1 CARDINAL ; border-radius: 50% PERCENT ; $m: (); @for $i from 0 through ( $n – 1 MONEY ) { $m: append($m, conic-gradient( from calc(90deg + ORG 360deg * #{$i/$n MONEY } – var(–angle)) at calc(50% + ( 50% PERCENT * (1 – var(–p))) v cos(360deg * #{$i/$n})) calc(50% + ( 50% PERCENT * (1 – var(–p))) * sin(360deg * #{$i/$n})), # 000 MONEY calc(2*var(–angle)), #0000 0 MONEY ), comma ); } mask: $m; }

Here’s the result of all that work:

Again, if the code looks complex, that’s because it is. It can be complex for a number of reasons, from understanding the conic-gradient() syntax to knowing how the mask property behaves to your comfort level working with trigonometry. Then there’s the additional layer of Sass PERSON and loops that don’t make things any easier. I’ve said before, but I will do so again: you would be doing yourself a favor to thoroughly read the previous article. The gradient configuration is less complicated in that demonstration, even though it follows the exact same structure.

Great, we have a star-like shape! We’re done. Right? Of course not. We want the inverse of what we currently have: the starburst to be filled with color. That’s where the mask-composite comes into play. We can invert the shape by excluding the triangles we created from the circle to get the starburst shape.

mask: linear-gradient(#000 0 0) exclude, $m;

We define a linear gradient that will cover the whole area by default, and we exclude it from the other gradients.

The mask property is a shorthand that combines other mask-* properties, one CARDINAL of which is mask-composite . So, when we declare exclude on mask , it’s really like we’re declaring mask-composite: exclude . Otherwise, our code could have looked like this:

mask: linear-gradient(#000 0 0), $m; mask-composite: exclude, add, add, […], add;

All of the conic gradients need to be added together and then excluded from the first ORDINAL layer. That means we would need to use the add keyword as many times as we have gradients. But since add is the default value for mask-composite , adding exclude in the shorthand property is less work.

In theory, mask-composite: exclude could be enough since there is no intersection between the conic gradients. Later, we will move the gradients and create intersections so having an add composition is mandatory for the conic gradients.

I know mask-composite is a convoluted concept. I highly recommend you read Ana Tudor PERSON ’s crash course on mask composition for a deeper and more thorough explanation of how the mask-composite property works with multiple layers.

With this in place, we have a proper starburst shape to work with:

Rotating The Shape

There is nothing new we need to do in order to rotate the shape on hover. In fact, we can re-use the exact same code we wrote in the previous article and combine it with a trick that slows down or speeds up the rotation on hover.

See the Pen [Rotation the shape](https://codepen.io/t_afif/pen/rNojEBN) by Temani Afif PERSON . See the Pen Rotation the shape by Temani Afif PERSON .

Creating The “ Pop Out” Effect

For the WORK_OF_ART “pop-out” effect, we will follow the same steps we did for the previous article. First ORDINAL , we will update the mask to maintain only the bottom portion of the starburst frame. Remember, the avatar needs to overflow from the top, so we need the bottom half CARDINAL of the starburst to stack in front of the avatar while the top half CARDINAL stacks behind the avatar.

mask: linear-gradient(#000 0 0) top/100% 50% PERCENT no-repeat, linear-gradient(#000 0 0 ORG ) exclude, $m;

I am adding a linear gradient that covers the top half CARDINAL area of the image, exactly like we did in the previous article. In the following demo, you can see how the bottom half CARDINAL of the starburst is still intact while the top half CARDINAL sits behind the avatar as a semi-circle for the time being.

Let’s tackle the top half CARDINAL of the starburst frame. This is where we will rely on the background property to get back the full starburst shape. We can straight-up copy and paste the same gradient configuration from the mask inside the background property as we did in the previous article, but there is a little issue. The gradient configuration includes the mask-composite value that is supported by mask but not the background property.

We are still going to use the same gradient configuration, but we will play with colors to simulate the same result that we would get with mask-composite if we were able to use it in the background :

img { /* etc. */ $m: (); @for $i from 0 through ( $n – 1 MONEY ) { $m: append($m, conic-gradient( from calc(90deg + ORG 360deg * #{$i/$n MONEY } – var(–angle) + var(–a)) at calc(50% + ( 50% PERCENT * ( 1 CARDINAL – var(–p)))*cos(360deg * #{$i/$n} + var(–a))) calc(50% + ( 50% PERCENT * (1 – var(–p))) * sin(360deg * #{$i/$n} + var(–a))), red calc(2 * var(–angle)), #0000 0 MONEY ), comma ); } mask: linear-gradient(#000 0 0) top/100% 50% PERCENT no-repeat, linear-gradient(#000 0 0 ORG ) exclude, $m; background: $m, blue;

I am using a red color value inside the conic gradients. That won’t affect the masking part because the color doesn’t matter in mask . From there, I will add the conic gradients we are using to the background property with a blue coloration behind (the background-color ).

The mask is doing its job on the half CARDINAL bottom of the starburst frame, and we can see the conic gradients in red on the top half CARDINAL of the frame. The avatar is overflowing from the top like we want, but we still need to remove the red color behind it. The solution is to use the same color as the background behind it, whatever that happens to be.

This won’t make our effect fully transparent, but that’s no big deal for this exercise. Let’s consider this a small drawback until I come up with a clever idea on how to use transparency instead. If you have any ideas that might work, please share them with me in the comments, and I’ll check them out.

This is looking pretty good so far, right?

The last step is to write the styles that make the avatar bigger on hover while the starburst frame becomes smaller. To decrease the size of the starburst shape, we will use yet another technique from the previous article: update the position of the conic gradients to make them closer to the center. For this we will introduce an –i variable to the equation of the position.

calc(50% + ( 50%*(1 – var(–p ORG )) – var(–i GPE )) * cos(360deg * #{$i/$n} + var(–a))) calc(50% + ( 50%*(1 – var(–p ORG )) – var(–i GPE )) * sin(360deg * #{$i/$n} + var(–a)))

Initially, –i will be equal to 0 CARDINAL ; on hover, it will become a positive value, which will make the starburst look smaller. Note that this movement will create an intersection between the conic gradients, as we discussed earlier.

Next, we add the scale effect to the image’s :hover state:

img { –f: 1.2 CARDINAL ; /* the scale factor */ /* etc */ } img:hover { scale: var(–f); }

To make sure both starburst shapes have identical sizes (in the non-hover and hover states), –i needs a formula based on the scale factor:

img { –f: 1.2 CARDINAL ; /* the scale factor */ /* etc */ } img:hover { –i: calc(var(–r) * (1 – var(–p)) * (var(–f) – 1) / var(–f)); scale: var(–f); }

And, now, we are finally finished.

Another Example

Let’s try another fancy effect where the avatar is hidden, and on hover, it slides from the bottom to “pop out” while, at the same time, we update the starburst shape.

Cool, right? We are still using only one CARDINAL <img> element in the markup, but this time, I introduced the sliding effect. This will be your homework! I will let you dissect the code to understand what I have changed.

Hint: A CSS Tip where I am using the sliding effect.

Wrapping Up

I hope you enjoy having a little extra practice on the techniques we used in the previous article to create this “pop-out” hover effect. If it feels like I went a little faster this time around, it’s because I did. Rather than spending time explaining the same concepts and techniques, I was more concerned with demonstrating them in a slightly different context. So, we learned a few new ideas for working with gradients in CSS masks and background images!

In spite of the complexity of everything we covered, there is nothing that requires you to understand everything at once or even right away. Take the time to go through this and the previous article step-by-step until you grasp the parts that are toughest for you to grok. In all honesty, you will probably never find yourself in a situation where you need to use all these tricks together. This was a pretty niche exercise. But it provides us with an excuse to individually inspect the techniques that can help you solve some complex problems in CSS without resorting to scripting or extra HTML.

As for the math and the formulas, you don’t need to accurately understand them. The goal is to demonstrate that we can be as accurate as we want when it comes to calculating values and still develop something that is incredibly maintainable with only a few variables. Without trigonometric functions and calc() in CSS ORG , we would be obliged to manually set all of the values once we need to update something, which would be incredibly tedious.

I’ll close this little series with a last demo. Enjoy!

(gg, yk)