HTML, CSS, JavaScript modal popup
A modal popup is a useful element to display an important piece of information after certain event, while hiding the focus from the other parts of the web page. The popup is dismissed on the click of a button on the modal. For example, once the user has submitted a form, we may want to display a confirmation dialog.
In this blog, let’s develop a modal popup in HTML, CSS, and JavaScript. Here’s what it’ll look like when open:
The HTML
We start with the following HTML:
<!DOCTYPE html>
<html>
<head>
<title>Modal</title>
<link rel="stylesheet" href="style.css">
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
</head>
<body>
<header class="hero">
<button type="submit" class="btn">Submit</button>
<div class="popup">
<span class="material-symbols-outlined">done</span>
<h2>Thanks</h2>
<p>Your response has been submitted!</p>
<button type="button">OK</button>
</div>
</header>
<script src="app.js"></script>
</body>
</html>
We link our page to a CSS file, and the Material icons library. Within the body
element, we create a header
element with a class of hero
. Instead of the textual content, we insert a button
with the text “Submit” in this element.
Next, we define a div
element for the modal popup. Inside it, we place the “done” check mark from the Material icon library. We add a heading with the text “Thanks”, followed by a paragraph element that gives the user some feedback. Next, we have a button
in the modal popup. Finally, we include our JavaScript file.
The CSS
We start with a CSS reset, and some CSS variable definitions:
*, ::after, ::before {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Roboto', 'Open Sans', sans-serif;
}
:root {
--clr-bluish: #3c5077;
--clr-bluish-2: #036099;
--clr-white: #fff;
--clr-light-gray: #333;
--clr-green: #33d692;
}
We set the margin
and padding
on all elements to zero, and set box-sizing
to border-box
so that the borders of elements are included in their size. We also specify preferences for the font family. Finally, we declared several variables for various colors. This makes it convenient if we want to change one shade that is used across multiple elements.
Styling the hero section
Next, let’s style the hero section, and the button in it.
.hero {
width: 100%;
height: 100vh;
background-color: var(--clr-bluish);
display: flex;
align-items: center;
justify-content: center;
}
.btn {
padding: 10px 50px;
border-radius: 50px;
background-color: var(--clr-white);
cursor: pointer;
font-size: 2rem;
font-weight: 500;
}
Using a class selector, we set the hero
section to occupy the entire page width. We give it a bluish background color. To center the contents, i.e., the button, both horizontally and vertically, we use CSS flexbox, and set the align-items
and justify-content
properties to center
.
The button does not look attractive, by default. Among other problems, the text content is too close to the button edges. We select the button using a class selector, and apply a bit of padding
. We give it a rounded look using the border-radius
property. We give it a white background color. To give the user a visual clue about the active nature of the button, we change the cursor
to pointer
. We increase the font size and weight to make it more prominent.
At this point, the page has all the elements, but it looks nothing like what we need:
Styling the modal
Let’s now make the modal popup look like a popup.
.popup {
width: 200px;
background-color: var(--clr-white);
color: var(--clr-light-gray);
border-radius: 8px;
text-align: center;
padding: 0 20px 20px;
}
.popup p {
margin-top: 10px;
font-size: 0.8rem;
}
We set the width
for the modal popup to 200 pixels, which should be enough. We gave it a white background color, and a light gray text color. For rounded edges, we used the border-radius
property. To center the popup contents, we set the text-align
property to center
. We give it a bit of padding to have some spacing between the popup edges and the content.
Next, we reduce the font size for the p
element in the popup. Also, we add a bit of top margin, so that the paragraph text is separated from the content above it. Here’s what the popup looks like, now:
The popup now looks like a decent card. However, it shouldn’t be displayed at the same time as the submit button, and it shouldn’t be displayed to its left, of course. We’ll fix the first issue, later, but let’s first make sure we are satisfied with the look and feel of the popup.
Let’s center the popup on the page:
.popup {
/* Other styling */
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
/* Rest of the styling */
}
We edit the style for the popup
class. We set its position
to absolute
, to take it out of the normal flow of the page, so that we can position it flexibly. We set its top
and left
properties to 50%, which places its top left corner at the center of the page. However, we need the center of the popup at the center of the page. To accomplish that, we add a trasform
or 50% translation towards the left, and 50% translation upwards. With that, now the popup is centered on the page.
Styling the check mark
Let’s make the check mark look a bit cooler:
.popup .material-symbols-outlined {
font-size: 4rem;
border: 2px solid black;
border-radius: 3rem;
background-color: var(--clr-bluish-2);
color: var(--clr-white);
box-shadow: 0 10px 3px rgba(0, 0, 0, 0.2);
}
We select the check mark using its class name. Note that we used a descendent selector with the popup
class name. Without this, I was unable to increase the font size, because the google font library’s style was overriding my rules. I increased the font size to 4 rem. I give it a solid black border, with rounded edges. I give it a bluish background color slightly different from that of the hero section. I set the text color to white, and gave it a box shadow.
How about we make the check mark look like it is sticking out of the popup dialog? We modify the styling of the check mark as follows:
.popup .material-symbols-outlined {
/* Other styling */
margin-top: -20%;
/* Other styling */
}
By giving the check mark symbol a negative top margin, it appears to stick out of the popup dialog.
Finally, let’s style that button:
.popup button {
width: 100%;
margin-top: 30px;
padding: 5px 0;
background: var(--clr-green);
color: var(--clr-white);
border: 0;
outline: none;
border-radius: 8px;
cursor: pointer;
box-shadow: 0 5px 3px rgba(0, 0, 0, 0.2);
}
We increase the button width
, space it away from the rest of the popup contents using margin-top
, and give it a bit of padding
on the top and bottom. We give the button a green background color, and a white text color. We get rid of the border
and outline
of the button. We give it rounded edges, and change the cursor
to pointer
. Finally, we give it a bit of a shadow.
Hiding the popup dialog
The popup dialog does not need to be visible all the time, so let’s hide it. To do that, we’ll do three things. First, we’ll change its top
property to zero, so that it is at the top of the screen. Second, we’ll reduce its size. When the dialog is displayed, we’ll apply a transition effect so that the dialog appears to fly in from somewhere near the top of the screen. Third, we’ll hide the dialog from the page so that you don’t see it.
.popup {
width: 200px;
background-color: var(--clr-white);
border-radius: 8px;
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, -50%) scale(0.05);
text-align: center;
padding: 0 20px 20px;
color: var(--clr-light-gray);
visibility: hidden;
}
We edit the specification for the popup
class with the three changes made above, and the popup dialog is now gone.
Revealing the popup on button click
We need to associate a click event handler with the submit button. In that event handler, we’ll add a CSS style to the popup dialog that displays it in its full glory at the center of the screen. First, let’s define such a CSS style. We just need to undo those three changes that we just made to the popup
class.
.popup-open {
top: 50%;
visibility: visible;
transform: translate(-50%, -50%) scale(1);
}
When the user clicks on the OK button on the popup dialog, we’ll remove this CSS style from the popup dialog. We also add a transition effect to the popup
dialog, so that the dialog appears to be flying in.
.popup {
/* Rest of the style */
transition: 0.5s;
}
Let’s write some JavaScript code to define and add the event handlers to the submit button:
const submitButton = document.querySelector('.hero .btn')
const okButton = document.querySelector('.popup button')
const dialog = document.querySelector('.popup')
submitButton.addEventListener('click', () => {
dialog.classList.add('popup-open')
})
okButton.addEventListener('click', () => {
dialog.classList.remove('popup-open')
})
With that, our popup modal dialog is done.
That’s all folks!
You may download the entire source code from this github repository. If you prefer to copy-paste, here’s our HTML:
<!DOCTYPE html>
<html>
<head>
<title>Modal</title>
<link rel="stylesheet" href="style.css">
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
</head>
<body>
<header class="hero">
<button type="submit" class="btn">Submit</button>
<div class="popup">
<span class="material-symbols-outlined">done</span>
<h2>Thanks</h2>
<p>Your response has been submitted!</p>
<button type="button">OK</button>
</div>
</header>
<script src="app.js"></script>
</body>
</html>
Here’s our CSS:
*, ::after, ::before {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Roboto', 'Open Sans', sans-serif;
}
:root {
--clr-bluish: #3c5077;
--clr-bluish-2: #036099;
--clr-white: #fff;
--clr-light-gray: #333;
--clr-green: #33d692;
}
.hero {
width: 100%;
height: 100vh;
background-color: var(--clr-bluish);
display: flex;
align-items: center;
justify-content: center;
}
.btn {
padding: 10px 50px;
border-radius: 50px;
background-color: var(--clr-white);
cursor: pointer;
font-size: 2rem;
font-weight: 500;
}
.popup {
width: 200px;
background-color: var(--clr-white);
border-radius: 8px;
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, -50%) scale(0.05);
text-align: center;
padding: 0 20px 20px;
color: var(--clr-light-gray);
visibility: hidden;
transition: 0.5s;
}
.popup p {
margin-top: 10px;
font-size: 0.8rem;
}
.popup .material-symbols-outlined {
font-size: 4rem;
margin-top: -20%;
border: 2px solid black;
border-radius: 3rem;
background-color: var(--clr-bluish-2);
color: var(--clr-white);
box-shadow: 0 10px 3px rgba(0, 0, 0, 0.2);
}
.popup button {
width: 100%;
margin-top: 30px;
padding: 5px 0;
background: var(--clr-green);
color: var(--clr-white);
border: 0;
outline: none;
border-radius: 8px;
cursor: pointer;
box-shadow: 0 5px 3px rgba(0, 0, 0, 0.2);
}
.popup-open {
top: 50%;
visibility: visible;
transform: translate(-50%, -50%) scale(1);
}
Here’s our JavaScript:
const submitButton = document.querySelector('.hero .btn')
const okButton = document.querySelector('.popup button')
const dialog = document.querySelector('.popup')
submitButton.addEventListener('click', () => {
dialog.classList.add('popup-open')
})
okButton.addEventListener('click', () => {
dialog.classList.remove('popup-open')
})