Animating the stroke of an SVG is perfect for simulating handwriting. Over the course of two tutorials we’re going to use CSS animation to make a signature neatly appear to be written, as though you’re signing the page yourself.
Here’s what we’ll be building:
1. SVG File
Before we dive into any code, we’re going to need an SVG version of your signature. It doesn’t matter what software you use to make this, but try to keep the lines and curves as smooth as possible for best effect.
Here’s mine, which you can see is drawn with three separate paths:



Make sure your artboard is cropped tightly to the signature, then save the file as an SVG.
2. Tidying the SVG Code
Opening the file in a code editor will reveal the SVG’s XML structure. Depending on which application you used to design it, plus how you saved or exported it, you’ll have an <svg>
element with some mumbo jumbo before it. The mumbo jumbo can be removed.
In this case, the elements we’re left with look something like:
<svg> <line/> <path/> <line/> </svg>
Within our main <svg>
we have a <line>
, then a <path>
, then another <line>
. These are the three vectors we drew, differentiated only because, technically, a line has no curvature, so it’s defined differently to a path in SVG.
3. Add Classes
We’ll need to separately target these vectors with CSS a little later on, so make sure they each have a suitable class name. The <svg>
element will likely already have an id reflecting the layer name in the application it was designed with.
<svg id="signature"> <line class="stroke-I" /> <path class="stroke-an" /> <line class="stroke-flourish" /> </svg>
I’ve given my vectors class names depending on what they are (the first one is the “I” in my name, for example).
4. All the Other SVG Attributes
In fairness, your SVG won’t be looking quite this neat. Each of these vectors will have a load of coordinates, plus several attributes buried within them. The coordinates will need to stay, but we can remove some of the commonly-used attributes and place those in our CSS instead, keeping things nice and DRY.
New Project
I’m going to build this using CodePen, but you can use standalone HTML and CSS documents if you prefer. Paste the SVG code directly into your HTML document. Then, remove the attributes each of the path and line elements have in common, placing them instead in the CSS document. For example, you’ll notice attributes like:
-
fill="none"
-
stroke="#0F436D"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
stroke-miterlimit="10"
These can be removed and applied via CSS instead, like so:
path, line { fill: none; stroke: #2a3745; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 10; }
Much cleaner!
5. Begin Animating
In order to animate the strokes of this SVG we’re going to be using a technique first discussed by Jake Archibald. The idea is as follows: each of these vectors is going to be given a dashed stroke. We do this by applying a stroke-dasharray
value within the CSS:
Dash Length
For each of these vectors we make the stroke-dasharray
precisely the length of the path, so each one has a single dash covering its entire length. This takes a bit of trial and error, but in our case the values look look like this:
.stroke-I { stroke-dasharray: 80; } .stroke-an { stroke-dasharray: 360; } .stroke-flourish { stroke-dasharray: 40; }
Now, in order to animate these strokes, we need to offset each of the dashes so that the gap covers the vector, not the dash. Does that make sense? These illustrations might help. In this first one, imagine the dashed line is being used to cover the flourish at the end of the signature.

Now in this one we’ve offset the dash, so it’s the gap which is over the flourish:

Now all we need to do is use CSS to animate from the offset state to the other.
6. Keyframes
CSS animation relies on first defining keyframes. Each keyframe represents states along a timeline, then our browsers render the animations between them.
Let’s first see how this dash offset can be animated. We’ll use the first stroke, the “I”, and animate between two states. Begin by setting up some keyframes:
@keyframes write1 { 0% { stroke-dashoffset: 80; } 100% { stroke-dashoffset: 0; } }
Here we give the keyframes a name (write1
) and using shorthand syntax specify that at the very beginning of the timeline (0%
) we want the stroke-dashoffset
to be 80
. In other words: the dash, which is exactly 80px long, will be offset completely.
At the end of the timeline (at 100%
) we want the stroke-dashoffset
to be 0
, so the dash is once more covering the vector.
Apply Animation
Now we have our keyframes, let’s attach them to an animation. We add another declaration to our stroke-I
rule:
.stroke-I { stroke-dasharray: 80; animation: write1 3s infinite linear; }
Here, using the animation
property, we say that we want to use the write1
keyframes defined a moment ago, we want the whole thing to last exactly 3
seconds, we want the animation to loop infinitely and we want the speed to be linear
(so that there’s no acceleration or deceleration).
Here’s what we get:
Note: I’m using Autoprefixer in CodePen which saves me having to use browser prefixes on the animation stuff.

Apply to All Three Vectors
We need to define two more sets of keyframes (write2
and write3
) for the remaining vectors in the signature–and we need to offset by the correct dash lengths we discovered earlier:
@keyframes write2 { 0% { stroke-dashoffset: 360; } 100% { stroke-dashoffset: 0; } } @keyframes write3 { 0% { stroke-dashoffset: 40; } 100% { stroke-dashoffset: 0; } }
Then we need to apply those animations to the remaining two vectors:
.stroke-an { stroke-dasharray: 360; animation: write2 3s infinite linear; } .stroke-flourish { stroke-dasharray: 40; animation: write3 3s infinite linear; }
Here’s what we get:
Now we’re getting somewhere! Each vector is animating perfectly, in a linear motion lasting 3 seconds.
Next step? To get them animating in sequence.
7. Sequential Animation
Currently we have three strokes all animating simultaneously. However, we ideally want the “I” to animate, then the “an”, then finally the flourish at the end. If we were to visualise that along a timeline it might look like this:

We can actually represent these sections of the timeline perfectly in our CSS keyframes. For example, the first section (from 0% to 33.3%) is when we want our “I” to animate, so we alter the keyframes to finish at 33.3% instead of 100%:
@keyframes write1 { 0% { stroke-dashoffset: 80; } 33.3% { stroke-dashoffset: 0; } }
Now, given that all three of our animations are the same length (3 seconds) we can make sure the second doesn’t start until 33.3%, when the first animation is complete:
@keyframes write2 { 0%, 33.3% { stroke-dashoffset: 360; } 100% { stroke-dashoffset: 0; } }
Here’s what that gives us:
Completing the Sequence
The first two animations flow nicely, so let’s improve things by getting the second to finish at 66.6%, at which point the final animation can start. Our keyframes will look like this:
@keyframes write1 { 0% { stroke-dashoffset: 80; } 33.3% { stroke-dashoffset: 0; } } @keyframes write2 { 0%, 33.3% { stroke-dashoffset: 360; } 66.6% { stroke-dashoffset: 0; } } @keyframes write3 { 0%, 66.6% { stroke-dashoffset: 40; } 100% { stroke-dashoffset: 0; } }
And the sequence will look like this:
Further Refinement
What we have is good, but it isn’t perfect–certainly far from a realistic pen movement. Each of these three vectors is being drawn over the course of one second, irrespective of its length. The middle vector is wa-ay longer than the last, so it should logically take longer to draw. A better timeline might look something like this:

For added realism there’s even a gap between the first vector finishing and the second beginning. So let’s alter our keyframe values to reflect that:
@keyframes write1 { 0% { stroke-dashoffset: 80; } 20% { stroke-dashoffset: 0; } } @keyframes write2 { 0%, 25% { stroke-dashoffset: 360; } 90% { stroke-dashoffset: 0; } } @keyframes write3 { 0%, 90% { stroke-dashoffset: 40; } 100% { stroke-dashoffset: 0; } }
Finally, let’s speed things up by changing all the 3s
values to 2s
. We can also update the animation declarations so that each one runs just once, not infinitely looping:
animation: write1 2s 1 linear;
You might also want to play with the linear
value, instead adding some easing such as ease-in
, ease-in-out
, ease-out
etc. to make the movement less uniform. What does that all give us?
Next Time
We’ve made great progress and learned a lot along the way! In the next tutorial we’ll take things a step further, using Waypoints.js to help us control when the animation takes place. I’ll see you there!
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post