The Making of the CSS-Tricks Logo Easter Egg Animation

When Chris first started the CSS-Tricks redesign, he came to me with some screenshots of the direction it was headed in, and suggested that I make an animation for the logo as part of the design refresh. I was excited about this project, and my mind immediately started to shuffle through possible animation and interaction.

I’ve been working a ton with the GreenSock Animation Platform and SVG lately. If you are not aware of it, is worth checking out. It’s very good at performant animation, complex timelines, and cross-browser stability in animation, among other things.

It was around this time that Jack and Carl from GreenSock were mentioning to me that they were building out the MorphSVG Plugin, which can morph complex SVG shapes, even when the number of path points are not the same. I felt that this project would be a good candidate to learn this new (and pretty amazing) technology. Also around this time, Val Head came out with “Designing Safer Web Animation for Motion Sensitivity” in A List Apart, suggesting a toggle for UI animation that causes a lot of movement.

First Attempt

I was interested in incorporating the CSS-Tricks logo, the “wild-card selector” star, with some morphing behavior from the type logo itself. The “K” in tricks, being almost a half-star itself, became a good prospect for this shape’s morphing target.

One of the coolest things GreenSock has made with the MorphPlugin release is a helper plugin called findShapeIndex. This plugin helps you pick the best type of morph for your animation by letting you cycle through the tweening points, if you want to be particular about the way the shape morphs. The default is auto, which will work most of the time. But, when you want to have slightly more control, it’s a huge boon to be able to use this plugin to see all of the possible variations for these tweens. You load it up, point it from one ID to another: findShapeIndex("#hex", "#star");, and a nice GUI appears.

See the Pen findShapeIndex by Sarah Drasner (@sdras) on CodePen.

When I was first creating this, though, that plugin didn’t exist yet. So, I found that to really control the “in-between” state of the “K” and the star, I could make it look best if I designed a half-star, one that married the two states:

k star morph

When I encourage others to try their hand at animation, I find it important to stress that my animation projects don’t happen overnight, and I usually don’t just make one iteration of them to get to the final piece. This project was no exception. My first iteration was much more pared-down (hit the replay button to see the animation):

See the Pen CSS-Tricks Logo First Attempt by Sarah Drasner (@sdras) on CodePen.

I had the star and “K” morph, but originally thought to morph the star into a few dots that spun using motion along a path with the Bezier Plugin, built into TweenMax. The star circles rotate by using very simple array of path points:

bezier = [
  { x: 0, y: 0 }, 
  { x: 0, y: 20 }, 
  { x: 20, y: 20 }, 
  { x: 20, y: 0 }, 
  { x: 0, y: 0 }
];

Then, when I set the curviness to 2, I get a full circle. The demo below shows that by increasing the curviness, you can surpass the point where a simple curve is created, and that 2 is optimal for rounding between four points.

tl.staggerTo(dS, 0.1, { bezier: {
    type: 'thru',
    values: bezier,
    curviness: 2
  }, opacity: 0, 
     ease: Linear.easeInOut }, 0.06);

See the Pen Demo for Curviness in GreenSock Bezier by Sarah Drasner (@sdras) on CodePen.

For further reading on how motion along a path works in GreenSock, please refer to this David Walsh Blog article.

Rather than have the “K” just appear again, I thought it would make a nice entrance to draw it back on the screen again with its own stroke.

drawSVG: '50% 50%' makes the whole stroke look like it doesn’t exist yet because both sides of the stroke are fully collapsed. I can then set the fill from none to black slightly after it.

tl.fromTo(k2, 0.8, {
   drawSVG: '50% 50%'
 }, {
   drawSVG: true,
   ease: Power4.easeOut
 }, "begin+=1");
 tl.fromTo(k2, 1, {
   fill: "none"
 }, {
   fill: "black",
   ease: Power4.easeOut
 }, "begin+=1.6");

But after all of this, I was a little unsatisfied with the outcome. It worked, but lacked some… energy and… pizzaz? I asked Chris for some art direction, and he suggested the characters morph into “CSS-TRIK*Z” and back again.

Further Design Refinements

Working from what I learned in the original attempt, I decided that I really liked the “K” and the star. I then decided to take that a step further, with many characters in motion. The “C”, “K”, and “S” at the end all clearly needed to move in order to become other characters. Working from the star, I started to sketch:

I find that ugly storyboards save me a lot of time. It takes me 15 seconds to jot down these visual notes to myself, and then when I head to Illustrator to flesh out my ideas, I have a gameplan. One of the most fundamental things I’ve learned in designing for animation is that it makes a lot of sense to design everything, and then unveil elements slowly.

By working within a tech theme, it was pretty easy to notice correlations in the letter shapes with possibilities for their morphing counterparts. I explored icomoon.io and saw a link icon that looked a little like an “S” if I just rotated it, and the GitHub fork icon that resembles a “T”, and all I had to do when I placed them in the Illustrator file was tweak the path data to match the widths and heights of the lettering in the logo.

To complement the star, I wanted something planet-like. I considered that the rings on Saturn could then turn into the CSS bracket, which also had potential to look like a “K”. The “S” and “Z” were a pretty straightforward flipping morph, but when I really considered the “Z”, it had the potential to look like code brackets: </>. I originally had some typing of a tag happen in between the brackets, but it slowed all of the animation down to accommodate that movement and was a little too busy, so I cut it out. Again, all of my animations go through many stages of iteration, and sometimes I’ll spend a while creating and coding something that doesn’t make the cut. Hey, that’s show business.

As I work along in Illustrator, I’ve made sure to properly name all of the pieces I’m working with so I can find my way in the code.

layers in illustrator

The original logo was converted to outlines, but had it not been, I would need to conver it to work with the path data for each letter. You may find that something like a logo is a compound path that’s hard to manipulate. In this case, you can draw a simple line through it in between the letters, open the pathfinder window, and choose “divide”. This should split the paths apart for you so you can use them each separately.

When exporting the full SVG, I optimize my code so that it’s not bloated and it’s easy to read. I usually use Jake Archibald’s SVGOMG, built from the very powerful terminal-based SVGO, but with a wonderful and helpful GUI so you can see what you are exporting. Since I spent all that time naming each path, and placing them in understandable groups, I make sure to uncheck “clean IDs” and “collapse useless groups”, otherwise I would strip away all of the identification of these path attributes.

Final Animation Development

The morphing itself is incredibly simple due to GreenSock's plugin. You can point from one ID to another and it does the heavy lifting for you:

tl.to(k, 0.2, {
    morphSVG: {
      shape: hstar
    },
    ease: Linear.easeNone
  });

While working along on this, it made a lot of sense to use a relative label in the timeline:

tl.add("begin");

That way I can make sure all of the animation begins at the same point in the timeline. It also means I can keep track of around when they should all finish their journey. In this case, you can see it all ends around 2.5 in, or "begin+=2.5".

I set the whole animation in the timeline to be much slower while I’m working so that I can really analyze every single movement—easy to do with one line of code:

tl.timeScale(0.4);

Then, when I have all the movements down, I can adjust this until I have a timescale that I think works well for the motion, in this case:

tl.timeScale(1.2);

I usually don’t use linear eases that much in my animations, but morphing tends to look really nice with a smooth linear ease. To visualize the different types of eases available, check out this great Ease Visualizer tool. I use “back” type eases in this sparingly, to accent to the linear ease and call attention to moments that I want to take precedence and call out in a slightly exaggerated way—in this case, the ring of Saturn morph, or the final star morph. (Click the orange toggle in the dash to activate the animation. You may need to hit rerun first.)

See the Pen Animated CSS-Tricks Logo by Sarah Drasner (@sdras) on CodePen.

One thing that’s vital for this to work is to make sure the things I’m initially hiding don’t show up, even momentarily, until I’m ready for the user to see them. So I set all of the path attributes that will be eventually shown to visibility: hidden; in the CSS, then set them back to visible in the JavaScript with TweenMax.set. This ensures that they stay hidden until the JavaScript is loaded and I can hide the elements with the animation logic.

I store any ID I use more than once in the animation as a variable to prevent multiple lookups. I can also store the path data from a letter if I need to use it a few times in the course of the animation, such as when I need something to tween to the same shape more than once:

var tPath = t[0].getAttribute("d");

The really cool thing about using GSAP’s timeline is that in order for the whole animation to be played backwards and forwards upon something like a click interaction, I can pause the timeline initially, then play the timeline for the first iteration, and reverse it for the alternate.

$(dotActive).on("click", function() {
  if ($(this).hasClass("active")) {
    $(this).removeClass("active");
    master.reverse();
  } else {
    $(this).addClass("active");
    master.play();
  }
});

That's jQuery using jQuery 3.0, which is the first version of jQuery to support SVG class operations. I also have a separate tween for the hover, to alert the viewer to the fact that the toggle works as a trigger, with a function called bloop for the popping repeated circle, as well as scale that works on mouseenter.

function bloop() {
  var tl = new TimelineMax();
  tl.fromTo(dotExpand, 0.75, {
    scale: 1,
    opacity: 1
  }, {
    scale: 5,
    transformOrigin: "50% 50%",
    opacity: 0,
    repeat: 3,
    ease: Sine.easeOut
  });
  return tl;
}

var hoverOn = new TimelineMax({
    paused: true
  }),
  dash = $("#dash");
hoverOn.add(bloop());

$(dotActive).on("mouseenter", function() {
  dotExpand.removeClass("hide");
  TweenMax.to(dash, 0.3, {
    scale: 1.3,
    transformOrigin: "50% 50%",
    ease: Sine.easeOut
  });
  TweenMax.to(dotActive, 0.3, {
    scale: 1.3,
    transformOrigin: "50% 50%",
    ease: Sine.easeOut
  });
  hoverOn.restart();
});

The dot had to move back and forth between the toggle, but the forward and reverse of timelines lent itself well to that kind of behavior. I simply added the dot moving across in the master timeline, so that when it reversed on the second state, it retraced its steps across the dash, indicating where we were in the state of the animation.

And there we have it! A nice “Easter egg” logo morph animation.