Fancy Image Decorations: Masks and Advanced Hover Effects
Temani Afif on
Oct 21, 2022
Let’s put those CSS skills to work! Claim $50 in free hosting credit on Cloudways with code CSSTRICKS.
Welcome to Part 2 of this three-part series! We are still decorating images without any extra elements and pseudo-elements. I hope you already took the time to digest Part 1 because we will continue working with a lot of gradients to create awesome visual effects. We are also going to introduce the CSS mask property for more complex decorations and hover effects.
Fancy Image Decorations series
Masks and Advanced Hover Effects (you are here!)
Outlines and Complex Animations (coming October 28 )
Let’s turn to the first example we’re working on together…
The Postage Stamp
CodePen Embed Fallback
Believe or not, all it takes to make postage stamp CSS effect is two gradients and a filter:
img
As we saw in the previous article , the first step is to make space around the image with padding so we can draw a background gradient and see it there. Then we use a combination of radial-gradient() and linear-gradient() to cut those circles around the image.
Here is a step-by-step illustration that shows how the gradients are configured:
CodePen Embed Fallback
Note the use of the round value in the second step. It’s very important for the trick as it ensures the size of the gradient is adjusted to be perfectly aligned on all the sides, no matter what the image width or height is.
From the specification : The image is repeated as often as will fit within the background positioning area. If it doesn’t fit a whole number of times, it is rescaled so that it does.
The Rounded Frame
Let’s look at another image decoration that uses circles…
CodePen Embed Fallback
This example also uses a radial-gradient(), but this time I have created circles around the image instead of the cut-out effect. Notice that I am also using the round value again. The trickiest part here is the transparent gap between the frame and the image, which is where I reach for the CSS mask property:
img
Masking allows us to show the area of the image — thanks to the linear-gradient() in there — as well as 20px around each side of it — thanks to the conic-gradient(). The 20px is nothing but the variable --s that defines the size of the frame. In other words, we need to hide the gap.
Here’s what I mean:
CodePen Embed Fallback
The linear gradient is the blue part of the background while the conic gradient is the red part of the background. That transparent part between both gradients is what we cut from our element to create the illusion of an inner transparent border.
The Inner Transparent Border
For this one, we are not going to create a frame but rather try something different. We are going to create a transparent inner border inside our image. Probably not that useful in a real-world scenario, but it’s good practice with CSS masks.
CodePen Embed Fallback
Similar to the previous example, we are going to rely on two gradients: a linear-gradient() for the inner part, and a conic-gradient() for the outer part. We’ll leave a space between them to create the transparent border effect.
img
You may have noticed that the conic gradient of this example has a different syntax from the previous example. Both are supposed to create the same shape, so why are they different? It’s because we can reach the same result using different syntaxes. This may look confusing at first, but it’s a good feature. You are not obliged to find the solution to achieve a particular shape. You only need to find one solution that works for you out of the many possibilities out there.
Here are four ways to create the outer square using gradients:
CodePen Embed Fallback
There are even more ways to pull this off, but you get the point.
There is no Best™ approach. Personally, I try to find the one with the smallest and most optimized code. For me, any solution that requires fewer gradients, fewer calculations, and fewer repeated values is the most suitable. Sometimes I choose a more verbose syntax because it gives me more flexibility to change variables and modify things. It comes with experience and practice. The more you play with gradients, the more you know what syntax to use and when.
Let’s get back to our inner transparent border and dig into the hover effect. In case you didn’t notice, there is a cool hover effect that moves that transparent border using a font-size trick. The idea is to define the --d variable with a value of 1em. This variables controls the distance of the border from the edge. We can transform like this:
--_d: calc(var(--d) + var(--s) * 1em)
…giving us the following updated CSS:
img img:hover
The font-size is initially equal to 0 ,so 1em is also equal to 0 and --_d is be equal to --d. On hover, though, the font-size is equal to a value defined by an --o variable that sets the border’s offset. This, in turn, updates the --_d variable, moving the border by the offset. Then I add another variable, --s, to control the sign that decides whether the border moves to the inside or the outside.
CodePen Embed Fallback
The font-size trick is really useful if we want to animate properties that are otherwise unanimatable. Custom properties defined with @property can solve this but support for it is still lacking at the time I’m writing this.
The Frame Reveal
We made the following reveal animation in the first part of this series:
CodePen Embed Fallback
We can take the same idea, but instead of a border with a solid color we will use a gradient like this:
CodePen Embed Fallback
If you compare both codes you will notice the following changes:
I used the same gradient configuration from the first example inside the mask property. I simply moved the gradients from the background property to the mask property.
I added a repeating-linear-gradient() to create the gradient border.
That’s it! I re-used most of the same code we already saw — with super small tweaks — and got another cool image decoration with a hover effect.
/* Solid color border */ img img:hover
/* Gradient color border */ img img:hover
Let’s try another frame animation. This one is a bit tricky as it has a three-step animation :
CodePen Embed Fallback
The first step of the animation is to make the bottom edge bigger. For this, we adjust the background-size of a linear-gradient():
CodePen Embed Fallback
You are probably wondering why I am also adding the top edge. We need it for the third step. I always try to optimize the code I write, so I am using one gradient to cover both the top and bottom sides, but the top one is hidden and revealed later with a mask.
For the second step, we add a second gradient to show the left and right edges. But this time, we do it using background-position:
CodePen Embed Fallback
We can stop here as we already have a nice effect with two gradients but we are here to push the limits so let’s add a touch of mask to achieve the third step.
The trick is to make the top edge hidden until we show the bottom and the sides and then we update the mask-size (or mask-position) to show the top part. As I said previously, we can find a lot of gradient configurations to achieve the same effect.
Here is an illustration of the gradients I will be using:
CodePen Embed Fallback
I am using two conic gradients having a width equal to 200%. Both gradients cover the area leaving only the top part uncovered (that part will be invisible later). On hover, I slide both gradients to cover that part.
Here is a better illustration of one of the gradients to give you a better idea of what’s happening:
CodePen Embed Fallback
Now we put this inside the mask property and we are done! Here is the full code:
img img:hover
I have also introduced some variables to optimize the code, but you should be used to this right now.
What about a four-step animation? Yes, it’s possible!
CodePen Embed Fallback
No explanation for this because it’s your homework! Take all that you have learned in this article to dissect the code and try to articulate what it’s doing. The logic is similar to all the previous examples. The key is to isolate each gradient to understand each step of the animation. I kept the code un-optimized to make things a little easier to read. I do have an optimized version if you are interested, but you can also try to optimize the code yourself and compare it with my version for additional practice.
Wrapping up
That’s it for Part 2 of this three-part series on creative image decorations using only the
element. We now have a good handle on how gradients and masks can be combined to create awesome visual effects, and even animations — without reaching for extra elements or pseudo-elements. Yes, a single
tag is enough!
We have one more article in this series to go. Until then, here is a bonus demo with a cool hover effect where I use mask to assemble a broken image.
CodePen Embed Fallback