Stripe Checkout is an example of a delightful application that tastefully uses CSS animations as an essential part of it's user experience. Michaël Villar, a User Interface Designer at Stripe, enumerates the reasons why animations were integrated into the design here. In this article we will dig into the first animation, pictured below.

Unable to find a really pertinent term for this animation, I dubbed it the "flap effect" as it feels like the flap of content is swinging towards the user. The animation is subtly pleasing and adds a level of depth that isn't typically seen in forms around the web.


The majority of this animation lies in the CSS, it makes use of the CSS rules: transform, transform-origin, perspective, and transition. Let's go through each rule to describe how it contributes to the overall effect.


The transform is responsible applying the rotation along the X-axis, the XY axis here is the plane of the monitor. The content will start at an angle away from the user and swing towards the user, going from -60 degrees to 0 degrees. Here's a diagram of what the rotation looks like to the user.

Since CSS rotation transforms have an origin along the axis of rotation. We will want to change the origin of the Y offset to be along the top of the content, this will make the animation origin the top of the container.

/* Initial */
transform: rotateX(-60deg);

/* End */
transform: rotateX(0deg);

transform-origin: 50% 0;

The initial rotation state looks like this:

The end state:


If you look at the Stripe animation above, it is clear that the initial state has a perspective applied to the element so it's not a flat transform. The z-axis needs to have a stronger vanishing point and for that we will use the perspective property. Let's apply a large perspective value to the content.

perspective: 300;

With the perspective applied to a containing element of the content, the initial rotation state comes out looking much better:


Now that the start and end states are defined, the glue for the animation needs to be applied. Let's pull out the Swiss Army knife of CSS transitions: transition. To make the content feel like it's coming into focus the opacity property will be transitioned from 0 to 1 as the rotation is changed. When applying the transition there is a plethora of easing functions to use, this site here does a great job at visualizing many of the choices. Let's go with easeOutQuint for the flap.

transition: all 400ms cubic-bezier(0.23, 1, 0.32, 1);

.hidden {
  opacity: 0;
  transform: rotateX(-60deg);

.expanded {
  opacity: 1;
  transform: rotateX(0deg);

All that's needed now is a sprinkle of JS for toggling the CSS classes when the input is clicked:

var showInputEl = document.getElementById("show-form");

showInputEl.addEventListener("click", function(e) {
  var description = document.getElementsByClassName("description")[0];
  description.className = ? "description expanded" : "description hidden";

Here's the final product:

Not too bad!

This is a part of the Dissecting Delight series, we initially will be reviewing the Stripe Checkout animations and hopefully many more great experiences. The code is available on Github here, please feel free to make poke me on Twitter if there's anything I can help with or correct!


  1. Michaël awesomely replied with a correction that the transition should have a slight bounce, this means changing one of the points in the Bezier to be > 1.0 so that it pops out and goes back into the page. He also suggested gifrocket, which I highly recommend for converting .mov files to .gif.