Fluttering with style

Explore SVG syntax and you are able to draw close to anything directly in a text editor. Lean on CSS and then, then you can almost learn how to fly.

To fulfill this natural premise, let’s create a mascot with a country good look.

SVG syntax

Body

Within the context of an <svg> element you have access to a few helper constructs. Elements such as <rect> and <circle> to draw the matching figures with a convenient set of attributes.

Rectangles have a width and a height.

<rect width="10" height="10" />

Circles have a radius.

<circle r="5" />

To get started, however, draw a round-ish shape with an <ellipse>.

<svg viewBox="-60 -60 120 120">
	<ellipse rx="28" ry="25" fill="#f9f5d0" />
</svg>

Realizing a wider-than taller base.

Legs

Let’s support the body with a couple of legs. In this context, there is no element more flexible than <path>.

Within the boundaries of the d attribute you instruct the browser how to draw. Lines, curves, arcs — the sky is the limit.

To ease into the syntax, begin with only straight lines.

<path d="M 0 0 l 0 7 l 7 7 l -5.5 -3 l -1.5 3 l -1.5 l -3 -5.5 l 3 7 -7" />

Don’t be deterred by the long sequence of characters. Break down the steps and the instructions become elementary:

  • start from (0, 0) — M 0 0

  • draw a line 7 units down — l 0 7

  • continue drawing lines per the pair of coordinates; first 7 to the right and 7 down, then 5.5 left and 3 up…

d=""

Conveniently, you need to specify just the first l character. The browser assumes the same directive until told otherwise.

<path 
	d="M 0 0 l 0 7 7 7 -5.5 -3 -1.5 3 -1.5 -3 -5.5 3 7 -7" 
	fill="#fd971b" 
/>

With a highly saturated fill and the stand is close to complete.

We can’t see the first line, however. Also, the sharp edges are at odds with the overall theme, they clash with the softer base.

By default <path> elements don’t have a stroke. Beef up the shape with a solid outline, and a couple of stroke-related attributes help to round up the visual.

<path
	d="M ..."
	fill="#fd971b"
	stroke="#fd971b"
	stroke-width="4"
	stroke-linecap="round"
	stroke-linejoin="round"
/>

A perfect way to match the ellipse.

Of course you need two legs, and you want them below the ellipse. To solve the first issue, define the shape in a <defs> element.

<defs>
	<path id="leg" d="M 0 0 l 0 7 7 7..." />
</defs>

To draw the lines back, reference the id attribute in a <use> element instead.

<use href="#leg" />

Adding a couple instances is a matter of repeating the element. Now on the left, now on the right.

<use href="#leg" x="-12" />
<use href="#leg" x="12" />

Wrap the legs in a group element, then, and you are able to tackle the second problem as well.

Indeed, the <g> element is a fitting place for common attributes — fill, stroke and the like — but also a convenient element to move the legs in the bottom half of the canvas.

<g 
	transform="translate(0 17)"
	fill="#fd971b"
	stroke="#fd971b"
	...>
	<!-- ...<use> -->
</g>

Add the group before the body, drawing the legs effectively behind, and half of the picture is already done.

Head

The head offers a good opportunity to explore the d attribute further. Introduce a <path> slightly off-center, to the left.

<path d="M -20 0" />

The moment you want to level up from straight lines to draw curves you have access to a couple of most pertinent instructions: q and c.

Use q and you draw a quadratic bezier curve, connecting the start and end coordinates with the help of a control point.

Drag the handles to move the control point.

d="M x0 y0 q 20 24 x1 x2"

Prefer c and you realize a cubic bezier curve, linking the positions with the guidance of two control points.

Drag the handles to move the control points.

c1
c2

d="M x0 y0 c -18 24 58 24 x1 y1"

Both strings outline the bone structure for the mouth, but the more complex syntax emulates a couple of round cheeks as well.

<path d="M -20 0 c -18 24 58 24 40 0" />

In the d attribute curves are perhaps the most complex command, so let’s complete the upper half with a simpler instruction. Wrap up the head with a semicircle back to the origin.

<path
	d="
    M -20 0 
    c -18 24 58 24 40 0
    a 20 20 0 0 0 -40 0
    "
/>

There’s a plethora of numbers for the a character, but to draw a semicircle you can get by understanding only a few of them.

The first pair describes the radius of the arc in either dimension, while the last couple of numbers points to the end coordinate.

Have the radii exactly half as the distance covered and the connection is complete.

In between the numbers you have three boolean values to toggle a few options. For the sake of a semicircle, you might need to change only the very last one, drawing the semicircle in a clockwise, or counter-clockwise, manner.

d="... a 20 20 0 0 0 -40 0"

Enough logic, however. Add a noticeable, rounded outline as with the legs.

<path
	d="M ..."
	fill="#f9f5d0"
	stroke="#f9f5d0"
	stroke-width="4"
	stroke-linecap="round"
	stroke-linejoin="round"
/>

Move the <path> up in a more fitting position.

<!-- ...body -->
<g transform="translate(0 -30)">
	<!-- <path> -->
</g>

And the composition assumes a new identity.

Face

The face is tightly coupled with the <path> element for the head, so it is reasonable to have the elements in the same, translated group.

<g transform="translate(0 -30)">
	<!-- <path> -->
	<!-- ...face -->
</g>

Two, dark circles for the eyes.

<g fill="#542b19">
	<circle r="2.75" cx="-12" />
	<circle r="2.75" cx="12" />
</g>

Two <path>s for the beak and more evident tongue. These share a common, rounded stroke.

<g stroke-linecap="round" stroke-linejoin="round">
	<!-- ...<path> -->
</g>

But distinguish themselves in terms of color and the stroke thickness.

<g fill="#fd971b" stroke="#fd971b" stroke-width="3">
	<!-- beak -->
</g>
<g fill="#fc531c" stroke="#fc531c" stroke-width="1">
	<!-- tongue -->
</g>

And in terms of d attribute, the two provide an excellent excuse to practice with cubic bezier curves.

<g fill="#fd971b" stroke="#fd971b" stroke-width="3">
	<path d="M -9 0 c 6 6 12 6 18 0 -7 -5 -11 -5 -18 0" />
</g>
<g fill="#fc531c" stroke="#fc531c" stroke-width="1">
	<path d="M -6 0 c 3 4 9 4 12 0 -4 1 -8 1 -12 0" />
</g>

The beak is slightly thicker, the curvature is wider, adding a bit of nuance.

Place both elements slightly below the eyes.

<!-- head -->
<!-- eyes -->
<g transform="translate(0 8)">
	<!-- beak and tongue -->
</g>

And the face of the ever more adorable chicklet is complete.

Wings

Wings allow you to rehearse almost every concept included so far.

Create a single wing protruding to the right with a couple of tentative curves.

<path 
	d="M 0 0 c 6 -5 14 -8 20 0 -5 5 -2 10 -12 10 -2 0 -8 0 -8 -10" 
	fill="#f0d584" 
/>

Leading to a rather rugged visual.

Add the now familiar and even more important stroke.

<path
	d="M ..."
	fill="#f0d584"
	stroke="#f0d584"
	stroke-width="8"
	stroke-linecap="round"
	stroke-linejoin="round"
/>

And with the rounded joints, with the smooth edges the shape looks right at home.

What is more, the numbers no longer seem arbitrary, but the result of a careful design. The realization of a full-fledged artist.

Similarly to the legs, define the shape in the <defs> block.

<defs>
	<!-- leg -->
	<path id="wing" d="M 0 0 c 6 -5 14 -8 20 0 -5 5 -2 10 -12 10 -2 0 -8 0 -8 -10" />
</defs>

Add a couple of instances with <use> elements.

<g 
	fill="#f0d584"
	stroke="#f0d584"
	stroke-width="8"
	stroke-linecap="round"
	stroke-linejoin="round">
	<!-- wings -->
</g>

The one on the right is good as-is, and would require only an horizontal offset.

<g transform="translate(25 0)">
  <use href="#wing">
</g>

For the one on the left, however, we want the wing to point in the opposite direction. Instead of creating an entirely different <path>, the trick is to lean on SVG transformations. Scale the shape horizontally and with a negative value.

<g transform="scale(-1 1)">
  <use href="#wing">
</g>

And the coordinate system for the wing is essentially flipped. The instructions previously drawing the curve to the right compose the shape to the left.

Similarly, the offset moves the instance westward, away from the center.

translate(25 0)scale(-1 1) translate(25 0)<use href="#wing">

There’s an added benefit to the setup. As you translate the shapes — refer to the black dot in the visual — any transformation applied on the wings takes place from the new spot.

Rotate the wing on the right and the element tips downwards.

<g transform="translate(25 0)">
  <use transform="rotate(30)" href="#wing">
</g>

Rotate the mirror image on the left and the effect is one and the same.

<g transform="scale(-1 1) translate(25 0)">
  <use transform="rotate(30)" href="#wing">
</g>

With the negative scale and the flipped coordinate system, you don’t even need to consider a rotation in the opposite direction.

translate(25 0)scale(-1 1) translate(25 0)<use href="#wing" transform="rotate(30)">

Transformations are likely one of the most puzzling sections when learning SVG, and certainly one of the most difficult sections to convey. Understanding the topic, however, opens a world of possibilities.

Back to the composition. Include the wings before the body, slightly above the center of the ellipse.

<!-- legs -->
<g transform="translate(0 -10)">
	<!-- ...wings -->
</g>
<!-- body -->

Rotate the elements 90 degrees to hide them for a brief moment and the mascot is ready to spring into action.

Crest

If there is one thing for which you can blame is trying to be exceesively symmetrical. Admittedly, I enjoy drawing most vector graphics in horizontal harmony, but there is a case for asymmetry.

Case in point, take a misshapen <path> element.

<path d="M 0 0 c 5 0 11 -2 11 -5 0 5 2.5 5 5 3 0 5 -10 7 -16 2" />

Add the staple stroke to mask imperfections.

<path
	d="M ..."
	fill="#f9f5d0"
	stroke="#f9f5d0"
	stroke-width="3"
	stroke-linecap="round"
	stroke-linejoin="round"
/>

Hide the shape behind the head, slightly off center, and the result is striking. A small touch to add flavor and a twist to the character.

CSS animations

Blink

The mascot is unquestionably adorable, but the fixed gaze is bound to unnerve after a short while.

Consider the markup for the eyes, for instance, the right one.

<circle r="2.75" cx="12" />

We want to scale the element, but unfortunately, it is not as simple as to change the size through the transform property.

circle {
	transform: scale(0.5);
}

The issue links back to the discussion around the <path> elements devoted to the wings, and is also a chance to gingerly propose an article on the topic of SVG transformations. That being said, let me try to summarize the problem in a few words.

Within the bounds of an <svg> element, transformations are applied from an origin first described by the viewBox. A fireproof way to update this origin is through the transform attribute, for instance with a translation — just as we positioned the shapes around the canvas.

<g transform="translate(0 -30)">
	<!-- head -->
</g>

Scale the circles in their current state, and the transformation takes place from the center of the head, not the center of the elements. To fix visual mishap, remove the cx attribute in favor of an earlier translation.

-<circle r="2.75" cx="12" />
+<g transform="translate(12 0)">
+	<circle r="2.75" />
+</g>

Thankfully, that’s the most drastic change you’ll need in terms of SVG syntax. Add a class to the now-centered eyes.

-<circle r="2.75" />
+<circle class="blink" r="2.75" />

And let’s finally work with CSS. A fitting animation would have the eyes disappear ever so briefly, but give enough time between iterations.

Structure the keyframes to rapidly scale the elements into nothing, before returning back to the initial scale.

@keyframes blink {
	30%,
	34% {
		transform: scaleY(0);
	}
	28%,
	32%,
	36% {
		transform: scaleY(1);
	}
}

Targeting only the y axis should work to give the impression of the eyes actually closing.

Through the class selector, then, point to the animation with your favorite easing.

.blink {
	animation: blink 4s cubic-bezier(0.37, 0, 0.63, 1) infinite;
}

The rapid succession means the choice is less than impactful, but there’s always a place for a sine function.

Jump

Continuing with more evident changes, the goal is to move the mascot down as if flexing its muscles and then up in a small, but evident, leap.

Wrap the wings, body and head in a common group element for the translation. Not the legs as you want to give the impression of the animal approaching the ground.

<!-- legs -->
<g class="jump">
	<!-- wings -->
	<!-- body -->
	<!-- head -->
</g>

Define the animation to implement the desired motion.

@keyframes jump {
	0%,
	45% {
		transform: translateY(0px);
	}
	55% {
		transform: translateY(5px);
	}
	70%,
	90% {
		transform: translateY(-2px);
	}
	95% {
		transform: translateY(1px);
	}
	100% {
		transform: translateY(0px);
	}
}

Honorable mention goes to the penultimate percentage, nudging the group ever so lightly before the resting state.

Finally, pair the animation through the dedicated class.

.jump {
	animation: jump 4s cubic-bezier(0.37, 0, 0.63, 1) infinite;
}

You are free to explore different durations and easing curves, but the same values chosen for the eyes work to create the incrementally impressive effect.

Flap

To complete the visual let’s work on the wings. These are already structured so that rotation takes place from their respective origin, meaning you need just the class selector.

<use class="flap" href="#wing" />

To animate the pair.

.flap {
	animation: flap 4s cubic-bezier(0.37, 0, 0.63, 1) infinite;
}

By repeating the duration of the previous animation we are able to follow the steps of the jump.

Frame the element to preserve the initial, tucked-in rotation. Up until the leap is about to take place.

@keyframes flap {
	0%,
	60% {
		transform: rotate(90deg);
	}
}

And then have fun detailing as many flaps as you can possibly tolerate.

@keyframes flap {
	0%,
	60% {
		transform: rotate(90deg);
	}

	65%,
	75%,
	85% {
		transform: rotate(0deg);
	}

	70%,
	80%,
	95%,
	100% {
		transform: rotate(90deg);
	}
}

Ready to be delighted?

Finishing touches

Do not worry, the animation is done, and the article is close to being over.

What is left is reaping the rewards of such a long, laborious journey. What is left is up to your imagination and your sense of whimsy. Shorten the duration, experiment with a different bezier curve, be it in the d attribute or through the animation-timing-function property.

Personally, I decided to indulge in one additional animation. This one traces the jump to have the entire mascot, legs included, move a smidge upwards and then down as the chicklet hits the ground. You’ll be the judge if the extra bounce is worth the effort.