CSS Snow animation
Updated for Christmas 2019 over on Codepen
A pure CSS solution for the festive season as used for Tesco Christmas 2015, the Tesco homepage, and store-locator too. This piece is a furtherance to Estelle Weyl's snow demo.
One issue outstanding in IE10, where only some flakes spin and drift, which is odd to say the least.
HTML
This is what a single row looks like:
<div id=flakes class=flakes>
<!-- 1 row at 768px -->
<i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i>
<!-- Common device widths -->
<i class=snow-1024px></i><i class=snow-1024px></i><i class=snow-1024px></i>
<i class=snow-1280px></i><i class=snow-1280px></i>
<i class=snow-1366px></i><i class=snow-1366px></i>
<i class=snow-1440px></i>
<i class=snow-1600px></i><i class=snow-1600px></i>
<i class=snow-1800px></i><i class=snow-1800px></i>
<i class=snow-1920px></i><i class=snow-1920px></i>
<div></div>
</div>
To increase the number of flakes simply duplicate ALL <i>
tags and increase .flakes
offset height, and falling distance.
Disable flex
while changing the quantities of snowflakes. Otherwise flex
will resize the <i>
tags to fit the available width.
CSS
For the sake of brevity I've excluded browser specific prefixes here. Though the stand-alone demo contains everything you need.
The snowflakes container
flex
is used to space the <i>
tags evenly across the horizontal plane. Playing nicely at in-between device widths. Be sure to temporarily disable flex
if you are adjusting the number of flakes. 20vh is allocated per row of flakes. The falling animation would require adjustment too.
Hardware acceleration is encouraged via 3D transforms.
.flakes {
position: fixed;
top: 0;
left: 0;
width: 100%;
text-align: center;
z-index: -1;
transform: translate(0, -20vh);
transform: translate3d(0, -20vh, 0);
display:flex;
justify-content: space-between;
flex-wrap: wrap;
}
Cover the flakes container with the background color. That's in case low screen height allows the start position of the flakes to show through.
.flakes div {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: #fff;
}
Generic styling of the snowflakes, setting defaults for the animation and transforms. Again encouraging hardware acceleration where supported.
.flakes i {
display: inline-block;
transform: translate3d(0, 0, 0);
backface-visibility: hidden;
perspective: 1000;
animation: falling;
animation-iteration-count: infinite;
background-size: contain;
background-repeat: no-repeat;
backface-visibility: hidden;
/* Updated to SVG */
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 96 96' fill='%23fff' fill-rule='evenodd'%3E%3Cpath d='M38.2 65l1-9.7 6.8-3.8v7.8L38.2 65zM58 65l-8-5.8v-7.8l6.8 4 1 9.6zm9.8-17l-9 3.8-6.6-3.8 7-4 8.6 4zm-39.4 0l8.7-4 7 4-6.5 3.8-8.8-4zM58 31l-1 9.6-7 4v-8l8-5.7zm-19.8 0l7.8 5.5v8l-6.7-3.8-1-9.8zm12-17L60.5 3.5l2.8 3-13.3 13v12L63 22l-1.7 16 10.2-5.7 5-18.3 4 1-4 14.4 12.2-7 2 3.6-12 7L93 37l-1.3 3.8-18.2-5-10.2 5.8L78 48l-14.7 6.4 10 5.6 18-5 1.2 4-14.3 3.8L90.6 70l-2 3.5L76 66.3l4 14.4-4 1-5-18.2-9.8-5.6L63 73.3 50 64v11.6L63.3 89l-2.8 3L50 81.5V96h-4V81.5L35.4 92l-2.8-3L46 75.8V64.4l-12.8 9.3 1.7-16-10.5 6-5 18-4-1 4-14.3-12.5 7L5 70l13-7.2-14.5-4 1-3.8L23 60l10-5.7L18.3 48l14.4-6.4-10.2-6-18.3 5-1-3.8 14.3-4-12.2-7 2-3.4 12.2 7-4-14.4 4-1 5 18.2 10.4 6-2-16 13 9.2V19.8L32.4 6.3l3-2.8L46 14V0h4v14z'/%3E%3C/svg%3E");
}
There are four flake sizes, each requires an individual origin set.
.flakes i:nth-child(4n+0) {
width: 130px;
height: 130px;
transform-origin: -5% -5%;
}
.flakes i:nth-child(4n+1) {
width: 97px;
height: 97px;
transform-origin: 15% 0;
}
.flakes i:nth-child(4n+2) {
width: 65px;
height: 65px;
transform-origin: 0 -30%;
}
.flakes i:nth-child(4n+3) {
width: 32px;
height: 32px;
transform-origin: -100% -100%;
}
Keep the first snowflake close to the left edge:
.flakes i:first-child {
transform-origin: 60% 40%;
}
Delay the start times of the snowflake animations:
.flakes i:nth-of-type(5n+0) {
animation-delay: 0s;
}
.flakes i:nth-of-type(5n+1) {
animation-delay: 2s;
}
.flakes i:nth-of-type(5n+2) {
animation-delay: 4s;
}
.flakes i:nth-of-type(5n+3) {
animation-delay: 6s;
}
.flakes i:nth-of-type(5n+4) {
animation-delay: 8s;
}
Animation durations are calculated to be multiples of each other +/- a few seconds for initial delays.
.flakes i:nth-child(3n+0) {
animation-duration: 12s;
}
.flakes i:nth-child(3n+1) {
animation-duration: 18s;
}
.flakes i:nth-child(3n+2) {
animation-duration: 24s;
}
Tweak the timing functions to improve the randomness.
.flakes i:nth-of-type(6n+0) {
animation-timing-function: ease-in-out;
}
.flakes i:nth-of-type(6n+1) {
animation-timing-function: ease-out;
}
.flakes i:nth-of-type(6n+2) {
animation-timing-function: ease;
}
.flakes i:nth-of-type(6n+3) {
animation-timing-function: ease-in;
}
.flakes i:nth-of-type(6n+4) {
animation-timing-function: linear;
}
.flakes i:nth-of-type(6n+5) {
animation-timing-function: cubic-bezier(0.2, 0.3, 0.8, 0.9);
}
Tweak opacity for even more randomness.
.flakes i:nth-of-type(7n+0) {opacity: 0.5;}
.flakes i:nth-of-type(7n+1) {opacity: 0.8;}
.flakes i:nth-of-type(7n+2) {opacity: 0.3;}
.flakes i:nth-of-type(7n+4) {opacity: 0.7;}
.flakes i:nth-of-type(7n+6) {opacity: 0.6;}
Increase the number of snowflakes at common device widths. Remember that flex
evenly distributes them across the horizontal.
.flakes .snow-1024px,
.flakes .snow-1280px,
.flakes .snow-1366px,
.flakes .snow-1440px,
.flakes .snow-1600px,
.flakes .snow-1800px,
.flakes .snow-1920px {
display: none
}
@media screen and (max-width: 767px) {
.flakes {display: none}
}
@media screen and (min-width: 1024px) {
.flakes .snow-1024px {display: inline-block}
}
@media screen and (min-width: 1280px) {
.flakes .snow-1280px {display: inline-block}
}
@media screen and (min-width: 1366px) {
.flakes .snow-1366px {display: inline-block}
}
@media screen and (min-width: 1440px) {
.flakes .snow-1440px {display: inline-block}
}
@media screen and (min-width: 1600px) {
.flakes .snow-1600px {display: inline-block}
}
@media screen and (min-width: 1800px) {
.flakes .snow-1800px {display: inline-block}
}
@media screen and (min-width: 1920px) {
.flakes .snow-1920px {display: inline-block}
}
One simple animation function for all the snowflakes:
@keyframes falling {
from {
transform:
translate(0, 0)
rotate(0deg)
scale(0.8);
}
to {
transform:
translate(0, 120vh)
rotate(360deg)
scale(1.2);
}
}
Adding snowflakes dynamically
Need to get around fixed template limitations? JavaScript once again to the rescue, and it may as well late-load all the assets too.
(function () {
"use strict";
// Dynamically add snowflake HTML & CSS to the page upon page-load.
function letItSnow() {
var flakesDiv = document.createElement("div"),
link = document.createElement("link"),
html = "";
// Fetch snow CSS:
link.href = "snow.css";
link.rel = "stylesheet";
document.head.appendChild(link);
// Build HTML:
flakesDiv.id = "flakes";
flakesDiv.className = "flakes";
// 1 row at 768px
html += '<i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i>';
// Common device widths
html += '<i class=snow-1024px></i><i class=snow-1024px></i><i class=snow-1024px></i>';
html += '<i class=snow-1280px></i><i class=snow-1280px></i>';
html += '<i class=snow-1366px></i><i class=snow-1366px></i>';
html += '<i class=snow-1440px></i>';
html += '<i class=snow-1600px></i><i class=snow-1600px></i>';
html += '<i class=snow-1800px></i><i class=snow-1800px></i>';
html += '<i class=snow-1920px></i><i class=snow-1920px></i>';
html += '<div></div>';
// Add HTML
flakesDiv.innerHTML = html;
document.body.insertBefore(flakesDiv, document.body.childNodes[0]);
}
// classList test to eliminate IE9 specifically
if (window.addEventListener && document.documentElement.classList) {
window.addEventListener("load", letItSnow, false);
}
}());
Social links and email client: