HTML, CSS, JavaScript modal popup

Muhammad Saqib Ilyas
8 min readOct 22, 2023

--

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:

A preview of our modal popup dialog

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:

The page after some basic styling

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 page after basic styling on the popup

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.

Styled up popup dialog

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.

Making the check mark stick outside the popup

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.

Popup after styling the button

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')
})

--

--

Muhammad Saqib Ilyas

A computer science teacher by profession. I love teaching and learning programming. I like to write about frontend development, and coding interview preparation