#9 Web
23 mins   /

How to create a shimmer effect using HTML and CSS?

Learn how to create the "shimmer" effect using HTML and CSS, commonly used as a UX pattern to represent loading.

What is the shimmer effect?

Let's start by witnessing the shimmer effect in real.

Here's a quick glimpse (press the "Start" button):

As complicated as it might seem, creating this effect is superbly simple. At the core lies two things: a linear gradient background and a CSS animation to keep the background moving constantly. And that's it!

So shall we get to the implementation? Let's go.

Dissecting the implementation

We'll start by creating a small rectangle <div> where the shimmer will shine:

HTML
<div></div>
CSS
div {
   height: 150px;
   width: 300px;
   background-color: lightgray
}

Here's the output of this code:

I'll be removing this light gray background color shortly below. For now, it's only to showcase the size of the <div>.

Let's now apply a gradient to the background of this <div>. If you know of it, CSS allows us to create two kinds of gradients: linear and radial.

Obviously, a radial gradient can't be used to replicate the shimmer effect, and it won't even look nice (what do you say?). This leaves us with the former choice — a linear gradient. And that's exactly how the shimmer effect is implemented.

But where to begin in creating this linear gradient?

Well, again as always, I strongly encourage you (yes you) to think about it and bring your amazing brain power into it. You have a lot more potential in you than you can think of.

It's important for you to develop rock-solid analytical skills as a developer. Try to keep your hands off from searching about everything on StackOverflow or ChatGPT without giving it a thought of your own. Great programmers think great!

Assuming that you've done your thinking, let's now get straight to the implementation.

Notice the moving white line in the shimmer effect above — it's actually the "shimmer" (shine). On the left and right ends of this line (horizontally), the background color seems to be the same tints of gray as the background.

Hmm... This means that the linear gradient should begin at some tint of gray and end at that same tint of gray, while having a white color burst in between (or if not white, still something considerably lighter than gray in order to really shine through).

See, we're getting somewhere. Let's go to the CSS to test this out.

Replicating the shimmer

Restating what was stated a while ago, we'll start with a gray color, followed by a much lighter tint, followed by the same gray color. Let's use #eee for gray and #fafafa as the lighter, shimmer tint.

Here's the setup of the linear gradient: (Notice that I've removed the background-color style, now that background is there.)

CSS
div {
   height: 150px;
   width: 300px;
   background: linear-gradient(to right, #eee, #fafafa, #eee);
}

A good start. The white shimmer is way too much scattered so let's reduce its spread.

Do you know how to do that in linear-gradient()? You ought to set a percentage value after the color value to specify the position at which the color should begin.

Since we want our shimmer right in the center, we'll start off with specifying the position for the #fafafa color (belonging to the shimmer):

CSS
div {
   height: 150px;
   width: 300px;
   background: linear-gradient(to right, #eee, #fafafa 50%, #eee);
}

50% means that the color #fafafa would be concentrated right at the center of the background. So far, so good. But just this won't produce any change because linear-gradient() is already configured to evenly position all the colors — the second color in the sequence of three colors is already situated at the 50% mark.

The real result comes by specifying the positions of the first and last gray color stops. The positions of both these end points should be equi-distant from the center point.

That means that if, for example, the first gray color is at 20%, then the last gray color should be at 80% (both colors being at a distance of 30% from the center, 50%). Similarly, if the first gray color is at 45%, the ending one should be at 55% (with the same distance of 5% from the center).

In my experiments, I found 40% and 60% as a good strike of balance. This is implemented as follows:

CSS
div {
   height: 150px;
   width: 300px;
   background: linear-gradient(to right, #eee 40%, #fafafa 50%, #eee 60%);
}

See the change? Having ticked the first box of nicely reproducing the shimmer, let's now angle it.

Angling the shimmer

In most shimmer effect implementations, the shimmer is placed at an angle rather than being pitch straight. Once again, angling the shimmer isn't difficult at all — it's just one simple step.

That is, we only have to change the to right keyword to -45deg to specify the angle at which the gradient will proceed. But why -45deg and not 45deg? you ask.

Well, let's use 45deg and see why it won't work and then get back to -45deg.

CSS
div {
   height: 150px;
   width: 300px;
   background: linear-gradient(45deg, #eee 40%, #fafafa 50%, #eee 60%);
}

As can be seen here, the shimmer runs approximately from the top-left corner to the bottom-right, looking like a backslash (\). However, shimmers are typically positioned like a forward slash (/).

Why is that so? When the shimmer moves from left to right, the one that resembles a backslash (\) just doesn't look very nice. The one that resembles a forward slash looks much better.

Speaking of which, to produce a forward slash formation of the shimmer, we use the -45deg angle:

CSS
div {
   height: 150px;
   width: 300px;
   background: linear-gradient(-45deg, #eee 40%, #fafafa 50%, #eee 60%);
}

Yup, much better!

The next step, now that we've completed implemented the shimmer, is to add an animation to it. But first we need to prepare certain things.

Preparing the animation

Let me start by asking a naive question to you: How do you think will the shimmer above be animated? Like, what will happen to it?

Well, it will move. How? Background gradients in CSS are treated as background images and thus all the properties that apply to background images, more or less, apply to background gradients as well.

In our case, since we want to move the background gradient horizontally, we'll be needing... you guessed it... the background-position-x property.

But there's an additional thing to note...

When background-position-x is given a percentage value, for e.g. 100%, it's resolved relative to the result of the following expression:

element width - background image width

The issue is that when the background size is 100%, which is the default, this expression resolves to 0 as the width of the element equals the width of the background image. This means that any percentage value given to background-position-x would resolve to 0 as well (because it's computed relative to 0).

In turn, this means that with the default (100%) background size, animating the value of background-position-x would have absolutely no effect.

The solution? Change the background size. Simple.

Let's try doubling or tripling the size of the image. Let's try tripling first, i.e. a background size of 300%:

CSS
div {
   height: 150px;
   width: 300px;
   background: linear-gradient(-45deg, #eee 40%, #fafafa 50%, #eee 60%);
   background-size: 300%;
}

Oops! That size threw the shimmer out of view. I'll bring it into view in a while but for now, let's see whether changing background-position-x is really working or not.

Below we try a position of -100%:

CSS
div {
   height: 150px;
   width: 300px;
   background: linear-gradient(-45deg, #eee 40%, #fafafa 50%, #eee 60%);
   background-size: 300%;
   background-position-x: -100%
}

Yeah, it's working! And I kinda like the 300% size. Anwyays, let's try another value for background-position-x:

CSS
div {
   height: 150px;
   width: 300px;
   background: linear-gradient(-45deg, #eee 40%, #fafafa 50%, #eee 60%);
   background-size: 300%;
   background-position-x: 40%
}

Fantastic!

Now, let's think about the initial position of the shimmer. The best configuration is to position it at the extreme left, out of view, ultimately brought in using background-position-x.

After some trial and error, the initial value background-position-x: 100% is sufficient for positioning:

Notice an important, slightly counter-intuitive, pattern here. As we increase the value of background-position-x, the shimmer moves leftwards. Similarly, as we increas the value of background-position-x, the shimmer moves rightwards. We'll be leveraging this pattern in the next section where we finally consider our animation.
CSS
div {
   height: 150px;
   width: 300px;
   background: linear-gradient(-45deg, #eee 40%, #fafafa 50%, #eee 60%);
   background-size: 300%;
   background-position-x: 100%; /* Initial position of the shimmer */
}

You're not seeing the shimmer here because it's out of view past the left edge of the box.

Adding the animation

Finally, it's time to add the animation. We'll use a CSS animation for this.

Let's decide on the different aspects of the animation:

  • Since a shimmer animation usually runs indefinitely while something is loading (which is what this effect is mostly used for), we'll use an infinite animation count.
  • The duration should be the time to complete one shimmer movement from the left to the right. Let's go with 1s (1 second).
  • The timing function will be linear to make the movement happen at a constant pace, without any kind of acceleration (when using ease, or ease-in, or other timing functions).

Below we set up this animation, called shimmer:

CSS
div {
   height: 150px;
   width: 300px;
   background: linear-gradient(-45deg, #eee 40%, #fafafa 50%, #eee 60%);
   background-size: 300%;
   background-position-x: 100%
   animation: shimmer 1s infinite linear;
}

Let's now define the keyframes for shimmer.

Recall from the note above that as we reduce the value of background-position-x, the shimmer moves rightwards.

Because we want this very nature of movement, i.e. from left to right, in our effect, we'll be applying a value to background-position-x in the to keyframe that's less than 100% (our initial value of background-position-x).

Precisely, we'll be using 0%. This is because at that point the shimmer sits past the right edge of the containing box, completely out of view.

CSS
@keyframes shimmer {
   to {
      background-position-x: 0%
   }
}

Let's see the output produced:

Oh! That looks stunning!

If the example above isn't running, reload the browser.
To save computing resources, we've refrained from using an infinite animation count for the example; we've instead used a count of 10. So after 10 cycles, the animation would stop. In a real-world application, you'll however want infinite cycles because the underlying assets will eventually load and ultimately replace these shimmer effect loading indicators.

In the end

As you saw, producing the scintillating shimmer effect using just HTML and CSS is very easy. We just need to have our basics right, and that's all.

You could tweak the effect in whatever way you like, but obviously keeping the main idea of the shimmer intact. For example, you can change the end point colors of the gradient, or maybe make the shimmer more concentrated, or maybe even change the speed of its movement.

There's a lot to play around with in this effect. And nothing is better than experimenting with some HTML and CSS in some free time. What do you say?

Bilal Adnan

Hi there! 👋 I'm the founder of Codeguage — basically the guy who's trying to make life easy for self-taught devs. You can follow me on LinkedIn or Medium to stay up-to-date with my conversations.