I was looking at some JavaScript-based snowfall effects on web when the idea of writing this article clicked my mind–doing Christmas snowfall effect in pure CSS!

Well, many others have done it already, and here’s my take on it. I’ve used Sass for keeping things simple and quick. Before proceeding any further, I’d like you to take a look at the live demo of the snowfall.

The Concept of CSS Snowfall

In a snowfall effect, each snowflake should have a unique animation and randomness associated with it. We’ll broadly make use of Sass’s random() function in order to achieve that randomness in our effect.

Pure CSS-based Snowfall Effect
Demo here

And of course, CSS keyframes and animation will be our saviour to achieve this cute-little falling snowflakes upshot. Usage of transformations and positioning is pretty obvious.

The Snowflake Icon

In order to avoid use of graphics or icon fonts, we are using the snowflake glyph, i.e. \2744 or simply . Since we need to float our snowflakes all over the place, we are going to make it break the flow by setting its position property to absolute.

.snow-flake {
  position: absolute; // [1] Break the layout flow
  color: white; // [2] Set the color for the snowflake
  &:after {
    content: "\2744"; // [3] Give it some shape
  }
}
  1. Making the icon independent from the layout flow will help us floating it like a real snowflake.
  2. Snowflakes are white in color, so we gave the same color to our snowflake element.
  3. We made the snowflake look good by adding a glyph to it, as discussed above already.

Animation

Let’s talk about the positioning first. If we give every snowflake element a random negative position on the y-axis, we can easily pull the random dropping of snow.

.snowflake:nth-child(1) {
  top: -20%;
}

.snowflake:nth-child(2) {
  top: -40%;
}

.snowflake:nth-child(3) {
  top: -33%;
}

...

There will be so many flakes, and to cut the extra work we will be looping through them using the Saas for loop.

Since the animation should be unique for each snowflake element of our snowfall, we are going to write unique CSS keyframe animation for each of it. Let’s say we want to have 120 snowflakes in our effect, then we can start like this:

$snowflakes: 120;

@for $i from 1 through $snowflakes {
  .snowflake:nth-of-type(#{$i}) {
    /* Random keyframe animation called */ 
    animation-name: snowflake-#{$i};

    /* Other animation details */
    animation-delay: ...;
    animation-duration: ...;
    animation-iteration-count: infinite;

    /* Random positioning details */
    left: ...;
    top: ...;
  }

  /* Random keyframe animation definition */
  @keyframes snowflake-#{$i} {
    0%   { ... }
    50%  { ... }
    100% { ... }
  }
}

If you go the above way, we can create 120 different animations and positions for our 120 snowflake elements.

After adding random positions, delays, and duration; we can repeat the animation infinitely to keep the snowfall going.

Tip: For anything random between 50 and 100 with Sass, you’d do something like this:

$random_number = random(50)+50

Below is the final code I came up with to make it sane enough to form an effect.

// Total numner of snowflakes
$snowflakes: 250;

// Randomize the animation and positioning for each snowflake
@for $i from 1 through $snowflakes {
  // Position of the snowflake on the y-axis
  $top: (random(50) + 50) * 1%;

  // Position of the snowflake on the x-axis
  $left: random(100) * 1%;

  // Animation delay for the flake
  $delay: random(20) - 1s;

  // Floating span for the flake
  $duration: random(6) + 4s;

  // Size of the flake
  $size: random(24);

   /* Snowflake ##{$i} */
  .snowflake:nth-of-type(#{$i}) {
    animation-name: snowflake-#{$i};
    animation-delay: $delay;

    // Play the animation for anything between 5-10 seconds
    animation-duration: $duration;
    animation-iteration-count: infinite;
    left: $left;
    top: -$top;
    &:after {
      font-size: $size * 1px;
    }
  }
  
  // Animation for snowflake ##{$i}
  @keyframes snowflake-#{$i} {
    0% {
      transform: rotate(0deg);
      left: $left;
      top: -$top;
    }
    25% {
      left: $left + 1%;
    }

    50% {
      left: $left;
    }

    75% {
      left: $left + 2%;
      opacity: 1;
    }
    100% {
      transform: rotate(360deg);
      top: $top + 40%;
      opacity: 0;
    }
  }
}

With a few more tweaks like hiding the scrollbars for the document that shows the snowfall, I ended up with something like:

See the Snowfall Live

And that’s about it! Play around with the code and find out the best version of your own snowfall effect, done using just CSS, as you have seen already. Merry Christmas!