Polaroid Stack to Grid Intro Animation

A tutorial on how to create an intro animation where a decorative Polaroid stack becomes a grid similar to the effect seen on the takeit website.


View demo Download source

Planning the effect

We have an intro “page” that contains a big headline, the Polaroid stack and a device with a button. We also have a header with some links. What we want to do is to animate the Polaroid stack to a grid layout and make all the other elements, except the header links, disappear with a neat animation. This will be triggered either by scrolling down or by clicking the arrow.

As with many effects, the best way is to think of the end state as default state. So, we won’t take a stack of Polaroids and calculate their positions in the final grid state but instead the grid state is their default state and the initial scattered position is something we set them to in the beginning. We’ll use the first six images and move them to a common center point and then scatter them a bit with the help of a factor that adds the illusion of randomness. Do we need to move the images up to some previous section? No, actually we don’t. We can use a fixed intro section that we’ll instead animate up, outside of the viewport, giving the illusion as if we are moving (the grid) down.

Since we will have many animations happening, we want to have solid, individual control over them. A library like dynamic.js will help us animate the elements, so we will control the crucial animations in our script.

Most of the layout will be powered by flexbox. Read this guide by Sara Soueidan from the CSS Reference to understand all its great properties.

The images we are going to use in the demo are from Unsplash.com.

Let’s start writing the markup!

Attention: We’ll be using some modern properties that are only supported in modern browsers.

The Markup

How are we going to make our effect look as if we are scrolling down the page and moving around the elements without actually scrolling? We can simply trick the user’s eye by moving a fixed background outside of the viewport, creating the illusion that we moved down in the document. For that we will create a section with the class page and the “modifier” page–mover. Inside we’ll only have a loader. Check out more CSS loaders on Load Awesome by Daniel Cardoso.

The main title will not go inside of that page section because we want to animate it differently. If we’d put it inside we’d have to consider the parent’s movement and we don’t want that. So we create a own division for the main title with the class title-wrap.

Then we define the section for the grid which will also be a page, but with the modifier page static. There we’ll also have a title wrapper and an unordered list as our grid. The last element in our static page is a button for loading more items (this we won’t implement but it’s ready for a dynamic solution).

The last two elements in our view container are the device and the arrow button to trigger the animation and show the grid.

That’s all the markup, let’s move on to the styles!


Note that we use the necessary -webkit- prefixes for better mobile support and Safari.

First, we’ll reset the box-sizing for all elements to border-box:

For the body, we’ll set some typography and colors:

We will need a helper class that prohibits initial scrolling (while the JavaScript is still loading; then we take care of it in our script):

The header element with the links will be positioned absolutely and we’ll use flexbox to lay out the elements inside:

With pointer-events set to none you don’t make this element clickable. Inner elements like links will have pointer-events set to auto. This technique can be useful for fixed or absolute elements that should be on top of everything but that are not obtrusive in the parts that are not “needed” to be clickable.

Now, let’s define the styles for the movable page. Since this is going to be our illusion artist, we set it to a fixed position and size it to the whole screen. The background is set to a dark color:

The title wrap will be positioned at the top part of the page:

The typographic styles for the inner elements are as follows:

With a media query for larger screens, we ensure that the title elements are sized relatively to the viewport:

The static page will have a flexbox column layout that makes sure that everything is neatly centered. We’ll add a maximium allowed width which will be enough space for three full images in a row:

The page title elements have the following styles:

Now, let’s style the device. We get the Sketch device from the Facebook devices collection and export the SVG. That will serve as the background image of our device division. This division is fixed with a z-index that will lay it on top of everything. We set the dimension to a square that is relative to the viewport height. This ensures that it never becomes bigger than half of the screen. We center it with the negative margin technique:

The screen of the device will contain an app screenshot and its sizes are percentage based, relative to the device:

Next, let’s style the two buttons, the arrow button and the “load more” button. Since they both have some style resets in common, we define the following styles:

The view button will be positioned absolutely and we’ll give it a little movement so that it stands out as trigger. We’ll also add some hover transition for the fill:

The load more button also has some text:

Besides a hover transition, the little Polaroid will be ready for a loading state that we trigger with the classbutton–loading. The three little circles will be animated to indicated that images are being loaded:

Then we will want to hide the button (that’s for when there are no more images to be loaded in your real case scenario):

The grid will also be powered by flexbox. The z-index is 100 just so that its underneath the device and the main title:

We want to control the size of our grid items so they don’t need to be flexible. The width should be one third of the parent’s width. A padding of 10px will create a gutter for the Polaroid images:

When we insert new items into the grid, we need to control the visibility of the grid items. For this we will create the following helper class:

The link element will be the one with the border decoration, so we add a padding of 13px for all sides except for the bottom side. This part will be styled in the title element.

The image will fill all available space:

The Polaroid titles will have a nice script font to look like they have been written on:

Note that we have inserted the link to the Google Web font in the head of our HTML.

Since we need to preload our images, let’s show a little animated loader that actually reminds of a Polaroid. We’ve used a CSS loader from Load Awesome and adjusted some styles:

We should never hide anything assuming that JS is available but think about how everything looks/works without it. So, while we are actually preloading the images, we don’t want some elements to be shown. Then, when the images are loaded, we’ll want to show the grid, the device and the arrow button:

The same holds for the pointer events of the static page:

Finally, we have to tweak our layout a bit for smaller screens:

And that are all the styles!

Now, let’s write the magic spells for this dead bird to come to life!

The JavaScript

First, let’s define and initialize some variables:

introPositions is the array where we can define the positions of each of the six images that are part of the stack. Initially, the images will be positioned on the center of the screen (at the bottom) and then each one will have a specific transform applied based on introPositions. The “tx” and “ty” values are percentage based and define how much of the item’s width/height will be added to the item’s translation. The “s” and “r” represent the scale and rotationZ respectively. If we’d want the stack to appear differently we’ll just need to adjust these array values.

Next, let’s define our init function:

We’ll preload all the images inside the grid, and only then show the stack of the six first images behind the device. While the images are being loaded we disallow the user to scroll the page, since the act of scrolling will also make the grid appear.

The showIntro function will position the six images behind the device. The images are first positioned in the center of the page (bottom) and the translation, scale and rotation values set in introPositions are applied to each image. We want that both the images and the device element slide from bottom up once all images are loaded, so we need to position these elements first and then animate them to their final state:

The bind/init events function looks as follows:

We will need to define the events for when we click the “show grid” control button (arrow), the page scrolling, the window resize and the loading of more grid items.

Now let’s define all the animations we need for showing the grid. Once again, we’ll be using the dynamics.js library which will make this process much easier: