DEV Community

loading...
Cover image for CSS Loading Animation - Ripple Effect in Mosaic (keyframes, flex box)

CSS Loading Animation - Ripple Effect in Mosaic (keyframes, flex box)

crayoncode profile image crayoncode Updated on ・3 min read

Today let's work with CSS animations, delays and flex box in order to create a nice little ripple effect on a colored mosaic.

Read the full article or watch me code this on Youtube:

Result

Markup

The markup of this loader is quite simple as it consists of one wrapping div.mosaic-loader and 16 div.cells. A templating engine like Pug can greatly help here, as it requires us to write only a few lines of code instead of copy'n'pasting a lot of them.

- const cells = 4;
.mosaic-loader
  - for (let i = 0; i < cells; i++)
    - for (let j = 0; j < cells; j++)
      div(class='cell d-' + (i + j))
Enter fullscreen mode Exit fullscreen mode

The expanded HTML looks like this. note that every 4 lines the d-n class starts with a lower value. This comes from the i+j as it sums up the row and the column index and therefore creates the diagonal symmetry. This is crucial for correctly managing the animation delays later.

<div class="mosaic-loader">
  <div class="cell d-0"></div>
  <div class="cell d-1"></div>
  <div class="cell d-2"></div>
  <div class="cell d-3"></div>
  <div class="cell d-1"></div>
  <div class="cell d-2"></div>
  <div class="cell d-3"></div>
  <div class="cell d-4"></div>
  ...8 more with d2-5 and d3-6
</div>
Enter fullscreen mode Exit fullscreen mode

Basic CSS

The basic CSS code on one hand consists of the setup of the wrapper and on the other hand of the setup for each cell. In the beginning some CSS custom properties like --cell-size are defined, as they allow us to easily customize the loader's appearance for each use case. Go ahead, give it a try and play with the values in the embedded code pen.

The wrapper itself is a flex container that allows its child elements to be wrapped. By calculating the --total-size as the width each of the elements in one row need and setting this as the wrapper's width, it ensures the square shape of the entire loader.

.mosaic-loader {
  --cell-size: 64px;
  --cell-spacing: 0px;
  --border-width: 1px;
  --cells: 4;
  --total-size: calc(var(--cells) * (var(--cell-size) + 2 * var(--cell-spacing)));
  --cell-color: white;

  display: flex;
  width: var(--total-size);
  height: var(--total-size);
  flex-wrap: wrap;
Enter fullscreen mode Exit fullscreen mode

For each cell, the flex configuration is setup in a way such that it can neither shrink nor grow (0 0) and always remains at a flex base (width in this case) of --cell-size.

.mosaic-loader {
  ...

  > .cell {
    flex: 0 0 var(--cell-size);
    height: var(--cell-size);
    margin: var(--cell-spacing);
    box-sizing: border-box;


    background-color: transparent;
    border: var(--border-width) solid var(--cell-color);
  }
}
Enter fullscreen mode Exit fullscreen mode

The animation itself

Now, for the animation itself it is setup to run for 1.5s and infinitely often. The keyframes are configured such that from 0% to 30% the background-color fades from transparent to --cell-color and back to transparent until 60%. So the rest of the 40% of the animation time it stays transparent in order to give it some time to overlap with the others.

.mosaic-loader {
  > .cell {
    animation: 1.5s ease ripple infinite;
    animation-delay: 0s;
  }
}

@keyframes ripple {
  0% {
    background-color: transparent;
  }

  30% {
    background-color: var(--cell-color);
  }

  60% {
    background-color: transparent;
  }

  100% {
    background-color: transparent;
  }
}
Enter fullscreen mode Exit fullscreen mode

Managing the delays

As indicated in the beginning, to produce the actual ripple effect it is the delays between the diagonals need to be managed correctly. The number of different delays is calculated by the number of cells in on row multiplied by two minus two. And between each step the delay is increased by 100 milliseconds, such that it produces some temporal overlapping across the neighboring diangonals.

.mosaic-loader {
  > .cell {
    $delays: (2 * 4) - 2;
    @for $i from 1 through $delays {
      &.d-#{$i} {
        animation-delay: $i * 100ms;
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Making it colorful

And finally, to make it cheerfully colorful, just create an array of 16 colors and apply them via the :nth-child selector. The nice thing is, that by using the --cell-color CSS custom property, the color for each cell can be overridden individually.

.mosaic-loader {
  > .cell {
    $colors: (
      ...16 colors of your choice
    );

    @for $i from 1 through length($colors) {
      &:nth-child(#{$i}) {
        --cell-color: #{nth($colors, $i)};
        border-color: var(--cell-color);
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Discussion (0)

pic
Editor guide