whoami

Lucas Jenß

cat /etc/motd

The Coding Journal ツ — Notes taken on an epic coding journey. Technical solutions, debugging notes, and practical guides from the trenches of software development.

Close-up of white dominoes with black dots standing on green felt surface, shallow depth of field, focused mood

ls -la ~/languages/

total 8
drwxr-xr-x
▶ PHP
▶ Ruby
▶ Scala
▶ C#
▶ JavaScript
▶ Objective-C
▶ Shell Scripting

ls -la ~/toolchain/

total 7
drwxr-xr-x
▶ Typo3
▶ Akka
▶ Capistrano
▶ Git
▶ MAMP
▶ Adobe Illustrator
▶ NSTrackingArea (Cocoa)

uname -a

platforms
drwxr-xr-x
▶ Mac OS X
▶ Unix

Bringing SVG paths to life with CSS stroke animations

There's a moment in every front-end developer's life when a static mock-up just feels too static. Whether you're polishing a portfolio site from a sun-drenched Brisbane studio or wiring up a loading sequence for a fintech dashboard, an SVG that draws itself on screen adds polish that stock spinners cannot match. The technique behind this trick is older than most of the frameworks you might pair it with, and it relies on two unassuming SVG presentation attributes: stroke-dasharray and stroke-dashoffset. Understanding how those two attributes interact with CSS keyframes is the difference between a path that jerks onto the page and one that feels hand-inked.

Most readers will have seen the effect on agency landing pages, GitHub READMEs, or a mate's startup splash page. The illusion is straightforward: a continuous stroke is broken into a single dash the length of the path, then the visible portion is shifted away frame by frame until the line appears to grow from nothing. The same logic explains why animated underlines look organic on a navigation menu, and why a well-timed signature animation can carry an entire brand reveal. Over the next sections we'll walk from the underlying math to the keyframes themselves, talk through the rough edges you'll hit in Safari and Firefox, and finish with a quick comparison against alternative approaches like SMIL and JavaScript libraries.

How stroke-dasharray and stroke-dashoffset combine

Before any animation runs, the browser needs to know two things about a path: how long it is, and how to break it into dashes. stroke-dasharray accepts a list of dash and gap lengths, and when you set it to a single value the browser interprets that number as both dash and gap, producing an alternating pattern. If the value you choose is equal to the path's full length, you get one dash that spans the entire stroke followed by one equally long gap. Pushing stroke-dashoffset to the same positive value slides the gap into view, hiding the line entirely. As the offset counts down toward zero, the dash gradually slides back into place.

The math is fiddly because every path is a different length. A circle of radius 40 has a circumference of roughly 251 units, while a complex illustration can run into the thousands. Hardcoding numbers is fine for a logo you fully control, but the moment you swap the artwork for a variant you are back in devtools measuring paths by eye. The cleanest production approach is to use the JavaScript getTotalLength() method to compute the value once on load, then apply it as a CSS custom property. That way the same animation works for every path the designer ships you, whether it is a minimalist koala mark for a Melbourne cafe or a sprawling diagram for a government client bound by the Australian Government Digital Service Standard.

const path = document.querySelector('.draw-me');
path.style.setProperty('--length', path.getTotalLength());

That single line cascades the length into the stylesheet, where it can drive both the dasharray and the offset without further JavaScript.

Preparing the SVG for animation

A path that animates well has very specific prerequisites. Fill must be set to none, or you will watch the silhouette fill in like a colouring book while the stroke crawls along the outline. Stroke colour needs to be visible against the background, and stroke-width should be chosen with the viewport in mind, because a 4 pixel stroke that reads beautifully on a 27 inch monitor can look chunky on a small Android handset out near Coober Pedy. Where possible, round line-caps soften the leading edge of the animation and stop the line from appearing to have a flat, laser-cut tip.

Another under-appreciated detail is the path direction. SVG paths are drawn in the order their commands appear, so an animation always reveals a stroke from M to the final Z. If a designer hands you a path that doubles back on itself, the animation will trace the loop twice and the artistic intent will be lost. Ask for the path data to be reordered upstream, or use pathLength="100" to normalise behaviour across paths of wildly different actual lengths. Setting pathLength on the element tells the browser to treat the path as 100 units long for the purpose of dash calculations, which is a gift when you are composing reusable components inside a design system.

Writing the CSS keyframes

Once the SVG is prepped, the CSS is almost disappointingly short. A two-stage keyframe sequence, dropping the offset from the full length down to zero over the desired duration, produces the classic drawing reveal. Add an ease-out timing function and the line decelerates like a calligrapher lifting a pen.

@keyframes draw {
  to { stroke-dashoffset: 0; }
}
.draw-me {
  stroke-dasharray: var(--length);
  stroke-dashoffset: var(--length);
  animation: draw 2.5s ease-out forwards;
}

The forwards fill mode keeps the final frame visible after the animation completes. Without it, the stroke snaps back to its invisible starting state the moment the keyframes finish, which is the most common reason newcomers assume their animation broke on page load. Loop the effect with animation-iteration-count: infinite and a small pause baked into the keyframes when you want a steady draw-erase-draw cycle, the sort of thing that fits a loading state on a Sydney-based ecommerce checkout.

For sites that need to honour reduced-motion preferences, gate the animation behind a media query. The Australian Human Rights Commission has long recommended respecting this preference, and the cost is one CSS block.

@media (prefers-reduced-motion: reduce) {
  .draw-me { animation: none; stroke-dashoffset: 0; }
}

Cross-browser quirks and performance notes

Safari has historically lagged behind Chrome on CSS animations applied to SVG geometry attributes, particularly when those attributes are first set in a stylesheet rather than as XML attributes. The reliable workaround is to set the initial stroke-dasharray and stroke-dashoffset inline on the element so the parser never has to reconcile them against CSS later. Firefox handles the trick well but occasionally drops the first frame if the SVG is injected late, which is a frequent problem with component frameworks that mount on hydration. Rendering an empty placeholder sized to the SVG until the client takes over avoids the flash of unstroked path entirely.

Performance-wise, animating stroke-dashoffset triggers paint rather than composite, so it is more expensive than a transform-based animation. On long paths or low-end hardware you will see frame drops on the first pass. Reach for will-change: stroke-dashoffset if the path is large or being redrawn frequently, and consider swapping the path out for a static image once the animation finishes if the user is unlikely to replay it. Battery-conscious users running a ThinkPad through a long cafe day on the NBN will appreciate the gesture, especially when the same animation is one of several drawing on initial load.

Pure CSS versus SMIL versus a library

When the trade-offs pile up, the right tool often depends on the constraints of the project rather than personal taste. The table below compares the three common approaches.

Approach Strengths Weaknesses Best fit
Pure CSS keyframes Tiny payload, no JS, plays well with prefers-reduced-motion. Hard to coordinate multiple paths or sequence steps. Logos, simple reveals, static marketing pages.
SMIL (<animate>) Lives inside the SVG, supports per-segment timing and complex easing natively. Deprecated paths in older Chrome docs, weaker tooling, no reduced-motion hook out of the box. Standalone SVG illustrations, email-friendly assets where CSS may be stripped.
JavaScript (GSAP, Motion One) Timeline orchestration, scrubbable on scroll, robust reverse and loop control. Heavier payload, hydration cost in framework apps, accessibility wiring is on you. Multi-stage storytelling, scroll-driven animations, complex interactive sites.

For most portfolio and product work, pure CSS is the right starting point. Reach for SMIL when you absolutely cannot ship JavaScript, and reach for a library when you need multiple paths to choreograph to the same beat.

Bringing it all together

A polished SVG draw effect does not require a heavy stack, but it does reward a few minutes of care. Set the dashes, write the keyframes, and watch out for fill rules, line-caps, and the dreaded missing forwards declaration. If you spend your working day chasing native build issues on Apple silicon and want a break from low-level tooling, the NDK build issues on OS X walkthrough elsewhere on this site is a good companion read while your keyframes finish their first iteration.

When the animation finally lands, share it. Drop a link in a Slack channel, post a clip in the team standup, or use it as a reason to revisit a side project you shelved back in January. Many developers find their best ideas while comparing notes over a flat white in Surry Hills, and there is no shortage of inspiration floating around the Cairns players guide kind of corner of the web where life outside the IDE gets a moment to breathe. Try the technique on your own site this week, tweak the easing until it feels like your own handwriting, and you will see why this small CSS trick has stayed stubbornly popular for over a decade.


cat ~/interests.json

KeyValue
editorTerminal-first workflow
osMac OS X / Unix
vcsGit, distributed version control
deployCapistrano, cron automation
graphicsSVG, Adobe Illustrator troubleshooting
networkingIP validation, SSH, VPN

git log --oneline --reverse

2013-10-30

Solving SVG import issues in Adobe Illustrator CS6 and CC

When importing an SVG into Illustrator, the operation fails with an unknown error [CANT]. A workaround for this Adobe-side bug.

2013

Solving NDK build issues on OS X

Troubleshooting native development kit compilation problems on Mac OS X.

2013

Programmatically adding PHP generated TypoScript to the backend configuration

Integrating dynamically generated TypoScript into Typo3 backend setups using PHP.

2013

ArgumentError: Could not parse PKey: no start line

Debugging an SSH key parsing error encountered during deployment.

2011-08-04

Validating IP-Addresses in PHP

Using PHP filter functions with flags like FILTER_FLAG_IPV4 and FILTER_FLAG_IPV6, and understanding how filter_var handles reserved IP addresses.

2011-07-09

Cocoa: Using NSTrackingArea

A short tutorial on using Cocoa's NSTrackingArea to capture mouseEntered and mouseExited events.


cat ~/contact.txt

github: github.com/x3ro
stackoverflow: x3ro
coderwall: coderwall.com/x3ro
twitter: @x3rames