Introduction
Modals are User Interface (UI) elements that aim to grab users' attention by displaying information that overlays the entire web page or (rarely) parts of it.
In this article, I'll explain in detail how to create a modal using HTML, CSS, and a bit of JavaScript. The content of the modal would be a login form. Albeit, you could put anything you want in the modal, the lesson here is to show you how it's done therefore, you can easily implement yours or add to what you'll learn therein.
In the end, I'll list other techniques that do not involve the usage of JavaScript when creating a modal.
Our final result
As an inspiration, here is the completed modal, with all its sections highlighted. I am doing this so that you can know the thinking that goes behind the HTML and thus the final design.
As a challenge, you can stop here, and see if you can implement this without reading the remainder of the article.
The HTML
From the image in the previous section, it's evident you'll need the following in your HTML markup:
- A button to trigger the modal
- A container for the modal
- An HTML form
- An image
- A container for the "X" symbol
In software engineering terms, these are the requirements. Below is the HTML code (excluding the DOCTYPE declaration, meta tags, and the opening and closing body
tag).
<main>
<h2>Modal Login form</h2>
<button id="openModal" class="button openModal">Open Modal</button>
<div id="modal" class="modal">
<form class="form modal__content animate" action="#">
<div class="form__row">
<span id="closeModal" class="closeModal" title="Close Modal">×</span>
<img src="profile.png" alt="Avatar" class="form__avatar" width="300px">
</div>
<div class="form__row">
<label for="username" class="form__label">Username</label>
<input
id="username"
type="text"
placeholder="Enter your username"
name="username"
required
class="form__input"
>
<label for="password" class="form__label">Password</label>
<input
id="password"
type="password"
placeholder="Enter your password"
name="password"
required
class="form__input"
>
<button class="button" type="submit">Login</button>
</div>
<div class="form__row modal__footer">
<button
type="button"
id="cancelModal"
class="button cancelModal">Cancel
</button>
<span class="forgetpassword">
<a class="forgetpassword__link" href="#">Forget password?</a>
</span>
</div>
</form>
</div>
</main>
Moving forward, perform the following steps:
- Create a project folder and call it any name the suits you.
- Create an
index.html
file with the DOCTYPE and meta tags. - Type the HTML code above between the opening and closing body tags.
Note: There is an image file referenced in the HTML code, called
profile.png
. It is the avatar depicted in the image of the final result. You can get a similar image from Pexels or Unsplash.
Save your index.html
file and launch it in your browser. The result at this stage is short of pretty, but you are on the right track.
Our next step is to add CSS to the markup, which will add some beauty and usability to the form.
The CSS
The first thing you'll do is to beautify the buttons because as things stand, they are not looking good. Less I forget, before you proceed, kindly do the following:
- Create a stylesheet with the
.css
extension in your project directory. - Link the stylesheet with your HTML file within the
<head>
element. (As a reminder<link rel="stylesheet" href="styles.css">
). Here, my stylesheet is namedstyles.css
.
Now, open your stylesheet, type, and save the following:
.button {
width: 100%;
margin: 8px 0;
padding: 14px 20px;
color: #ffffff;
background-color: #1560bd;
border: 0;
cursor: pointer;
}
The buttons should have some feeling of life in them, but they are still all over the place. That means you have to declare a boundary that will restrict them to where you'll like them to be.
You'll start with the buttons within the form.
Here is what you'll do:
- Declare a width for the button parent.
- Add some margin and necessary styling.
Here, the parent for the buttons within the form is the form
element itself which has 3 classes named form
, modal__content
, and animate
. You'll use modal__content
because this will increase the readability of the stylesheet.
Anyone who reads the stylesheet would know the styles are meant for modal content. This would not be the case if you were to use .form
. The reader will have a hard time figuring out what type of form you are referring to in the codebase.
.modal__content {
width: 80vmin;
margin: 0 auto 15% auto; /* This applies margin to all four sides of the modal */
border: 1px solid #888888;
background-color: #fefefe;
}
Save your file, and refresh your browser.
Next, you'll take care of the image and the "X" sign.
From the final result, you'll recall that the avatar image is situated at the center of the form and the "X" sign is positioned at the top-right, know, that, the avatar image and the "X" sign are both situated in a single form row and if you would like to position the "X" at the top-right location, its parent should have a position
property set to anything other than static
(which is the default). But, (read on).
There is a challenge. You'll need to target this form row alone because you will not perform any positioning on other form rows. How would you solve this? So far, here is what you know:
- The form row containing the avatar image is indeed the first form row.
- You'll need a position property value other than
static
.
To solve the first of these constraints, here is what you'll do:
- You'll target the first row using the
:first-child
pseudo-class. - Next, you'll change the position to
relative
.
In your stylesheet, type the following (don't save, yet):
.form__row:first-child {
position: relative;
text-align: center; /* This will put the image at the center of the form row. */
margin: 24px 0 12px 0;
}
Next, as evident from the final result, the image is round, and its size reduced compared to its current state. To achieve the rounded shape, you'll use the border-radius
property and for the size, take a guess, the width
property.
In CSS:
.form__avatar {
width: 40%;
border-radius: 50%;
}
That's it for the image, if you'd like, you can save and observe your result.
Next, is the "X" sign.
Since the first row already has the position set to relative
, you'll need to do the following to position "X" in the desired location:
- Set its position to
absolute
. - Align it with offset properties.
- Add a color.
- Increase its font size.
- Make it stand out.
- Change color and mouse pointer when user interacts with.
All these in code:
.closeModal {
position: absolute;
top: 0;
right: 25px;
color: #000000;
font-size: 35px;
font-weight: bold;
}
.closeModal:hover,
.closeModal:focus {
color: red;
cursor: pointer;
}
Save and refresh, the "X" should look a lot better.
We are approaching the modal design in a top-down approach, so far, you've positioned the modal; styled the first row of the form.
Next, is the form labels and inputs.
The form label is bold, therefore the following will take care of that:
.form__label {
font-weight: bold;
}
From the final result, the form inputs occupy almost the entire width of the form, this means you'll need something like width: 100%
, and some additional styling (like border, margin and padding) to make it stand out. Write the following:
.form__input {
display: inline-block;
width: 100%;
margin: 8px 0;
padding: 12px 20px;
border: 1px solid #cccccc;
/* This is not complete, check below */
}
Save, and you'll realize that there is a tiny problem, as shown in the image below.
The form inputs are crossing their boundary, this is not good for the design. To fix this, you'll need the box-sizing
property and a value of border-box
. Why? If you might ask. Here is why.
The reason the inputs are crossing their boundary is because of 3 reasons listed below:
- The inputs have a width of
100%
. - The inputs have margin and padding values.
- The inputs have a border greater than zero.
All these add to the final computed width of the inputs, hence the width of the inputs is longer than you'll expect. It's not magic, it's how the CSS box model works.
To prove my point, temporarily do the following to your stylesheet:
- Change the padding value to
12px 0px;
. This will remove the left and right padding. - Change the border value to
0
. This will remove the padding on all sides of the form inputs. - Save your file, and refresh your browser. You'll realize that the input now plays nicely but it's invisible.
- Now add the following:
outline: 1px solid red;
. We use an outline because it does not add to the width of the element.
Therefore, the styles for .form__input
should match the following:
/* This is TEMPORARY. It's meant to prove a point. */
.form__input {
display: inline-block;
width: 100%;
margin: 8px 0;
padding: 12px 0px;
border: 0;
outline: 1px solid red;
}
Refresh your browser, your form input should match the image below.
I hope this clears things up. Now, undo the temporary steps taken earlier and let's continue.
Therefore, when you use box-sizing: border-box;
, you are telling the browser to account for any border and padding values you specify for an element width and height and the content box (the default box-sizing) will shrink to absorb the extra width (which is what you want in this scenario).
In this regard, your final styles for the .form__input
class should match the following:
.form__input {
display: inline-block;
width: 100%;
margin: 8px 0;
padding: 12px 20px;
border: 1px solid #cccccc;
box-sizing: border-box;
}
Save and refresh your browser and inputs should work as expected.
Still on the form itself. Next, you will style the modal footer. But before styling the modal footer as a whole, you'll apply styles to its element one at a time. The elements are the "Cancel" button and the "Forget password" link.
The next CSS code block gets the job done. Add it to your stylesheet and don't forget to save it before refreshing your browser.
.cancelModal {
width: auto;
padding: 10px 18px;
background-color: #aaaaaa;
}
.forgetpassword {
padding-top: 16px;
}
.forgetpassword__link {
padding: 0.3em;
}
It looks better, but it's still not in the state that you would like it to be, the reason being that in the final design, the "Cancel" and "Forget password" are aligned to the edges of the modal.
This is the part where you apply styling to the modal footer as a whole.
.modal__footer {
display: flex;
flex-wrap: wrap; /* Elements would move to a new line if necessary. */
justify-content: space-between; /* Space between the modal footer elements. */
background-color: #f1f1f1;
}
The result in the browser shows you are almost done.
In its current stage, the form looks rigid. You'll need to apply some padding.
.form__row {
padding: 16px;
overflow: auto;
}
Save and refresh and it should look better.
There you go, you are done with the modal, or, are you? Because at this stage, the modal is always in view (a modal should show based on human interaction), there is no indication that indeed it's a modal.
This means you have to do 3 things, which are:
- Add some styles to the modal to show its modal.
- Hide the modal.
- Provide a mechanism to show the modal.
You'll start with the first one — additional styles to the modal.
Note that you'll want the modal at the center of the page when it's active, therefore, you'll use position: fixed
. Also, it has to appear over all other elements. z-index: 1
would take care of this.
Here you go:
.modal {
width: 100%;
position: fixed;
top: 0;
left: 0;
height: 100%;
z-index: 1;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4);
padding-top: 60px;
}
When you save your file and preview the result in the browser, you'll notice that you are almost done. Almost, because there are some changes that you need to make.
Currently, your user would find it difficult to use the "Cancel" button and "Forget password" on smaller viewports, as shown in the screenshot below.
At this viewport size, it's best to make it easier for the user to click the button and links. There is no better way than to stack them. The next code block would take care of this.
/**
* This would target a viewport of 480px and less.
*/
@media screen and (max-width: 30em) {
.cancelModal,
.forgetpassword {
width: 100%;
}
.forgetpassword__link {
display: block;
padding: 10px;
outline: 1px solid #1560bd;
text-align: center;
text-decoration: none;
}
}
If you've done everything right, you should have an output similar to the image below.
Now, you are done with the modal, but it's still in view, you need to hide it and show it on demand because that is the nature of modals.
Go ahead and update the styles for the .modal
class by adding the following:
.modal {
/* Previous styles remains the same */
display: none; /* Add this */
}
The modal is hidden, when you press the "Open modal" button, nothing happens. Speaking of which, the width of the "Open modal" button is way longer than usual. The next block offers a fix.
.openModal {
width: auto;
}
Still, when you press the button, you get nothing. You'll need JavaScript to show the modal.
The logic behind the JavaScript is described below:
- Get the modal.
- Get the "Open modal", "Close modal" buttons, and the "Cancel modal" sign.
- Attach an Event listener that will open the modal to "Open modal".
- Attach an Event listener that will close the modal to "Close modal".
- Attach an Event listener that will also close the modal to "Cancel modal".
- When the user clicks anywhere outside the modal, close the modal.
Now, create a JavaScript file in your project directory and, save it with the .js
extension. Link it with your HTML file using the <script>
tag.
For example, <script src="script.js"></script>
. Here, my JavaScript file is saved with script.js
, you can give your JavaScript file any name that you'd prefer.
When you are done, type the following:
let modal = document.getElementById('modal');
let openModal = document.getElementById('openModal');
let closeModal = document.getElementById('closeModal');
let cancelModal = document.getElementById('cancelModal');
openModal.addEventListener("click", () => {
modal.style.display = 'block';
});
closeModal.addEventListener("click", () => {
modal.style.display = 'none';
});
cancelModal.addEventListener("click", () => {
modal.style.display = 'none';
});
// When the user click anywhere outside
// the modal, close it.
window.onclick = (event) => {
if (event.target === modal) {
modal.style.display = 'none';
}
}
Save your file and test it out. The modal should show when you click the "Open modal" button, and when you click anywhere outside the modal, the "X" sign or the "Cancel" button, the modal should go back to its hidden state.
It's all good. Meanwhile, the modal, in its current state, pops out suddenly when the user clicks on the "Open modal" button. It could use some animation to soften things up.
Switch back to your CSS file, and add the following:
.animate {
-webkit-animation: zoomit 0.6s;
animation: zoomit 0.6s;
}
@-webkit-keyframes zoomit {
from {
-webkit-transform: scale(0);
}
to {
-webkit-transform: scale(1);
}
}
@keyframes zoomit {
from {
transform: scale(0);
}
to {
transform: scale(1);
}
}
That completes the modal. Save all your files and test them out.
A note on Accessibility
The modal login form is completed, but know this: it is unusable when your users have JavaScript disabled. The question is: How do you cater to such users. Here are my suggestions:
- Perform a check for JavaScript availability, if this check fails, ensure the "Open modal" button links to another page that contains the login form.
- Disable the button and tell the user to enable JavaScript. I do not recommend this, but still, it's among your options in this situation.
Other (non-JS) solutions
A quick search online reveals that indeed you can create a modal without JavaScript, they are listed below:
Demo
The demo is available online at the following URL: https://ziizium.github.io/my-webdev-notes/modalLoginForm/
Code
The code is available in the my-webdev-notes
GitHub repo linked below.
ziizium / my-webdev-notes
Code snippets for series of articles on DEV about my experiments in web development
My WebDev Notes
This repositiory contains code snippets, and links for series of articles on DEV about my experiments in Web development.
List of articles
- My WebDev Notes: CSS Loaders published on the 25th February 2020
- My WebDev Notes: Filter table published on the 1st April 2020
- MyWebDev Notes: Center page elements with CSS Grid published on the 3rd of April 2020
- My WebDev Notes: Photo gallery with CSS Grid published on the 7th of April 2020
- My WebDev Notes: Fullscreen overlay navigation published on the 13th of April 2020
- My WebDev Notes: A simple and accessible accordion published on 28th of April 2020
- My WebDev Notes: How to create a tooltip with HTML and CSS published on 3rd February 2021
- How to create a modal published on 22nd June 2021
Conclusion
This article explained how to create a modal by taking you through the journey of the thought process behind every coding decision. Armed with this knowledge, you are in a better position of creating your modal from scratch or modify what you've learned to suit your needs.
Credit
Cover photo by Mika Baumeister on Unsplash.
Edited July 4, 2021: Grammar fix.
Top comments (1)
Awesome