Animations with Tailwind CSS
Transition, the hidden star of Headless UI.
The best mobile apps, like Airbnb and Spotify, are fun to use. But what makes them so great? Yes, they have an outstanding UI and UX. But what separates them are these little animations on page transitions and for other user interactions.
I create web apps, and most of the time, I use Tailwind CSS for styling.
How can I add those little animations to my web projects with Tailwind CSS?
Of course, I can use a library like Framer Motion. It’s fantastic, but in this article, I will show you how you can use the amazing Headless UI Transition component in combination with Tailwind CSS native animation properties to achieve great results.
I’m implementing two common use cases:
Animated form input elements in a slide-over panel
Elements that fade in when they enter the viewport
Find the source code of all steps from the beginning to the final working examples on my public GitHub repository:
markustripp/tailwind-animation.
Animated Input Elements
I assume you are familiar with setting up a Next.js project with Tailwind CSS, Tailwind Forms, and Headless UI. If not, tons of tutorials show you how to do that.
Step 1: Create Page Structure
First, create a page with two simple components: a Button
component and an Input
component. Clicking the button toggles the input field.
I’ll name the example pages slide1.js, slide2.js, slide3.js, … each number for each step during the development.
Step 2: Add Headless UI Transition
Now, when clicking the button, I want to trigger the animation and fade in the input field. You control this by linking the Transition
show attribute with the show state variable.
For the fade-in animation, you set the following attributes:
- Opacity from zero to 100%
- Translate-y from 6 to zero to move the element up during fading in
Step 3: Introducing Transition.Root and Transition.Child
On button click, I want to animate multiple form elements individually. Therefore I must convert the Transition
component to Transition.Child
components and wrap it with a Transition.Root
component.
The Transition.Root
component is linked to the show state variable and controls when all child components get triggered.
Step 4: Create FadeIn Component
For re-usability and readability, I convert the Transition.Child
animation into a FadeIn
component. This enables me to apply the FadeIn
component to multiple fields easily.
Please note that each input field starts the animation with a different delay:
- First name:
delay-[0ms]
- Last name:
delay-[300ms]
- Email:
delay-[500ms]
Note: For the Tailwind CSS compiler to work properly, the attributes must be available in clear text. E.g., it is not possible to use variables for the milliseconds like
delay-[${index * 100}ms]
Step 5: Add Background and SlideOver layer
Next, I want a panel with the form to slide in when the button is clicked. To create this effect, you must create two extra layers:
- BackgroundLayer
- SlideOverLayer with a panel that fades in the form input fields
To prevent scrolling of the underlying layer, I use a combination of
fixed inset-0
andabsolute
properties.
Step 6: Animate Form Elements on SlideOverLayer
As the final step, I add an animation to the BackgroundLayer and SlideOverLayer and fade in the form input fields.
In addition, you should add a Dialog component to the slide-over layer as described in the Tailwind UI component library.
Library Ideas
Steps 1 to 6 helped me create my own component library for my projects.
But slide7.js
and slide8.js
include some ideas on how to structure and name a general animation library using this technique. Feel free to add your ideas in the comments below.
Creating such a library is technically pretty simple. The hard part is naming it properly and adding default animations that are smooth and look awesome on many different use cases.
Please note: slide7.js and slide8.js are just ideas and NOT ready libraries and NOT tested! Don’t report bugs in the comments.
This example replaces the Transition.Root
component with an Animate
component. And a general purpose SlideOver
is introduced and can be re-used in many projects.
The children of this Animate.Group
component are faded in using a linear timing.
Animate on Entering Viewport
Another common use case is to trigger an animation when an element or elements enter the viewport. Either the animation starts immediately or with an offset.
This example fades in a box that includes a header and paragraph when the box enters the viewport. Each element is animated individually.
First, I must get notified when an element enters the viewport. All modern browsers implement the Intersection Observer API. But instead of using the native API, I use the react-intersection-observer library.
For this use case, I used the InView
component. It contains a boolean property named inView
. This property is true when the referenced ref
element is inside the viewport and false otherwise. Now I link the inView
property with the show
property of the Transition.Root
component.
Step 1: Create Page Structure
I start creating the page without the animation effect. It is just a centered box within a container. For this example, the container has the height of the entire screen.
Step 2: Add Animation
Similar to the slide-over example, I add a Transition.Root
and multiple Transition.Child
components.
When the second box enters the viewport, the inView
variable is set to true, which triggers the animation of the Transition.Child
elements in the box.
Step 3: Library Ideas
A possible usage of an animation library could look like that:
Conclusion
You will be surprised by what you can do with just the standard tools from Headless UI and Tailwind CSS. If you have advanced requirements for your animations, you can use a library like Framer Motion. But most of the time, using the standard tools is sufficient.
Over time, I created a set of animation components that I use across my projects.
About the author (Markus Tripp):
I’m a freelance web developer and Shopify consultant — and I’m the creator of Headcode CMS (www.headcodecms.com), a 100% open-source headless CMS for Next.js 13 App Router, Server Components, and Server Actions. Watch the video below for a quick product demo: