How to make a composited typing animation with Tailwind
This is how I turned an inefficient animation into a composited one, using Tailwind and CSS knowledge. I use the animation on top of my homepage so it would have been hard for me to accept that a Cumulative Layout Shift is the top issue of my visitor’s load. If it wasn’t obvious (or I’ve changed my homepage since writing this post), the animation is the one below.
hi, I'm vanntile 👋
The starting point
We all need to start somewhere, and this is where I have started. First of all, I am using Tailwind as a CSS framework in order to avoid reinventing the wheel whenever I need some basic utility (and I can also rely on some beautiful patterns and dark mode out of the box). But it should be as straightforward as some other code to somebody familiar with modern CSS and animations.
But first, requirements – the animation needs:
- to be timed properly
- to be able to have an emoji (that’s because of my own copy)
- to be responsive (to look just as good on different screen sizes)
The above HTML
block tries to do lots of things in the same time. First of all, it promotes the span being animated on
its own layer using the will-change-transform
class, included below. This has the effect of
hinting the browser to optimise that element’s paint
stage in the render cycle.
It’s essential that we don’t overdo it (you can but shouldn’t promote everything as it takes processing power from the browser’s allowed CPU time).
Next, it has an animate-type
class which sets an animation continuously, but that gets overriden by
animate-type-reverse
when the container group
is hovered. Both are custom animations written by myself and change small details like duration, timing, ease and
direction for a base set of keyframes, which you can see below. Yes, I went through a painstaking work of matching
keyframe percentages to element width in characters.
At the end of it all, there is a span
element that is animated as a blinking cursor. For this, we’ll animate the
opacity of it’s after pseudo-element
, with a linear timing and an alternate direction (back and forward).
To recap, what we are doing for now is animating the width of an element with hidden overflow which both changes the layout and moves the next element’s position (the cursor). To be read: It’s really bad. Why? Because it triggers on all browsers all rendering steps - layout, paint and composite (which need to be recalculated).
The end result
After several iterations I have reached the following version. First, let’s address the irrelevant, but necessary
changes: we’re changing the single quote into a '
and the emoji is resized depending on the screen size.
Afterwards, the animation logic completely changed from animating the width and hiding the rest to animating the cursor span only and translating it. Animating the transform will trigger only compositing on most browsers. In order to make our trick work we position it absolute from the relative parent and we make it full width (while positioning it slightly over the top to cover our text completely). Of course, it wouldn’t work if we wouldn’t it’s display wouldn’t be block, in order to have some width without content.
You can see in the snippet below the keyframes to the one and only animation (we have removed the complex logic that would change on hover because sometimes it’s better to keep it simple, stupid).
Conclusion
Our browsers are incredible bundles of code nowdays and we’d better spend some more time as developers to serve a performant, non-wasteful experience to our user, which takes advantage of the optimisations our browsers can add to the rudimentary rendering model we hold into our coding abstractions. And hey, don’t forget to use Tailwind, it makes things much easier!