Create a Pop-Up Modal with CSS & JavaScript

Pop up modals are a great way of providing additional information to users without taking up any extra space occupied by your main content. Amongst other things, they can be used for displaying game settings, gathering user data (such as an email address for a mailing list subscription), and providing event details. Take a look at the Codepen below to see what we'll be creating, then we'll go through the CSS and JavaScript required to make it work. Click the information icon at the top of the demo to toggle the modal.

Starting Template - Creating the Modal

If you want to code along, the starting template for this project is linked below for you to fork to your Codepen. The starter template doesn't contain any of the HTML content to create the modal, and none of the CSS to style it or the JavaScript to make it function.

Writing the HTML

The first thing we'll have to do is create our modal in HTML. The modal will consist of two div elements. The first will be the modal div, and within this we'll have a child div called modal-inner which will consist of the content we want to show the users. The content we want to include will be:

  • A span element that will close the modal on click.
  • A h2 element to display a title.
  • A p element to display a message.
  • An a tag which will act as a sign up link.

The HTML code to produce this is shown below.

<div class="modal">
  <div class="modal-inner">
    <span class="close">&times;</span>
    <h2>Want to know more?</h2>
    <p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Omnis, sed!</p>
    <div class="btn">
      <a href="#">Sign Up!</a>
    </div>    
  </div>
</div>

Writing the CSS

Styling the Modal Container

When considering the CSS for the modal, the first thing we need to do is make sure the modal div has position:fixed; which will remove it from the normal document flow and position it relative to the viewport (as opposed to being positioned relative to a parent). We can then set the top, right, left, and bottom values to 0 to make it occupy the entire viewport.

We can also set the colour of the modal using background: rgba(0,0,0,0.65);. Feel free to use your own background colour for this and play around with the opacity to find a value you're happy with. Reducing the opacity allows the user to see the page content beneath and lets them know they haven't left the page they were on.

Finally, I'm going to add padding-top:100px; which will provide a bit of space between the top of the viewport and the modal-inner div that we will style next. This is something you can play around with.

.modal{
  position: fixed;
  top:0;
  left:0;
  right:0;
  bottom:0;
  padding-top: 100px;
  background:rgba(0,0,0,0.65);
}

Styling the Inner Modal

To style the inner modal, which contains our content, we need to give it a background colour, a width, along with a margin and some padding. The main point to note here is that we can use margin: auto; to centre it within the viewport. The rest are a matter of personal preference, so have a play around with whatever values you'd like here.

The code for the inner modal is shown below.

.modal-inner{
  background:#fff;
  width:70%;
  margin:auto;
  padding:1rem;
}

Styling the Inner Modal Content

Next we need to style the elements within the inner modal, such as the h2, p, and a elements. These are mostly a matter of taste and personal preference so I won't explain too much here. Play around with these as much as you like, or you can use the code below.

.modal-inner h2, .modal-inner p{
  text-align:center;
}

.close{
  color: #aaaaaa;
  float: right;
  font-size: 1.75rem;
  font-weight: bold;
}

.close:hover{
  color: #000;
  text-decoration: none;
  cursor: pointer;
}

.btn{
  text-align:center;
  margin-bottom: 1rem;
}

a{
  background:red;
  color:#fff;
  font-weight:bold;
  text-decoration:none;
  padding: 10px 12px;
}

Preventing Vertical Scrolling

Finally, to prevent vertical scrolling when the modal is open, we're going to add the following class:

.open{
  height:100vh;
  overflow-y:hidden;
}

This will make the height of the body 100% of the viewport height, and will hide any content that vertically overflows. We'll dynamically add and remove this to the document class list depending on whether the modal is open.

The code we've generated so far should produce something like the Codepen below:

Adding the JavaScript

Now we have the modal and its content styled, we need to dynamically toggle it when the user clicks on the information icon. We then need to close it when the user either clicks the 'X' icon in the pop-up, or when they click outside of the inner modal content.

For demonstration purposes, one line of CSS was left out of the modal styling in the above section. This line of code that we need to add is display:none; so that the modal cannot be seen until we toggle it with JavaScript. Go ahead and add this declaration to the modal selector.

.modal{
  display:none;
}

Now we can get started with the JavaScript. Firstly, lets grab the elements we need from the DOM.

//The information icon
const info = document.querySelector('.info');
//The 'X' icon to close the pop-up
const close = document.querySelector('.close');
//The modal
const modal = document.querySelector('.modal');
//The body
const body = document.querySelector('body');

Next we're going to add a click event listener to the information icon which will show the modal every time it is clicked and prevent page scrolling:

info.addEventListener('click', ()=>{
  modal.style.display = 'block';
  body.classList.add('open'); 
})

So now, every time the information icon is clicked, we change the display property of the modal from none to block, making it visible to the user. We also add the open class to the document, which will prevent vertical scrolling.

Next up, we need to add a click event listener to the close icon that will change the display property of the modal back to none when it is clicked, and remove the open class from the document.

close.addEventListener('click', ()=>{
  modal.style.display = 'none';
  body.classList.remove('open');
})

Finally, as an alternative method of closing the pop-up, we want to add a click event listener to the modal that will change the display property to none when clicked, and again remove the open class from the document.

modal.addEventListener('click', ()=>{
        modal.style.display = 'none';
        body.classList.remove('open');
})

BUT WAIT! - What's the issue with above code to close the modal?

Notice that when the modal is open, clicking anywhere within it will cause it to close - even if you click the content within the modal-inner div. This is a problem, especially if we want the user to interact with the content of the inner modal. This issue occurs because the modal-inner div is a child of the modal div, so by clicking this you are triggering the arrow function. To get around this issue, we need to check if the click occurred specifically on the modal div and then close it if the check returns true.

The above code now becomes:

modal.addEventListener('click', (e)=>{
    if(e.target.className == 'modal'){
        modal.style.display = 'none';
        body.classList.remove('open');
    }
})

Notice that you need to pass the event object to the arrow function in order to check the target of the click. Here, we're accessing the className property of the event object, and checking if it is equal to modal. If this is true, then we close it the same way we did with the close button. If this isn't the case, nothing will happen.

Finishing Up

You now know how to create a modal with CSS and JavaScript! The final Codepen is linked below again, so you can see what we've created.

This is a really fun feature you can implement in your projects. For your next project, why not try to create a JavaScript game and use a pop-up modal to allow the user to change game settings such as sound and hints?

If you found this tutorial useful, let me know on Twitter - @jacobcollinsdev

Kevin le texier's photo

It's good but it would be better if it respected accessibility :) See modal/disclosure sections : w3.org/TR/wai-aria-practices-1.1

Jacob Collins's photo

Thanks for pointing out, Kevin. I'll take a look right away!

Emily Boston's photo

Cool, it's so easy and simple. Thank you for sharing. I am just getting started with web designing.

Jacob Collins's photo

Thanks for your comment, Emily. Best of luck with your journey in web design!