How to create pure CSS illustrations and animate them - Part 3
Tags: css, purecss, animation, illustration
Published: 2018-09-04 11:22:19

---
title: How to create pure CSS illustrations and animate them - Part 3
published: true
description: A guide to create pure CSS illustrations and animate them
tags: CSS, pureCSS, animation, illustration
cover_image: https://thepracticaldev.s3.amazonaws.com/i/0u67lvhvwl4wvahg4uw3.jpg
---

This is the third part in a three-part series about CSS illustrations and animations. In this last part, we are going to build an animated lighthouse scene. For this, we'll look at new techniques such as SASS loops and CSS 3D transforms.

[Part 1: Learning basics and workflow tips with a CSS Smiley Face](https://dev.to/agathacco/how-to-create-pure-css-illustrations-and-animate-them---part-1-1j1k)
[Part 2: Intro to CSS animations with a CSS Polaroid](https://dev.to/agathacco/how-to-create-pure-css-illustrations-and-animate-them---part-2-1ao4) 
**Part 3: More advanced techniques with a CSS Lighthouse Scene**

Here's what we'll be building: 

![](https://thepracticaldev.s3.amazonaws.com/i/05hsnvrtbdnb7f9rte4k.jpg)

Because this illustration is a bit more complex than the previous ones, we will go more quickly over what we've already seen. I will however introduce some new techniques like CSS 3D transforms, repeating-gradients and SASS loops.

Before we get started, let's observe what components make this illustration, and how they overlap. Our scene is composed of two main groups, the background and the lighthouse. We need to set the background at the bottom of the z pile, and the lighthouse at the top. Both of these two groups each contain many elements that also overlap, which can easily become confusing. Let's make use of the z-index property to ensure each element is at the right place in the pile.

Here's what our basic HTML/Pug tree looks like. We'll be adding a few more elements to it later on.

```html
.scene
  .background
    .stars
    .moon
    .mountains
    .sea
      .waves
      .boat
  .lighthouse-group
    .land
    .lighthouse-holder
      .shadow
      .lighthouse
      .top
      .windows
      .door
        .stairs
```

In the SCSS, let's define color variables and global properties: 

```scss
$x-dark: #29284c;
$dark: #4c4b82;
$medium: #717ae1;
$light: #b9befa;
$x-light: #d6d9f6;
$aqua: #75e2fa;
$grey: #9e9ebe;
$yellow: #f7f2b4;

html,
body {
  height: 100%;
  width: 100%;
  overflow: hidden;
  padding: 0;
  margin: 0;
}
html {
  box-sizing: border-box;
}
*,
*:before,
*:after {
  box-sizing: inherit;
}
* {
  position: absolute;
}
*:before,
*:after {
  content: "";
  position: absolute;
}
```

We'll work on the background first: 

```scss
.scene {
  width: 100vw;
  height: 100vh;
}
.background {
  background: $x-dark;
  background-image: linear-gradient(
    $x-dark 0%,
    $dark 10%,
    $medium 60%,
    $aqua 90%
  );
  width: 100%;
  height: 100%;
  overflow: hidden;
  z-index: 1;
}
.sea {
  background: $x-dark;
  background-image: linear-gradient(
    to top,
    $x-dark 0%,
    $dark 30%,
    $medium 60%,
    $aqua 90%
  );
  width: 100%;
  height: 170px;
  bottom: 0;
  left: 0;
  z-index: 2;
}
```

The `.scene` element will act as our main container and we want it to be as wide and high as our screen.  The `.background` is  our first layer, and the `.sea` is placed on top. We are applying gradients to both elements with the `background-image` property. Again, in real life, we would have to use vendor prefixes for this property, but for the sake of brevity, let's omit them.

![](https://thepracticaldev.s3.amazonaws.com/i/wi1ixt8hxihg0thp21hm.jpg)

####Using loops to generate and randomize content

On to the stars. We want to create about 60 stars, so we need 60 HTML elements. We also need to generate a unique position for each of these `.star` elements. It means 60 different CSS classes, or 60 uses of the `nth-child` selector. We don't really want to do this as it sounds really long and repetitive. Instead, let's us a loop. 

In Pug and SCSS, just like in Javascript, loops are a powerful tool. They allow you, amongst other things, to easily and automatically generate content. Here's how it works.

First we need to create 60 `.star` elements with Pug. Pug loops are simply [javascript loops](https://www.digitalocean.com/community/tutorials/how-to-construct-for-loops-in-javascript) with a slightly different syntax:

```html
- for (var x = 0; x < 60; x++)
  .star
```

That's it! Only two lines of code!

Now we need to style these elements. First, we  create  a `.stars` container that will contain all the `.star` elements. Then we can define the common styles for all them. (we'll create the twinkle animation a bit later).  

```scss
.stars {
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  .star {
    border-radius: 50%;
    background-color: $light;
    animation: twinkle 5s ease-in-out infinite;
  }
}
```

We still cannot see any stars in our illustration because we haven't set a position or size yet. We need these properties to be different for each star. Like we said earlier, we could target these elements one by one and manually code a different value for each, but we've already decided against that. A SASS loop is going to help us randomly generate these values instead. Here's the syntax of a basic loop: 

```scss
@for $i from 1 through (60) {
 // do something
}
```

In order to target each element with different values, we can use the nth-child selector:

```scss
@for $i from 1 through (60) {
  .star:nth-child(#{$i}) {
    // do something
  }
}
```

In CSS this will compile to: 

```scss
.star-nth-child(1) {
  // do something
}

.star-nth-child(2) {
  // do something
}

...

.star-nth-child(60) {
  // do something
}
```

And to randomize the values, we are using the SASS random function:

```scss
@for $i from 1 through (60) {
  .star:nth-of-type(#{$i}) {
    top: random(100)+vh;
    left: random(100)+vw;
    width: random(4)+px;
    height: random(4)+px;
    animation-delay: random(5)+s;
  }
}
```

The random function takes an integer as a parameter, and returns a random value between 1 and the passed integer. So `random(100)` will return a random number between 1 and 100. We then add the unit we want as a suffix. We want the stars to spread across the entire screen, so I'm using the vw/vh units for the `top` and `left` properties. It will place the stars at a random position on the screen, and because the random function is defined in the loop, it will be called for each star, generating a new position each time.

This was easy to set up, but we have an issue. If you look at the stars, you'll see that they aren't round. This is because the height and width properties have each been assign a different value that's been generated with a different instance of the random function. To fix this we need to store the result of the random function in a variable: 

```scss
@for $i from 1 through (60) {
  $size: random(4)+px;
  .star:nth-of-type(#{$i}) {
    top: random(100) + vh;
    left: random(100) + vw;
    width: $size;
    height: $size;
    animation-delay: random(5) + s;
  }
}
```

Here we go. Our stars aren't animated yet but we'll fix this later.  

![](https://thepracticaldev.s3.amazonaws.com/i/xzwyevvf3ceyyzabgntp.jpg)

___

Let's add some more details to our background: 

The moon is created with a simple border-radius, combined with the `box-shadow` property to create a glow around it:

```scss
.moon {
  width: 80px;
  height: 80px;
  top: 25%;
  right: 10%;
  border-radius: 50%;
  z-index: 2;
  background-color: $x-light;
  box-shadow: 0 0 10px $x-light, 0 0 20px $x-light, 0 0 30px $x-light, 0 0 40px $x-light, 0 0 50px $aqua, 0 0 100px $x-light;
}
```
![](https://thepracticaldev.s3.amazonaws.com/i/b4s203xvp9yqr34x5gmu.jpg)

For the mountains, we use a Pug loop again to create four elements.

```html
.mountains
  - for (var i = 0; i < 4; i++)
    .mountain 
```

Each mountain is essentially a square that's been rotated by 45 degrees, and half hidden behind the `.sea` element. Then for each, we use the `:after` selector to add a gradient. We can give each of them a different position and size using the `nth-child` selector.

```scss
.mountains {
  width: 100%;
  height: 250px;
  bottom: 65px;
  z-index: 3;
  .mountain {
    width: 250px;
    height: 250px;
    background-color: $medium;
    right: 50px;
    bottom: -40px;
    transform: rotate(45deg);
    border-radius: 3px;
    &:after {
      width: 100%;
      height: 100%;
      opacity: 0.7;
      background-image: linear-gradient(135deg, $dark 0%, $medium 20%, $aqua 40%);
    }
  }
  .mountain:nth-child(2) {
    right: 220px;
    width: 240px;
    height: 240px;
    z-index: 2;
  }
  .mountain:nth-child(3) {
    right: 350px;
    width: 260px;
    height: 260px;
  }
  .mountain:nth-child(4) {
    right: 130px;
    width: 200px;
    height: 200px;
    z-index: 3;
    bottom: -70px;
    &:after {
      background-image: linear-gradient(135deg, $dark 0%, $medium 6%, $aqua 20%);
    }
  }
}
```
![](https://thepracticaldev.s3.amazonaws.com/i/6cosroux6dw2bbe6uoh9.jpg)

To add waves in the sea, I'm using Pug and SASS loops again: 

```html
.sea
  - for (var i = 0; i < 30; i++)
    .wave
```
```scss
.wave {
  background-color: $x-light;
  height:3px;
  border-radius: 100%;
  opacity: 0.2;
  animation: wave 5s linear infinite;
}
@for $i from 1 through (30) {
  $size: random(100) + 50px;
  .wave:nth-of-type(#{$i}) {
    bottom: random(170) + px;
    left: random(100) + vw;
    width: $size;
    opacity: random (5) * 0.1;
    animation-delay: random(3) + s;
  }
}
```
![](https://thepracticaldev.s3.amazonaws.com/i/r7bv5ag94dcubmpxui2l.jpg)

The last element in the background is the boat. It is composed of three main parts: a base, and two sails. To create the base and the triangular shapes of the sails, let's use the [clip-path](https://css-tricks.com/almanac/properties/c/clip/) property. [This tool](https://bennettfeely.com/clippy/) is great for creating clip-path shapes on the fly.  

```scss
.boat {
  width: 90px;
  height: 90px;
  bottom: 90px;
  .base {
    width: 110px;
    height: 25px;
    bottom: -20px;
    clip-path: polygon(0 0, 100% 0, 100% 100%, 20% 100%);
    background-color: $dark;
  }
  .sail:nth-child(1) {
    width: 90px;
    height: 80px;
    left: 5px;
    clip-path: polygon(50% 0%, 0% 100%, 50% 100%);
    background: linear-gradient($light 0%, $dark 60%);
  }
  .sail:nth-child(2) {
    width: 80px;
    height: 70px;
    left: 15px;
    bottom: 10px;
    transform: scaleX(-1);
    clip-path: polygon(50% 0%, 0% 100%, 50% 100%);
    background: linear-gradient($light 0%, $dark 60%);
  }
}
```
> the transform: scaleX(-1) on the second sail is used to flip it horizontally.

And with `:before` and `:after` selectors, we can add a shadow and a trail. The `z-index:-1` property ensures these elements are placed behind their siblings:

```scss
.boat {
  width: 90px;
  height: 90px;
  bottom: 90px;
  &:after {
    height: 8px;
    width: 200px;
    background: linear-gradient(90deg, transparentize($x-light, 0.3) 30%, rgba(255, 255, 255, 0) 100%);	
    border-radius: 40%;
    top: 105px;
    left: 20px;
    z-index: -1;
  }
  &:before {
    width: 92px;
    height: 50px;
    left: 25px;
    bottom: -70px;
    background: linear-gradient(to bottom, transparentize($x-dark, 0.2) 30%, transparentize($x-dark, 1) 100%);
    z-index: -1;
  }
  //...
}
```
![](https://thepracticaldev.s3.amazonaws.com/i/i3fonc0e737a07nv63rz.jpg)

####Using CSS transforms

The background part of our illustration is done. Let's move on to the lighthouse.

We'll be using a `.lighthouse-group` div that will act as the main container for this part of the illustration. It has two main components, the land, and the actual lighthouse. Let's place `.lighthouse-group` on the document by assigning it a size and position: 

```scss
.lighthouse-group {
    width: 50%;
    height: 100%;
    bottom: 0;
    left: 0;
    z-index: 2;
}
```

Let's add the bit of land first:

```scss
.land {
  width: 400px;
  height: 60px;
  left: -30px;
  bottom: 0;
  background-image: linear-gradient(to top, $x-dark 80%, $medium 100%);
  transform: skewX(35deg);
  border-radius: 10px;
}
```

With the transform property we can [skew](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/skew) the element, then move it to the left and hide the left side.

![](https://thepracticaldev.s3.amazonaws.com/i/g91wkkzdx6hpke979tfx.jpg)

Now for the actual lighthouse, we can start by giving a size and position to the lighthouse group:

```scss
.lighthouse-holder {
  height: 480px;
  width: 100px;
  bottom: 80px;
  left: 180px;
}
```

Then, to make the body of the lighthouse narrower at the top, we're going to use two transform properties, `perspective` and `rotateX`. 

The `perspective` property sets a 3D space for the lighthouse element, then the `rotateX` property rotates it on the X axis on this 3D space. Technically, the top of the lighthouse is further away from us, while the bottom is nearer, but this gives us the trapeze effect we want.

```scss
.lighthouse {
  width: 100%;
  height: 100%;
  transform: perspective(600px) rotateX(20deg);
  background-color: $x-light;
}
```
![](https://thepracticaldev.s3.amazonaws.com/i/tvra2td6po862hb3wcyi.jpg)

[Here's](https://3dtransforms.desandro.com/) an excellent link if you wish to know more about CSS 3D transforms.

Next, to create the stripes, let's use a [repeating gradient](https://css-tricks.com/snippets/css/css-repeating-gradients/):

```scss
.lighthouse {
  width: 100%;
  height: 100%;
  transform: perspective(600px) rotateX(20deg);
  background-color: $x-light;
  background-image: repeating-linear-gradient(
    -40deg,
    transparent,
    transparent 60px,
    $dark 60px,
    $dark 120px
  );
}
```
![](https://thepracticaldev.s3.amazonaws.com/i/10ctszbb0bnu3b9c3um1.jpg)

I also want to add some shading with another gradient layered on top. We'll use the `:after` pseudo-selector for that: 

```scss
.lighthouse {
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transform: perspective(600px) rotateX(20deg);
  background-color: $x-light;
  background-image: repeating-linear-gradient(
    -40deg,
    transparent,
    transparent 60px,
    $dark 60px,
    $dark 120px
  );
  &:after {
    width: 100%;
    height: 100%;
    opacity: 1;
    background-image: linear-gradient(
      90deg,
      transparentize($x-light, 0.4) 0%,
      $x-dark 8%,
      transparent 70%,
      transparentize($x-light, 0.6) 100%
    );
  }
}
```

And we also need an extra shadow at the bottom of the lighthouse. We can use the `skewX` property for this again: 

```scss
.shadow {
  width: 117px;
  height: 50px;
  left: -32px;
  bottom: -70px;
  background: $x-dark;
  transform: skewX(-45deg);
}
```
![](https://thepracticaldev.s3.amazonaws.com/i/eatw3qr8uqjwgcrei82t.jpg)

To create the windows, we first need to add four `.window` elements to the HTML. To space them out, we can use a SASS loop like we did before. However this time, instead of using the random function, we can do a simple addition.

First, we're setting the original value of the bottom property to `90px`. Then, at each run of the loop, we add 90px to this value. The end result is four identical `.window` elements that are each 90px apart vertically. 

```html
.windows
  - for (var i = 0; i < 4; i++)
	.window
```
```scss
.windows {
  height: 100%;
  width: 100%;
  .window {
    background-color: $x-dark;
    height: 25px;
    width: 15px;
    left: 43px;
    border-bottom: 2px solid rgba($light, 0.7);
    border-radius: 25px 25px 0 0;
  }
  $bottom: 90px;
  @for $i from 1 through (4) {
    .window:nth-of-type(#{$i}) {
      bottom: $bottom;
    }
    $bottom: $bottom + 90px;
  }
}
```
![](https://thepracticaldev.s3.amazonaws.com/i/6j102usz8fadj9q2kbrs.jpg)

The door is pretty straightforward. For the stairs, let's use a combination of `perspective` and `rotateX` again to get a trapeze shape. Then a repeating gradient will create steps.

```scss
.door {
  background-color: $x-dark;
  height: 40px;
  width: 25px;
  left: 38px;
  bottom: -2px;
  border-radius: 2px 2px 0 0;
  .stairs {
    width: 27px;
    height: 28px;
    background-color: $x-dark;
    top: 34px;
    left: -1px;
    transform: perspective(100px) rotateX(45deg);
    background-image: repeating-linear-gradient(
      to bottom,
      $x-dark,
      $x-dark 4px,
      $light 4px,
      rgba(white, 0.1) 5px
    );
  }
}
```
![](https://thepracticaldev.s3.amazonaws.com/i/v0t1utcm75t0uli6i7p5.jpg)

Okay, the bottom part of the lighthouse is done. Let's move on to the top part.

First we need to add a few HTML elements:

```html
.top
  .light-container
    .light
  .rail
  .middle
  .roof
    .roof-light
  .glow
```

We are first setting a size and a position for the `.top` container, and then, we can get started on the `.base` part of the structure. We're using the `perspective` + `rotateX` technique again to give it some shape, as well as a repeating gradient to create the rail.

```scss
.top {
  width: 94px;
  height: 60px;
  left: 3px;
  top: -15px;
    .rail {
      width: 100%;
      height: 17px;
      bottom: 1px;
      border: 3px solid $x-dark;
      border-radius: 1px;
      transform: perspective(1000px) rotateX(-35deg);
      background-image: repeating-linear-gradient(
        90deg,
        $x-dark,
        $x-dark 3px,
        $grey 3px,
        $yellow 10px
      );
      background-position: -2px 0;
   }
}
```
![](https://thepracticaldev.s3.amazonaws.com/i/e4ubomtk8yzuu6i91e6o.jpg)

The `.middle` part is very similar, except we don't need a transform. We're also using the `:before` selector to add a nice glow behind it:

```scss
.middle {
  width: 88px;
  height: 35px;
  left: 3px;
  bottom: 14px;
  border: 2px solid $x-dark;
  border-radius: 3px;
  background-image: repeating-linear-gradient(
    90deg,
    $x-dark,
    $x-dark 4px,
    $grey 4px,
    rgba(255, 255, 255, 0) 21px
  );
  background-position: -2px 0;
  &:before {
    width: 100%;
    height: 100%;
    z-index: -1;
    background-color: $yellow;
    box-shadow: 0 0 10px $x-light, 0 0 20px $yellow, 0 0 30px $yellow,
    0 0 40px $yellow, 0 0 70px $yellow;
  }
}
```

![](https://thepracticaldev.s3.amazonaws.com/i/yl45romslg3q239sytxk.jpg)

For the roof, we can use the [border technique](https://css-tricks.com/snippets/css/css-triangle/) to create a triangular shape. 

There are a handful of ways to create triangles in css. Earlier we used the clip-path method to create the triangular sails of the boat. In this case however, I want to use `:before` and `:after` to create the top parts of the roof, which `clip-path` wouldn't let me do. 

```scss
.roof {
  width: 0px;
  height: 0px;
  left: -3px;
  bottom: 45px;
  border-left: 50px solid rgba(255, 255, 255, 0);
  border-right: 50px solid rgba(255, 255, 255, 0);
  border-bottom: 40px solid $x-dark;
  &:before {
    width: 14px;
    height: 14px;
    left: -7px;
    bottom: -7px;
    background-color: $x-dark;
    border-radius: 50%;
  }
  &:after {
    width: 4px;
    height: 14px;
    left: -2px;
    bottom: 5px;
    background-color: $x-dark;
    border-radius: 3px;
  }
}
```
![](https://thepracticaldev.s3.amazonaws.com/i/4a9zpun7hdyay79ltxog.jpg)

Last we need to add some light on the roof. We've run out of pseudo-selectors so we need to create a new HTML `.roof-light` element. This time I'm using the `clip-path` property to make sure the gradient is contained in the roof:

```scss
.roof-light {
  width: 100px;
  height: 40px;
  left: -50px;
  clip-path: polygon(50% 0, 0% 100%, 100% 100%);
  background-image: linear-gradient(
    135deg,
    $x-dark 40%,
    rgba($yellow, 0.5) 100%
  );
}
```

![](https://thepracticaldev.s3.amazonaws.com/i/derwf40plbkhmo1thju8.jpg)

The last element of our illustration is the light. Because we are going to animate it in a moment on a 3D space, we need two elements. The `.light-container` element will be used to rotate the light from back to front. The `.light` element is used to give the light its trapeze shape:

```scss
.light-container {
  height: 40px;
  width: 35vw;
  bottom: 4px;
  left: 40px;
  transform-style: preserve-3d;
  transform-origin: left bottom;
  transform: perspective(500px) rotateY(0deg);
  .light {
    width: 100%;
    height: 100%;
    transform-style: preserve-3d;
    transform-origin: left center;
    transform: perspective(500px) rotateY(-35deg);
    background: linear-gradient(90deg, $yellow 40%, rgba(255, 255, 255, 0) 100%);
  }
}
```
![]()

And the last touch is to add a glow when the light rotates towards us. We'll set its opacity to 0 for now:

```scss
.glow {
  width: 100px;
  height: 60px;
  top: 0;
  left: 0;
  background-color: $yellow;
  opacity: 0;
  border-radius: 50%;
  box-shadow: 0 0 10px $yellow, 0 0 20px $yellow, 0 0 30px $yellow, 0 0 40px $yellow, 0 0 50px $yellow, 0 0 60px $yellow, 0 0 70px $yellow, 0 0 80px $yellow;
}
```
![](https://thepracticaldev.s3.amazonaws.com/i/fqpawnoixmhq1ropbleh.jpg)

And our illustration is complete! This was a lot of work, but we're not quite done yet. It's time for animations!

Here are the elements we are going to animate:
* the stars will twinkle
* the waves will move from left to right
* the boat will move across the screen
* the shadow of the boat will transform as it moves away from the moon
* the light will rotate from back to front
* a glow will appear when the light faces us

The star animation is simple as we are only animating the opacity property. We've previously added a random animation delay to the star elements, which ensures the animation doesn't run simultaneously for all stars. 

```scss
@keyframes twinkle {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.3;
  }
}

.star {
  //...
  animation: twinkle 5s linear infinite;
}
```

The wave animation is an easy one too:

```scss
@keyframes wave {
  0%,
  100% {
    transform: translateX(0);
  }
  50% {
    transform: translateX(-10px);
  }
}

.wave {
  //...
  animation: wave 5s linear infinite;
}
```

Let's animate the boat. Initially, it needs to be off screen. We can use the `translateX` property to move the boat to the far right of the screen. During the animation, the boat will appear to the right, then slowly move to the left, then disappear off screen again. I've also added the `scale(0.8)` value to reduce its size a bit.

```scss
@keyframes boat {
  0% {
    transform: translateX(120vw) scale(0.8);
  }
  80%,
  100% {
    transform: translateX(-25vw) scale(0.8);
  }
}
```

We also need to animate its shadow as it passes in front of the moon, then moves away from it: 

```scss
@keyframes boatShadow {
  0% {
    transform: skewX(35deg) translateX(15px);
  }
  50%,
  100% {
    transform: skewX(-55deg) translateX(-40px);
  }
}
```
```scss
$boatSpeed: 100s;

.boat { 
  //... 
  animation: boat $boatSpeed linear infinite; 
  &:after { 
    //... 
    animation: boatShadow $boatSpeed linear infinite; 
  }
}
```

The light animation is the most complex one. It needs to rotate to the back first,  then rotate to the front, then go back to its initial state  at the end of the animation.

The initial state is the one we have defined in our `.light-container` selector already. To rotate it to the back, we are rotating it on its Y axis by 35 deg. Then to rotate to the front, we do the same but with a negative value:

```scss
@keyframes lightRotate {
  0%,
  100% {
    // initial and final state
    transform: perspective(500px) rotateY(0deg);
  }
  25% {
    //rotates to the back
    transform: perspective(500px) rotateY(35deg);
  }
  75% {
    // rotates to the front
    transform: perspective(500px) rotateY(-35deg);
  }
}

$lightSpeed: 40s;

.light-container {
  //...
  animation: lightRotate $lightSpeed linear infinite;
}
```

And lastly, we also need to make the glow appear when the light is rotating towards us: 

```scss
@keyframes lightGlow {
  0%, 50%, 100% {
    opacity: 0;
  }
  75% {
    opacity: 1;
  }
}

.glow {
  //...
  animation: lightGlow $lightSpeed linear infinite;
}
```

Aaaaand we're done! 

> 3D transforms can be a bit tricky to understand, and if you're struggling a bit, here's a tip: add a border to all elements that have a 3D transform. It'll reveal the shape and position of all elements, including the hidden ones.

Eg:

```scss
.light-container {
  //...
  border: 1px solid white;
  .light {
    border: 1px solid pink;
  }
}
```		

Here's the final project in Codepen.

{% codepen https://codepen.io/agathaco/pen/JaGQPr %} 

#Next steps

So you've followed along the tutorial and built and animated your first CSS images. Now what? If you don't know where to begin, I suggest starting by replicating existing illustrations. Look on [Dribbble](https://dribbble.com/)  and use a simple illustration you like as a reference (as long as you give credit to the illustrator and link to the original illustration, it's totally fine!). Starting with a model will take away the creative aspect and let you focus on the code and logic. When you are comfortable enough with the process,  you'll find it easier to create your own CSS images from scratch. 

As a rule of thumb, it is best to go for a ‘flat design’ look, with geometric shapes, no textures and flat colors, as they're easier to replicate with CSS. But then again, with a little imagination and experimentation,  you can achieve amazing results. Here are a few examples of impressive pure CSS illustrations: 

* [CSS Francine](http://diana-adrianne.com/purecss-francine/) by Diana Adrianne
* [Cat Swinging on String](https://codepen.io/davidkpiano/pen/Xempjq) by David Khourshid
* [Pure CSS Moustached Nanny](https://codepen.io/miocene/pen/mjLPVp) by Julia Muzafarova
* [Only CSS Paper Bird](https://codepen.io/YusukeNakaya/pen/BwBgvq) by Yusuke Nakaya
* [CSS Camera](https://codepen.io/cassidoo/pen/QMZmNV) by Cassidy Williams
* [Glowing Meteor](https://codepen.io/maaarj/pen/KWNLaO) by Marjo Sobrecaray

#Useful tools and resources

* [CSS gradient generator](https://cssgradient.io/)
* [Color palette generator](https://coolors.co/)
* [Clip-path generator](https://bennettfeely.com/clippy/) 
* [Codepen](https://codepen.io/)
* [SASS](https://sass-lang.com/)
* [Pug](https://pugjs.org/api/getting-started.html)

That's it folks! I hope you enjoyed this series and learned a few things along the way. Follow me on [CodePen](https://codepen.io/agathaco) to see what I'm up to, or hit me up on [Twitter](https://twitter.com/agathacco) to keep in touch.