Fancy Image Decorations: Single Element Magic
As the title says, we are going to decorate images! There’s a bunch of other articles out there that talk about this, but what we’re covering here is quite a bit different because it’s more of a challenge. The challenge? Decorate an image using only the
<img> tag and nothing more.
That right, no extra markup, no divs, and no pseudo-elements. Just the one tag.
Sounds difficult, right? But by the end of this article — and the others that make up this little series — I’ll prove that CSS is powerful enough to give us great and stunning results despite the limitation of working with a single element.
Before digging into the code let’s enumerate the possibilities for styling an
<img> without any extra elements or pseudo-elements. We can use
outline, and, of course,
background. It may look strange to add a background to an image because we cannot see it as it will be behind the image — but the trick is to create space around the image using
border and then draw our background inside that space.
Let’s get back to our first example:
We are defining
padding and a transparent
border using the variable
--s to create a space around our image equal to three times that variable.
Why are we using both
border instead of one or the other? We can get by using only one of them but I need this combination for my gradient because, by default, the initial value of
background-origin is equal to
Here is a step-by-step illustration to understand the logic:
Initially, we don’t have any borders on the image, so our gradient will create two segments with
1px of thickness. (I am using
3px in this specific demo so it’s easier to see.) We add a colored border and the gradient still gives us the same result inside the padding area (due to
background-origin) but it repeats behind the border. If we make the color of the border transparent, we can use the repetition and we get the frame we want.
outline in the demo has a negative offset. That creates a square shape at the top of the gradient. That’s it! We added a nice decoration to our image using one gradient and an
outline. We could have used more gradients! But I always try to keep my code as simple as possible and I found that adding an
outline is better that way.
Here is a gradient-only solution where I am using only
padding to define the space. Still the same result but with a more complex syntax.
Let’s try another idea:
For this one, I took the previous example removed the
outline, and applied a
clip-path to cut the gradient on each side. The
clip-path value is a bit verbose and confusing but here is an illustration to better see its points:
I think you get the main idea. We are going to combine backgrounds, outlines, clipping, and some masking to achieve different kinds of decorations. We are also going to consider some cool hover animations as an added bonus! What we’ve looked at so far is merely a small overview of what’s coming!
This one takes four gradients. Each gradient covers one corner and, on hover, we expand them to create a full frame around the image. Let’s dissect the code for one of the gradients:
We are going to draw a gradient with a size equal to
50px 50px and place it at the top-left corner (
0 0). For the gradient’s configuration, here’s a step-by-step illustration showing how I reached that result.
We tend to think that gradients are only good for transitioning between two colors. But in reality, we can do so much more with them! They are especially useful when it comes to creating different shapes. The trick is to make sure we have hard stops between colors — like in the example above — rather than smooth transitions:
This is basically saying: “fill the gradient with a transparent color until
25% of the area, then fill the remaining area with
You might be scratching your head over the
0 value. It’s a little hack to simplify the syntax. In reality, we should use this to make a hard stop between colors:
That is more logical! The transparent color ends at
darkblue starts exactly where the transparency ends, making a hard stop. If we replace the second one with
0, the browser will do the job for us, so it is a slightly more efficient way to go about it.
0 is always smaller than any other value, so the browser will always convert it to the largest value that comes before it in the declaration. In our case, that number is
Now, we apply the same logic to all the corners and we end with the following code:
I have introduced CSS variables to avoid some redundancy as all the gradients use the same color configuration.
For the hover effect, all I’m doing is increasing the size of the gradients to create the full frame:
51% instead of
50% — that creates a small overlap and avoids possible gaps.
Let’s try another idea using the same technique:
This time we are using only two gradients, but with a more complex animation. First, we update the position of each gradient, then increase their sizes to create the full frame. I also introduced more variables for better control over the color, size, thickness, and even the gap between the image and the frame.
Why do the
--_p variables have an underscore in their name? The underscores are part of a naming convention I use to consider “internal” variables used to optimize the code. They are nothing special but I want to make a difference between the variables we adjust to control the frame (like
--c, etc.) and the ones I use to make the code shorter.
Here is an illustration to better understand the different values:
Let’s try another type of animation where we reveal the full frame on hover:
But this time, instead of covering all the element, I cover only a small portion by defining a
height to get something like this:
This is the top border of our frame. We repeat the same process on each side of the image and we have our hover effect:
As you can see, I am applying the same gradient four times and each one has a different position to cover only one side at a time.
Another one? Let’s go!
This one looks a bit tricky and it indeed does require some imagination to understand how two conic gradients are pulling off this kind of magic. Here is a demo to illustrate one of the gradients:
The pseudo-element simulates the gradient. It’s initially out of sight and, on hover, we first change its position to get the top edge of the frame. Then we increase the height to get the right edge. The gradient shape is similar to the ones we used in the last section: two segments to cover two sides.
But why did I make the gradient’s width
200%? You’d think
100% would be enough, right?
Now that we have explained the logic for one gradient, the second one is easy because it’s doing exactly the same thing, but covering the left and bottom edges instead. All we have to do is to swap a few values and we are done:
As you can see, both gradients are almost identical. I am simply swapping the values of the size and position.
This time we are not going to draw a frame around our image, but rather adjust the look of an existing one.
You are probably asking how the heck I am able to transform a straight line into an angled line. No, the magic is different than that. That’s just the illusion we get after combining simple animations for four gradients.
Let’s see how the animation for the top gradient is made:
I am simply updating the position of a repeating gradient. Nothing fancy yet! Let’s do the same for the right side:
Are you starting to see the trick? Both gradients intersect at the corner to create the illusion where the straight line is changed to an angled one. Let’s remove the outline and hide the overflow to better see it:
Now, we add two more gradients to cover the remaining edges and we are done:
If we take this code and slightly adjust it, we can get another cool animation:
Can you figure out the logic in this example? That’s your homework! The code may look scary but it uses the same logic as the previous examples we looked at. Try to isolate each gradient and imagine how it animates.
That’s a lot of gradients in one article!
It sure is and I warned you! But if the challenge is to decorate an image without an extra elements and pseudo-elements, we are left with only a few possibilities and gradients are the most powerful option.
Don’t worry if you are a bit lost in some of the explanations. I always recommend some of my old articles where I go into greater detail with some of the concepts we recycled for this challenge.
I am gonna leave with one last demo to hold you over until the next article in this series. This time, I am using
radial-gradient() to create another funny hover effect. I’ll let you dissect the code to grok how it works. Ask me questions in the comments if you get stuck!
If you need help creating a digital marketing strategy for your business, don’t hesitate to contact one of Digidude’s consultants.