How to build a fancy animated modal box

Hello everyone I’m back again with another Pixel2HTML tutorial. This time we’ll make a modal box pop-up kind of like the ones that show up when you try to sign to a newsletter or something related like that. Let’s check the final result first…

Completed project overview

Demo gif

Overview

​The whole idea here is that we have a button with an icon inside of it. Check the article “How to work with SVG icons” for a more in depth look at working with SVGs like a boss.

So on hover we’ll do some fancy GPU accelerated transitions to scale the button and rotate the icon inside of it.

On click the overlay will fade-in and the contents behind it will blur out using some new css filters. Then the modal box shall come down in a bouncy way.

You can then dismiss it by either clicking on the “X” or on the overlay background or by pressing the ESC key on your keyboard.

Let’s go!

Setup

​As I always like to start my projects I’ll do a copy of my always trusty Ground-Zero Repo which also includes my collection of *SaSS mixins Manila Mixins because yummy Mango Manila, you know! 🌴

The directory structure is pretty standard, everything is inside the src directory and after doing gulp it will be compiled and spitted out to the dist directory.

Please Note: This project’s code is made up using Jade, the SaSS Syntax (.sass not .scss) and CoffeeScript, however for the following examples I’ll write the outputs of the compilers so everyone can read the code ❤️

Making the Header First

First I’ll show you the markup and then I’ll break it down into reasonable components:

This is the header part. (This is visible at the beginning)

<header>
  <div class="container">
    <h1>Get a Space Trip!</h1>
    <p>Secure your chance for a cool trip to the stars!</p><a class="button modal-trigger"><span>Click Here</span>
          <svg class="icon sir">
            <use xlink:href="#sir"></use>
          </svg></a>
    <p> <a title="A Pixel2Html Invention" href="//pixel2html.com" title="Terms" target="_blank" class="medium-underline">A Pixel2Html Invention</a></p>
  </div>
</header>

​​
Now let me show you the CSS for which I do mine with SaSS:

header {
  min-height: 100vh;

  @include background("village.jpg", rgba(0, 0, 0, 0.3));

  color: $white;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  transition: filter $trns-duration;

  @include is(blurred) {
    filter: blur(5px);
  }
}


That’s quite some CSS let me break it down for you a bit:

  • The first part sets the header to be at least the whole size of the screen sets up a background and turns the text white.
  • Then using Flexbox I’m perfectly centering all of the contents of the header
  • The transition property tells the browser to get ready to transition the fill property for a duration stored as a variable (in this case its 0.3s)
  • Finally I’m using another of my mixins to set up a JS-trigger class that will be called header.is-blurred that will make the header and its contents blur by 5px when the class is applied to the element.

Making the Modal Box


Let’s first take a look at the markup needed for the modal box to happen.

<aside class="modal-box">
  <div class="background-overlay"></div>
  <div class="contents">
    <div class="close"> <span>X</span></div>
    <div class="half stars"></div>
    <div class="half">
      <h4>More than you imagine!</h4>
      <p>It's your chance for a trip to space!</p>
      <input type="text" placeholder="Enter your email">
    </div>
  </div>
</aside>


I’m declaring the whole HTML element as an aside because I think it holds more semantic meaning than a simple div

Everything else should be pretty straight forward.

Onto the SaSS:

.modal-box {
  position: fixed;
  z-index: -1;
  top: 0;

  @include size(100%);

  text-align: center;
  opacity: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  transition: $trns-duration;

  @include is(active) {
    z-index: 2;
    opacity: 1;
    pointer-events: all;

    .contents {
      transform: translateY(0) translate3d(0, 0, 0);
    }
  }


  .background-overlay {
    background: rgba($black, 0.4);
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }

  .contents {
    background: $white;
    border-radius: 15px;
    position: relative;
    width: 90%;
    transform: translate3d(0, 0, 0) translateY(-250%);
    transition: $trns-duration cubic-bezier(0.5, -0.5, 0.5, 1.5);

    @include breakpoint(laptop) {
      display: flex;
      width: 60%;
    }
  }

  .close {
    display: block;
    position: absolute;
    font-family: $font-family--secondary;
    top: -10px;
    right: -10px;

    @include size(em(40px));

    border-radius: 50%;
    background: $main-color;
    color: $white;
    cursor: pointer;
    box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15);

    span {
      @include align;
    }
  }

  .half {
    padding: em(40px);

    @include breakpoint(laptop) {
      @include flex-cols(6);

      padding: em(120px) em(40px);
    }
  }

  .stars {
    min-height: 250px;
    border-radius: 10px 10px 0 0;
    background: url("../img/stars.jpg") no-repeat (bottom / cover);

    @include breakpoint(laptop) {
      min-height: 100%;
      border-radius: 10px 0 0 10px;

      @include background("stars.jpg");
    }
  }

  h4 {
    line-height: 1;
    margin-bottom: em(10px);
  }

  p {
    margin-bottom: em(15px);
  }

  input {
    display: block;
    font-size: em(16px);
    border: none;
    border-radius: 20px;
    padding: em(8px) em(15px);
    outline: none;
    margin: 0 auto;
    text-align: center;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.6);
    transition: box-shadow $trns-duration, transform $trns-duration;
    color: $color-acento;
    line-height: 1;

    @include placeholder(rgba($black, 0.7));

    &:hover {
      box-shadow: 0 5px 8px rgba(0, 0, 0, 0.15);
      transform: translate(0, -2px);
    }
  }
}


We have a TON of SaSS happening here, but on a broad scale whats going on is that we are placing the modal box using fixed position on top of everything.

Inside the aside we have the background overlay and also the modal box itself.

We are using flexbox for everything and also a bit of soft shadows for some elements.

Now lets glue everything together using javascript…

(function($) {
    var close, closing, everything, header, modal, overlay, trigger;
    header = $('header');
    modal = $('.modal-box');
    trigger = $('header .modal-trigger');
    close = $('.modal-box .close');
    everything = $(document);
    overlay = $('.modal-box .background-overlay');
    trigger.on('click', function() {
      header.addClass('is-blurred');
      return modal.addClass('is-active');
    });
    closing = function() {
      header.removeClass('is-blurred');
      return modal.removeClass('is-active');
    };
    close.on('click', closing);
    overlay.on('click', closing);
    return everything.keyup(function(e) {
      if (e.keyCode === 27) {
        return closing();
      }
    });
  })(jQuery);


I’m first making everything a self-executing function to prevent global scope variable pollution. I then declare variable names for everything I’ll use and then the whole thing the JS does is basically either add the classes needed or remove them depending on the situation.

For extra UX-ness I’ve added the possibility of using the ESC key or clicking the overlay background to remove the modal box.

All compiled the CSS weights a stunning 5kb (amazing, isn’t it?) and the JavaScript is not even a single kb. The more heavy stuff are the images which we could compress more but we won’t because we want that sharpness and wow factor to remain.

And that’s it! You should be good to go at this point. Don’t forget to take a look at the demo and to check the source code.

Have any questions, comments or you think we can make something better? Leave us a comment below! We’d love to exchange ideas and see how you build amazing things with this technique.

If you want more cool tips and tricks hit me up on twitter at @mpalau and don’t forget to follow us at @Pixel2HTML as well.

Until next time!





Do you want us to help you build your project?

Get it started