I've been asked by a few people if I could write a tutorial for my Zelda Fairy CSS animation.

This is part 1 of the tutorial, which will cover how to code the fairy.

Part 2 is coming soon and will cover animation (and sparkles).

Creating assets

Everyone's workflow will vary, but I like to hop into Sketch and design my ideas prior to coding. This allows me to get a sense of what the project might look like in its final, or near final state. Plus, by creating my design in Sketch, I have assets ready to export as SVG files, just in case. Any graphics program that can export SVG should work.

To get a feel for how to draw a Zelda: Breath of the Wild fairy, you should watch this reference video on Youtube. The fairies are comprised of five unique parts: circular body, glow, top wing, bottom wing, and sparkling. Take some screenshots and drop them into your graphics program, they are very helpful to look at while creating your assets.

My reference images

My created assets

CSS or SVG?

We can decide which parts of the fairy are feasible to code in CSS and which are better to export as SVG.

CSS has some very powerful gradient functions which are detailed here. As Chris Coyier writes in the article, using code when feasible is best practice. Performance is better, resolution is scalable, and you are able to make quick changes by tweaking values rather than exporting and replacing assets.

With that in mind, let's make the body of our fairy (the glowing orb) using CSS only. It is a very simple shape, and the CSS is straightforward.

The wings however, are fairly intricate. While it would be fun to see if we could create them using CSS clip path, let's just use SVG for now. Simply select the wings you made in your graphics program and export them as SVG (export best practice here). I recommend exporting the top and bottom wings for each side as a group, like this. For our purposes, you can treat the file like any other image format.

Let's get coding!

The structure of the code really depends on how the fairy will be animated. If you re-watch the reference video, make a note of the animated elements: left and right side wings flap independently (top and bottom wings on each side move synchronously), glow expands and contracts, sparkles appear and disappear randomly, and fairies aimlessly hover. In short, animation applies to the left wing, right wing, glow, sparkles, and hover.

We will not be animated anything in part 1, but knowing which parts will be animated in part 2 will help us structure our code accordingly.

We are going to create separate elements for each animated piece of the fairy in our code, so that we can have independent control. We'll place a container around the entire fairy so that all the elements are grouped and can easily move together if needed. Let's name the container element fairy. Let's also create two div's inside our container named wings and glow.

  <div class="fairy"> <!-- container -->
  <div class="wings"></div> <!-- wings... obviously -->
  <div class="glow"></div> <!-- fairy body and glow -->
</div>

Center the fairy container

Pretty easy so far, right? Now let's add some CSS. Create a .fairy element for the container, adding height, width, and background-color properties. These three properties allow us to actually view the container on screen, making it easy to see what we are doing.

  body {
  background-color: #1E6128;
}

.fairy {
  width: 300px;
  height: 300px;
  background-color: #222;
}

As you can see, our container is in the upper left corner. We want to center the container in the middle of the screen. CSS Flexbox is a great way to do this, but we will be using position: absolute; instead.

We must set the top and left properties of .fairy to 50%, which means that the top left corner of our container is placed at the exact center of our page.

  .fairy {
  /* previous properties here */
  position: absolute;
  top: 50%;
  left: 50%;
}

We want the center of our container to be at the center of the page though, not the top left corner. There is a trick we can use. We will simply set margin-top and margin-left values of .fairy to be negative half the value of the height and width properties, respectively.

I am using shorthand for margin, but margin-top and margin-left would also work.

  .fairy {
  width: 300px;
  height: 300px;
  background-color: #222;
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -150px 0 0 -150px;
}

Fairy glow

We already wrote the code for centering elements, so lets copy from .fairy and paste into .glow, changing only a few values as needed.

We will also add a border-radius property and set it to 100px. This takes takes the default square shape of .glow and curves the borders so that we have a circle shape instead.

  .glow {
  width: 56px;
  height: 56px;
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -28px 0 0 -28px;
  border-radius: 100px;
}

Now it's time to add a background so that we can actually see our fairy's body.

Radial gradient

We will use CSS radial gradients to do this. You may already be familiar with a color gradient — one color transitioning to another color from a given direction.

Radial gradients also represent transitions between set colors, or color-stops, except that they emanate outward from a center point — like ripples from dropping a stone in water. You can read more about CSS gradients here.

Lets add a radial-gradient property to our .glow element. You can use the eye-dropper tool in your graphics program to pick out your fairy's colors from your reference image or assets.

  .glow {
  /* previous properties here */
  background: radial-gradient(
    rgba(255, 255, 255, 1) 0%,
    rgba(223, 126, 132, 1) 100%
    );
}

The code above transitions a set color in the center of an element (the center being 0%), to another set color on the outer edge of the element (100%). You may have noticed that I am using rgba color values instead of HEX.

This is because I have more precise control using rgba. For example, by adjusting the last value inside the rgba parenthesis in a range from 0 to 1, I can control opacity.

Let's make things more interesting

We can add more color-stops to radial-gradient; simply include more rgba values along with a percentage, separating each with a comma.

Changing the percentage before the comma will change the position of the color-stops relative to the center. The closer in value two percentages are to one another, the more abrupt the transition between the colors they represent.

Lets go ahead and add some more color-stops to our .glow element. The goal is to make it look as close to your reference image as possible.

  .glow {
  /* previous properties here */
  background: radial-gradient(
    rgba(255, 255, 255, 1) 0%,
    rgba(255, 240, 240, 1) 35%,
    rgba(255, 204, 204, 1) 50%,
    rgba(242, 148, 154, 1) 65%,
    rgba(223, 126, 132, 1) 100%
    );
}

Here is a comparison between my code and reference image (minus the outer glow). Looks very similar!

Outer glow

To add an outer glow to our fairy, we will be using the CSS box-shadow property. You can read more about the syntax here.

We will be adding two layers shadow to our .glow element. Using multiple layers, with different colors, gives our shadow some complexity and depth. You can add multiple shadows using one box-shadow declaration. The stacking order is top to bottom, so the shadows you declare first will be stacked on top of the ones you declare after.

Make sure you separate each new shadow with a comma, like below.

  .glow {
  /* previous properties here */
  box-shadow: 0 0 20px 5px rgba(239, 145, 145, 0.9), 
              0 0 30px 20px rgba(195, 66, 102, 0.8);
}

If you look closely at our result above, you'll notice that we get a slight gap between our .glow body and outer glow. To fix this, we are going to create a pseudo-element, :before, where we will declare our box-shadow property instead.

Pseudo-elements

A pseudo-element creates a "fake" element and inserts it before or after the content of the element that you have assigned it to. You can read more about pseudo-elements here.

To fix the previously mentioned gap, we will make the width and height properties of the .glow pseudo-element slightly smaller and update the negative margin properties to match.

Technically, the gap is still there on the .glow element, but since .glow:before is slighter smaller than .glow, the viewer sees the outer glow displayed on top of the gap, covering up the problem. You can see the comparison in the image below.

The outer glow is the red ring, our fix is shown on the right.

Here is how the code for .glow and its pseudo-element should look like so far.

  .glow {
  width: 56px;
  height: 56px;
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -28px 0 0 -28px;
  border-radius: 100px;
  background: radial-gradient(
    rgba(255, 255, 255, 1) 0%,
    rgba(255, 240, 240, 1) 35%,
    rgba(255, 204, 204, 1) 50%,
    rgba(242, 148, 154, 1) 65%,
    rgba(223, 126, 132, 1) 100%
    );
}

.glow:before {
    content: ""; /* You must always add this */
    width: 54px;
    height: 54px;
    position: absolute;
    top: 50%;
    left: 50%;
    margin: -27px 0 0 -27px;
    border-radius: 100px;
    box-shadow: 0 0 20px 5px rgba(239, 145, 145, 0.9), 
                0 0 30px 20px rgba(195, 66, 102, 0.8);
  }

Our result looks much better and there is no more visible gap!

Fairy Wings

Now it's time to make use of the SVG assets we exported at the beginning of this tutorial. The top and bottom wings for each side should be grouped, like this.

You will need to know the width and height of your SVG wings. You can find both values by opening your SVG file in a code editor and looking near the top. Take a look at the example image below, you will find the width and height values on line 2.

After viewing our SVG code, we know that width needs to be 64px and height needs to be 114px. We will repeat the margin trick we used to center the other elements, though we must make a slight change.

We don't want our wings to be exactly in the center, rather each wing should be positioned slightly off-center. That means the negative side margins should be greater than the wing width. For example, since my wing width is 64px, my negative side margin should be something like -72px.

The same thing goes for the top margin. Each wing side is comprised of a large upper wing and smaller lower wing. If we position the wing using its natural vertical center, the bottom wing would float in space.

You can see an example of this with the right wing below (the left wing position is our goal).

To account for this we are going to decrease the value of the top margin. Normally we would use -57px, which is half the wing height. Instead, we will use -87px, which raises the wing even higher, making it look like our reference image.

Let's add the wings via CSS, using pseudo-elements. I will use .wings:before for the left wing and .wings:after for the right wing.

  .wings:before, .wings:after {
    content: "";
    width: 64px;
    height: 114px;
    position: absolute;
    top: 50%;
    left: 50%;
    margin: -87px 0 0 -72px;
    background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/85486/wing-left.svg') no-repeat;
  }
.wings:after {
    left: auto;
    right: 50%;
    margin: -87px -72px 0 0;
    background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/85486/wing-right.svg') no-repeat,
  }

You may have noticed that I grouped .wings:before and .wings:after in the example above. This is because the assets and position of each wing is nearly identical, therefore the code is largely similar.

All we need to do for the right wing, or .wings:after, is add the property right: 50%, change the left negative margin to a right negative margin, and change the background image URL. We also need to add left: auto to reset the left property to the default state.

By structuring our code like the example above, we save ourselves from having to repeat four lines of code.

Let's take a look at our result (reference image on the left, code example on the right).

Perspective

If you take a look at the in-game reference images, you may have noticed that the fairies in Zelda have their wings angled backwards.

Let's finish up by giving our fairy a 3D perspective, so that its wings match the reference image.

We are going to do this by using the CSS perspective property. You can learn more here, but basically the perspective property gives the children of an element a 3D-space by affecting the distance between the Z plane and the user. The smaller the value of the property, the more exaggerated the effect will be.

Lets go ahead and add perspective: 250px; to our .fairy container.

  .fairy {
  /* the rest of the properties are still here */
  perspective: 250px;
}

So far nothing happens. This is because the perspective property simply defines the 3D-space for the children of the element.

To make the wings angle backwards, we will use the transform property on the children elements — in our case the .wings pseudo-elements.

  .wings:before, .wings:after {
  /* previous properties here */
  transform: rotateX(-15deg) rotateY(-45deg);
}
.wings:after {
  /* previous properties here */
  transform: rotateX(-15deg) rotateY(45deg);
}

You'll notice that our wings have rotated in the 3D-space, but they still look a bit strange. That is because they have rotated from their natural center. We need to add the transform-origin property, which we can use to customize the center point of rotation.

Wings act like hinges, the tip of the wing makes large movements, but the part that connects to the body is basically stationary.

For the left wing, we can use transform-origin: right; (because the right side of the left wing connects to the fairy's body) and transform-origin: left; for the right wing.

  .wings:before, .wings:after {
  /* previous properties here */
  transform-origin: right;
}
.wings:after {
  /* previous properties here */
  transform-origin: left;
}

Make sure you add transform-style: preserve-3d; to .glow. This property value specifies that child elements should preserve their own 3D transforms and it fixes a visual glitch on the .glow element for webkit browsers.

  .glow {
  /* previous properties here */
  transform-style: preserve-3d;
}

We can go ahead and remove background-color: #222; from .fairy since we no longer need it.

Let's combine all the code and see how it fits together. It should be similar to the code below, though certain values may be different depending on the assets you were working with.

The final code

  <div class="fairy"> <!-- container -->
  <div class="wings"></div> 
  <div class="glow"></div> 
</div>

  body {
  background-color: #1E6128;
}

.fairy {
  width: 300px;
  height: 300px;
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -150px 0 0 -150px;
  perspective: 250px;
}

.glow {
  width: 56px;
  height: 56px;
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -28px 0 0 -28px;
  border-radius: 100px;
  transform-style: preserve-3d;
  background: radial-gradient(
    rgba(255, 255, 255, 1) 0%,
    rgba(255, 240, 240, 1) 35%,
    rgba(255, 204, 204, 1) 50%,
    rgba(242, 148, 154, 1) 65%,
    rgba(223, 126, 132, 1) 100%
    );
}

.glow:before {
    content: "";
    width: 54px;
    height: 54px;
    position: absolute;
    top: 50%;
    left: 50%;
    margin: -27px 0 0 -27px;
    border-radius: 100px;
    box-shadow: 0 0 20px 5px rgba(239, 145, 145, 0.9), 
                0 0 30px 20px rgba(195, 66, 102, 0.8);
  }

.wings:before, .wings:after {
    content: "";
    width: 64px;
    height: 114px;
    position: absolute;
    top: 50%;
    left: 50%;
    margin: -87px 0 0 -72px;
    background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/85486/wing-left.svg') no-repeat;
    transform: rotateX(-15deg) rotateY(-40deg);
    transform-origin: right;

  }
  .wings:after {
    left: auto;
    right: 50%;
    margin: -87px -72px 0 0;
    background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/85486/wing-right.svg') no-repeat;
    transform: rotateX(-15deg) rotateY(40deg);
    transform-origin: left;
}

If you did everything correctly, your result should look something like this:

Congrats! We just made a Zelda: Breath of the Wild fairy!

If you have any questions, you can always get a hold of me on Twitter

Part 2 of this tutorial is coming soon. It will cover animation (and sparkles).

Thanks!


3,120 4 57