hop.ie

Zelda: A link to the CSS

April 20, 2013

A month ago I posted about making an iPhone using CSS. This month I thought I’d take some of the lessons from that and push it a little further.

Full code used here is available on Github and makes use of these SASS mixins.

Zelda: Linked to the Past

This week, a new release of the popular Zelda game franchise was announced for the Nintendo 3DS. It is to be set in the world of “A Link to the Past”, a game that was released in the early 90’s on the Super Nintendo platform.

It was always my favourite game when growing up, so what better way to celebrate than to recreate part of the opening sequence to the old SNES game using nothing but HTML and CSS?

The idea was to create something that would look like this:

Zelda: A link to the CSS

If you’re running a modern version of Chrome, Safari or Firefox, check out the live demo.

Getting started: HTML

To get started, I created the HTML structure to which I’d be applying the styles. The layout requires three 3D triangles, and some text to sit on top. I began with a single triangle:

<div class="stage">
  <div class="triangle">
    <figure class="front"></figure>
    <figure class="back"></figure>
    <figure class="side1"></figure>
    <figure class="side2"></figure>
    <figure class="side3"></figure>
  </div>
</div>

The front and back pieces needed to be triangular, with 3 rectangles covering the left, right and bottom sides.

As luck would have it, I happened across a great triangle SASS mixin recently. It works by applying borders the full width of the object and where the borders intersect, they create a 45% line. Handy. Applying this to the front figure above, we get something like this:

SASS:
+arrow(150px, 150px, up, #ffe403)

CSS:
{
  border-left: 75px solid transparent;
  border-right: 75px solid transparent;
  border-bottom: 150px solid #ffe403;
}

I’ve included the processed CSS in the above example to illustrate how borders are being used to generate the shape.

Setting the stage

Once we have the shape in place, we’ll need to place a back piece and position them both in 3D space. A containing “stage” div is needed to achieve this. We can apply a perspective value to the stage and any objects on it will be then drawn in relation to each other.

.stage
  @include perspective(800px)
  @include perspective-origin(120% -200px)

figure.front
  +arrow(150px, 150px, up, #ffe403)
  @include transform(translateZ(80px))

figure.back
  +arrow(150px, 150px, up, #ffd200)
  @include transform(translateZ(-80px))

The 3D positioning is achieved by using the translateZ transform. The front face is brought forward 80 pixels and the back face pushed back 80px.

I also rotated the back panel 180 degrees on the Y axis so that it now faces out toward the back.

The perspective value relates to the depth of the scene, and larger values produce more extreme distortion. Similar to this, the perspective-origin value is used to move the viewpoint. In this case, the viewpoint is looking down from 200 pixels up, and from off to the right side of the stage.

Sides

With the front and back in place, there are gaps to fill on the sides. To fill these, rectangular divs can be positioned like so:

figure
  &.side1
    content: " "
    display: block
    position: absolute
    height: 168px
    width: 40px
    background-color: #ffd200
    @include transform(translateY(-9px) translateX(18px) rotateY(-90deg) rotateX(26.5deg))

  &.side2
    content: " "
    display: block
    position: absolute
    height: 168px
    width: 40px
    background-color: #ffd200
    @include transform(translateY(-9px) translateX(92px) rotateY(90deg) rotateX(26.5deg))

  &.side3
    content: " "
    background-color: #ffd200
    width: 150px
    height: 40px
    @include transform(translateY(130px) rotateX(-90deg))

One small adjustment made in this example was to rotate the back panel 180 degrees to ensure it was facing outwards. With the back-faces hidden, it would have been invisible otherwise.

Three triangles

With one triangle done, it was straightforward to position three of them on the stage. I copied the div called “triangle” above, and replicated it three times. I then added a class “one”, “two” and “three” to each so they could be referenced individually:

.triangle
  @include animation(rotate 5s infinite linear)

  &.two
    top: 150px
    left: 0

  &.three
    top: 150px
    left: 150px

Styling the text

To create the proper Zelda atmosphere, the right fonts were needed. For the main font, EB Garamond seemed a decent choice. The smaller text looks good with Old Standard TT.

Including these fonts involves referencing them in the head of your HTML:

<link href='http://fonts.googleapis.com/css?family=EB+Garamond' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Old+Standard+TT' rel='stylesheet' type='text/css'>

You can then use them in your CSS:

h1
  font-family: "EB Garamond"

The finishing touch was to add a border around the text:

ZELDA

.zelda-style
  font-family: "EB Garamond"
  @include text-fill-color(#b62f22)
  @include text-stroke-color(#8b3536)
  @include text-stroke-width(2px)
  margin: 10px auto

Bringing it together

To achieve the animation I put together a few different animations and timed them so that they all worked together. Using keyframe animation in CSS is made much easier by using SASS, and I used a few basic mixins to make the process easier. I’m using Sass with the Compass framework, but to add support for animation and keyframes to Compass, you’ll need this gist.

Animation 1: Rotating

The spinning effect is creating using the following keyframes:

@include keyframes(rotate)
  0%
    @include transform(rotateY(0))
  100%
    @include transform(rotateY(360deg))

To apply this animation to the figures, the following SASS is used. Note, it makes use of the “animation” mixin found here:

figure
  @include animation(rotate 5s infinite linear)

This calls the “animation” mixin and passes in the name of the keyframes (rotate), the duration of 5 seconds, repeated forever. It also specifies that the animation easing be “linear”, which maintains a constant speed throughout the animation.

Animation 2: Tumbling triangles

The introduction of the figures sees them tumble into place. This is done by transitioning them from an initial position offset to the top, left or right, while applying 360 degrees of rotation. These combine in the following keyframes:

@include keyframes(introduce-1)
  0%
    top: -200px
    opacity: 0
  45%
    top: -100px
    @include transform(rotateY(180deg) rotateX(-180deg))
    opacity: 1
  90%, 100%
    top: 0
    @include transform(rotateY(360deg) rotateX(-360deg))

By putting the ending position of the animation at both 90% and 100%, it ensures that the animation stays still for a moment at the end of it’s tumbling.

Applying both these to the figures at the same time looks like this:

@include animation(rotate 5s infinite linear, introduce-1 5s linear)

Animation 3: Text

The text overlay is invisible at first, and I wanted it to wait until after the tumbling animation had finished before fading into place. To achieve this, the following keyframes apply to the text:

@include keyframes(showZeldaText)
  0%, 80%
    @include transform(scale(0.6) translateZ(-100px))
    opacity: 0
    -webkit-filter: blur(2px)
  90%
    -webkit-filter: blur(0)
  100%
    @include transform(scale(1) translateZ(0))
    opacity: 1

For 90% of the time, the text is invisible. Then in the last 10% the text scales up from 60% scale to 100%, while the opacity changes from invisible to visible.

Since the text animation starts at the same time as the tumbling, it needs a longer time to run so that it lines up properly. In this case, I set it to run for 6.5 seconds:

@include animation(showZeldaText 6.5s linear)

With these in place, the last animation is the flash of background colour when the tumbling stops.

Animation 4: Background flash

The background starts black, then when the tumbling finishes, briefly flashes white before the background image is shown. This colour sequence is also timed to coincide with the appearance of the text. The keyframes:

@include keyframes(animate-background)
  0%
    background: #000
  83%
    background: #000
  84%
    background: #fff
  87%, 100%
    background: #000

A similar set of keyframes is used to show the background image after the screen flashes.

See it in action

Here’s a live demo. It works reasonably in recent versions of Chrome, Safari and Firefox.

Have a go

The HTML and CSS is available on Github. Please fork, share, and have a play with it. If you’d like to add to or fix something, pull requests are a fast way to becoming one of my favourite people. Do it!

Issues

IE10 support is basic, so any browser support improvements would be a good thing. There’s also a bit of a glitch where the tumbling animation on the elements doesn’t always start. It seems to happen in all browsers. Any suggestions on fixing that would be appreciated.

I hope you enjoyed this post. I am more than happy to respond to questions and suggestions on Twitter at @donovanh.