Learn how to draw a snow globe with a single div and CSS.
In this blog post I will walk you through how I created a snow globe with animating snowflakes using one div and a bunch of CSS.
With the holidays right around the corner, I wanted to create something festive. I went back and forth between a Christmas tree or a snow globe and settled on a snow globe.
For this tutorial, I will be picking up right where I left off from my previous blog post: CSS Magic with a Single DIV. In that tutorial, I covered the basic building blocks of single div drawing:
- pseudo-elements
- box-shadows
- background and gradients
- …and more.
Now, let’s get started!
Drawing a Snow Globe with CSS
It’s helpful to break down the object you intend to draw into basic shapes. In the case of the snow globe, there are two main shapes:
- a circle for the globe
- a quadrilateral for the base
Step 1: Create the basic shapes of the snow globe
With our basic shapes in mind, it’s time to draw them. First add a div for the drawing on the page.
<div></div>
At its core, single div drawing is just drawing in layers. The globe portion of our snow globe is overlapped by our base. I’ll style the div for the globe and use a pseudoelement for the base.
body {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: dodgerblue;
}
/* snow globe */
div {
height: 50vh;
width: 50vh;
border-radius: 50%;
background: white;
}
/* snow globe base */
div::before {
content: '';
position: absolute;
top: 65%;
left: 25%;
width: 50%;
height: 25%;
background: burlywood;
}
(There’s a small quirk to using the psuedoelements. The content property is required or else there is nothing to draw. Assigning it an empty string will suffice.)
Our globe looks really basic. Let’s refine it with gradients. We’ll start with the base and move up to the globe.
Step 2: Transform the base into a trapezoid
The look I am going for the base is the shape you get if you sliced off a portion of a cone from the base up. If you were to look at that shape from dead on, it would like a trapezoid.
One way we can turn our rectangle into a trapezoid is using the transform property. Here’s an article that can explain this technique better than I. I will use the perspective and rotateX functions to create a shape that looks like a trapezoid, but it’s really just a rectangle with a different viewing angle.
transform: perspective(75px) rotateX(10deg);
The transformation altered the size of the base, so I made a few more tweaks to div::before
pseduoelement to get the shape you see above.
div::before {
content: '';
position: absolute;
top: 65%;
width: 50vh;
height: 15%;
background: burlywood;
transform: perspective(75px) rotateX(10deg);
}
Step 3: Add dimension to the base with gradients
The base to the snow globe is flat and boring. With linear gradients, we can add some fun dimensions to the base.
To make the base look more like a conical shape, we’ll apply a linear gradient that runs from the left to right of the shape.
background-image:
linear-gradient(90deg, saddlebrown,
chocolate,
transparent,
chocolate,
saddlebrown);
Now, that looks better. However, while it’s no longer flat in appearance, the base is still looks a little boring. A nice touch would be adding a small ridge to the top and bottom of the base. We can do this with the help of an another gradient. With the background-image
(or background
) property, we can apply multiple layers of a gradients. The first gradient in the background-image
property is the topmost gradient.
We’ll need to make use of the background-position
and background-size
properties to properly arrange the new additions to the base.
background-image:
/* Top ridge */
linear-gradient(saddlebrown, transparent, saddlebrown),
/* Bottom ridge */
linear-gradient(saddlebrown, transparent, saddlebrown),
/* Main base */
linear-gradient(90deg, saddlebrown,
chocolate,
transparent,
chocolate,
saddlebrown);
background-size:
/* Top ridge */
100% 15%,
/* Bottom ridge */
100% 10%,
/* Main base */
100% 100%;
background-position:
/* Top ridge */
0 0,
/* Bottom ridge */
0 100%,
/* Main base */
0 0;
background-repeat: no-repeat;
We’re done with the base for now. Let’s revisit the globe.
Step 4: Add a radial gradient to the globe
For the globe, we’ll use a radial gradient. Replace the white
background color with a radial-gradient
.
background: radial-gradient(transparent 45%, white);
I used a stop of 45% over the default so only the edges of the circle will have a “frosted glass” look.
Step 5: Add a layer of snow to the base of the globe.
Next up, we need to add a layer of snow to the inside of the globe. For this we’ll use a linear-gradient
. We can manipulate the gradient stops to only draw at the lower portion of our globe.
background:
radial-gradient(transparent 45%, white),
linear-gradient(transparent 70%, white 71%);
If the gradient stop for the white
color was set to 70%, there would be a harsh solid division between the transparent and white portions of the gradient. With 71%, it softens the transition ever so slightly such that it’s not too jarring or too fuzzy.
Step 6: Add snowflakes
My original thought for the snowflakes was to create squares and multiply them, but then I discovered there is a unicode character for a snowflake.
Three in fact:
- U+2744 (snowflake)
- U+2745 (tight trifoliate snowflake)
- U+2746 (heavy chevron snowflake)
To make use of the unicode characters in the single div, we can use inline SVG.
(Is it cheating to use SVG in single div drawings?)
SVG has a text element and we can add multiple SVGs to our background
property. There is one small caveat to using inline SVG: it must be URL encoded. Fortunately, I found a handy tool, from the github user yorkel, to take care of the all the tedious work.
The SVG for drawing a snowflake is pretty simple. With the following SVG input:
<svg height='40' width='40'>
<text x='0' y='15' fill='black'>❄
</text>
</svg>
The SVG URL encoder outputted:
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='40' width='40'%3E%3Ctext x='0' y='15' fill='black'%3E&%23x2744; %3C/text%3E%3C/svg%3E");
Add the URL encoded SVG to the background image. I created an inline SVG for each snowflake variant.
background:
radial-gradient(transparent 45%, white),
linear-gradient(transparent 70%, white 71%),
/* Snowflake Style U+2744 */
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='50' width='50'%3E%3Ctext x='0' y='15' fill='lightcyan'%3E&%23x2744;%3C/text%3E%3C/svg%3E"),
/* Snowflake Style U+2745 */
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='40' width='40'%3E%3Ctext x='0' y='15' fill='white'%3E&%23x2745;%3C/text%3E%3C/svg%3E"),
/* Snowflake Style U+2746 */
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='60' width='60'%3E%3Ctext x='0' y='15' fill='linen'%3E&%23x2746;%3C/text%3E%3C/svg%3E");
Step 7: Animate the snowflakes
Our drawing is really starting to come together. To bring it to life, let’s animate the snowflakes’ positions.
div {
[ a bunch of other stuff... ]
animation: animate-snowflakes 35s linear infinite alternate;
}
@keyframes animate-snowflakes {
to {
background-position:
0 0,
0 0,
20% 50%,
75% 50%,
50% 100%;
}
}
Step 8: Add the final touches
We still have a whole pseduoelement (div::after
) we can leverage for drawing additional elements like a small plaque on the base, or an object inside of the snow globe.
div::after {
position: absolute;
content: 'Christmas 2021';
font-family: 'Oleo Script Swash Caps', cursive;
text-align: center;
width: 10%;
height: 1.5rem;
left: 45%;
bottom: 22%;
color: #331F01;
background: gold;
background-image:
linear-gradient(90deg, goldenrod, transparent, goldenrod);
border: 2px outset goldenrod;
padding: 4px 0;
}
Also to add a little more depth to the snow globe, we can make use of the box-shadow
property.
Add this box-shadow to both the div
and div::before
styles:
box-shadow: 0 0 55px 10px dodgerblue;
And finally, update background for the body element to use a linear gradient too:
background: linear-gradient(dodgerblue, midnightblue);
Tinker with project over at codepen.io:
See the Pen Single DIV Snow Globe with animating Snowflakes by Ashley Grenon (@townsean) on CodePen.