email@encode8.com
Tutorials

Weekly EncodeUI № 2: Animated Email Receipt with CSS Flexbox and Anime.js

email receipt final layout

This is a part of our weekly feature where we would build a simple webpage with different CSS and JavaScript techniques. Here, I pick a random #dailyui shot from Dribbble and show you how you can convert it to functional HTML page. I am not going to use SCSS or LESS, Pug or any other preprocessors. We would build these pages with just plain HTML, CSS and (mostly) vanilla JavaScript. At the end of the tutorials, we would create a list of key takeaways. I would try to explain in as simple a way as I can.

What we will build


This week I chose this Dribble shot. I like how the animation grabs the user’s attention without being too obtrusive. The animations are smooth and have a logical flow. Although email clients do not allow external JavaScript, this page may not have any practical application. But it is a good starting point to learn how we can use Anime.js to chain animations.

You can try the demo here. You can also check the GitHub repository for all the HTML, CSS and JavaScript files.

1. Getting Started

Planning the layout

We are going to have three sections: the top black bar will have id topbar, the middle part of the receipt (which contains the table) will have id details and finally the barcode area will be contained inside #barcode

Planning the animations

We are going to use Anime.js for this tutorial. It is a lightweight javascript animation framework. If you have a close look at the animations, you’d notice that the animations run one after another. That is, each one runs after the last one completes. It is a good case to implement Anime.js’ timeline feature.

So, while animating, we would just start displaying each animation by setting the element’s scale from 0 to 1. This way we can show each element one after one.

It is important to note here that we can’t insert elements and animate them one after another. This is because, due to performance issues, Anime.js do not read the DOM every time an animation is to be fired.

2. Starting with index.html

Let’s start with a blank HTML file.



    Email Receipt

Let’s start by adding the stylesheets: add these lines inside head tag.

   

Here, I am using PureCSS. This is optional — I am using this just to reset browser quirks. css/style.css is where our CSS styles will be.

3. Centering the content

In order to vertically and horizontally center the main content, put these lines inside the body tag in index.html:

And in our style.css:

body {
    background:#EBEBEB;
    font-family: 'Data Control';
    font-weight: 200;
    font-style: normal;
    letter-spacing: 0.3ch;
}

/*
 * Outer, middle, center technique from https://stackoverflow.com/a/6182661/450733
 */
#outer {
    display: table;
    position: absolute;
    height: 100%;
    width: 100%;
}
  
#middle {
    display: table-cell;
    vertical-align: middle;
}
  
#inner {
    margin-left: auto;
    margin-right: auto;
    max-width: 450px;
}

Using this technique, you can center any element — both horizontally and vertically.

Insert these lines inside

:

We want these sections to show up in a column. To do so, add these to your style.css:

#inner {
    ...
    display:flex;
    flex-direction: column;
    ...
}

This way direct children of our main container (i.e. #inner) will be flexbox items and they show up one below another.

4. Creating the black top section

I am using this Apple logo from LogoSvg.com. So, insert these lines inside

:

03-09-2018

And these styles:

#topbar {
    background:#333333;
    display:flex;
    color:#ffffff;
}

All the items in #topbar are flexbox items. And so, we can add:

#date {
    text-align: right;
    height:72px; /* Same height as the logo */
    color:#9E9E9E;
    line-height:72px;
    flex-grow: 1;
    margin-right:20px;
}

By setting flex-grow to 1, we can make #date take up all the remaining space inside the topbar. The line-height is set to the height of the logo, so that the text shows vertically centered.

At this point, our page looks like this:

email receipt topbar logo and date
For the font, I am using the Data Control. I used Transfonter to generate the @font-face. So, after you generate the font-face, put the fonts inside css/fonts/ directory and add these to style.css:

@font-face {
    font-family: 'Data Control';
    src: url('fonts/DataControl.eot');
    src: url('fonts/DataControl.eot?#iefix') format('embedded-opentype'),
        url('fonts/DataControl.woff2') format('woff2'),
        url('fonts/DataControl.woff') format('woff'),
        url('fonts/DataControl.ttf') format('truetype');
    font-weight: normal;
    font-style: normal;
}

5. The details table

For the table showing items, add these inside

:

Description Subtotal
Apple iPhone 7 $749
iPhone Silicone Care $35
AirPods $159
Total $943

And in style.css:

#details {
    background: #ffffff;
    transform-origin:top center;
}

.receipt-table {
    width:100%;
    font-size: 0.8rem;
    border:0px;
}

.receipt-table thead {
    color:#cccccc;
}

.receipt-table thead, .receipt-table td, .receipt-table th, .receipt-table tr {
    background-color: transparent;
    font-weight: normal;
    border:0px !important;
    padding:15px;
}

.receipt-table tbody td, .receipt-table tfoot td {
    padding:8px 15px;
}

.receipt-table tfoot {
    margin-top:25px;
}

.receipt-table .grand-total {
    font-size: 1.3rem;
}

This is fairly simple: we added a table with thead, tbody, and tfoot. Then we set a bunch CSS styles to correctly match the mockup.

6. Perforated effect

If you look at the mockup, you can see the part that separates the details table and barcode, has two little semi-circles at both ends. It gives the design a paper-like effect. To achieve this, I added a div after the table in this way:

And the corresponding styles:

.qtrCircles {
    display:flex;
}

.circle {
    width:10px; 
    height:10px; 
    border:1px solid transparent;
    background: #ebebeb; /* same as body */
}

.topright {
    border-radius: 0 14px 0 0;
}

.topleft {
    border-radius: 14px 0 0 0;
}

.dummy {
    flex-grow:1;
}

.dummy.dashed {
    border-bottom:2px dashed #EBEBEB; /* Same color as the body bg */
}

I have used this technique from Paulund’s CSS shapes to create the quarter of circles. The dummy class has nothing to do here but fill up the entire space (we did so by setting flex-grow:1)

At this point the page looks like this:

email receipt perforated look

7. The barcode

This part is very simple. Insert these lines inside

:

And the styles:

.bottomright {
    border-radius: 0 0 0 14px;
}

.bottomleft {
    border-radius: 0 0 14px 0;
}

#barcode {
    background: #ffffff;
    text-align: center;
}

#barcode img {
    padding:8px;
}

Here, again the same circle-technique as earlier. bottomright and bottomleft classes show the circles in right and left places repectively.

At this point, our layout is complete!

email receipt final layout8. Animation

First, we need to add the anime.js library in our index.html. Put these lines before the closing

:

Next, we need to hide everything. Add these lines to style.css:

#inner {
 ...
 transform: scale(0);
}

#date {
 ...
 transform: scale(0);
}

#details {
 ...
 transform: scale(0);
}

#barcode {
 ...
 transform: scale(0);
}

If you refresh the page, it’s blank now.

Animating the topbar

Notice that when the animation starts the apple logo is the middle of the topbar. Then it moves to the left and the date comes in. So, let’s position the apple logo in the middle of the topbar. To do so, add these in style.css:

#inner {
 transform-origin: top;
}

#topbar {
 ...
 position:relative;
}

#apple-logo {
 position: absolute;
 left:50%;
 transform: translateX(-50%);
}

Next, in main.js (inside js/ directory) add these lines:

document.addEventListener("DOMContentLoaded", function(event) {

  var animTimeline = new anime.timeline();

  animTimeline.add({
    targets: '#inner',
    scale: [0,1],
    easing: 'easeOutElastic',
  })

  animTimeline.add({
    targets: '#apple-logo',
    left: ['50%', 0],
    translateX: ['-50%', 0],
    elasticity:0,
    duration:200,
})

});

Here, first we create a anime timeline. Then we start our series of animations by showing #inner (by changing scale from 0 to 1). Then, we animate the apple-logo from the center to the left (by setting left from -50% to 0). Why -50%? Because that’s how to apple-logo was positioned in center.

Also, note we set the transform-origin on #inner to top. This is because we want start the animation from the top.

For the date to animate, add this to main.js:

animTimeline.add({
    targets: '#date',
    easing: 'easeOutQuad',
    duration: 200,
    scale: [0,1],
    translateX: ['-8px', '0px'],
    offset: '+=0',
})

This is fairly simple: we show the date by changing scale from 0 to 1, we add a subtle motion animation by changing translateX from -8px to 0. Notice the offset parameter. Offset is used to indicate the starting time of an animation. Here we want no gap between the logo and the date to animate. We do so with +=0.

If you refresh the page, the topbar animation will be there!

Animating the details table

Next, we want to animate the details section. Add these to style.css:

#details {
...
transform-origin:top center;
}

This is to start the animation from the top center position. Next, in main.js:

animTimeline.add({
targets: '#details',
rotateX: ["-90deg", "0deg"],
})

Here, we give a drop-like animation by changing translateX from -91 degree to 0 degree.

To animate each item inside the table, add this to main.js:

animTimeline.add({
    targets: '.row-anim',
    opacity: [0,1],
    translateY: ['3px','0px'],
    delay: function(el, i) { return (i * 200); },
})

Nothing fancy here except the delay parameter. The delay parameter indicates the amount to miliseconds between two animations. Instead of the default, we add a subtlty to it by increasing the delay by 200 ms.

Animating the barcode

This is just like the details section. We create a drop-like animation with translateX. In style.css:

#barcode {
 ...
 transform-origin:top center;
}

And in main.js:

animTimeline.add({
    targets: '#barcode',
    rotateX: ['-90deg', '0deg']
})

And that should be all! Just refresh the page and the animations would be there.

Conclusion

Anime.js is a feature-rich library. It puts a lot of focus on performance. With anime.js you can do a lot with html elements. In this tutorial, I just scrated the surface by showing how animations can be chained. If you are intrested, do read the documentation.