How to create a modal

Habdul Hazeez - Jun 22 '21 - - Dev Community

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.

A modal with its sections highlighted

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">&times;</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>
Enter fullscreen mode Exit fullscreen mode

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.

Basic HTML for a modal

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 named styles.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;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

Save your file, and refresh your browser.

An incomplete modal design

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;
}
Enter fullscreen mode Exit fullscreen mode

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%;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

Save and refresh, the "X" should look a lot better.

An highlighted X sign of the modal

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;
}
Enter fullscreen mode Exit fullscreen mode

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 */
}
Enter fullscreen mode Exit fullscreen mode

Save, and you'll realize that there is a tiny problem, as shown in the image below.

Overflow of form inputs in the modal design

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:

  1. Change the padding value to 12px 0px;. This will remove the left and right padding.
  2. Change the border value to 0. This will remove the padding on all sides of the form inputs.
  3. Save your file, and refresh your browser. You'll realize that the input now plays nicely but it's invisible.
  4. 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;
}
Enter fullscreen mode Exit fullscreen mode

Refresh your browser, your form input should match the image below.

Highlighted form inputs

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;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

The result in the browser shows you are almost done.

An incomplete modal design

In its current stage, the form looks rigid. You'll need to apply some padding.

.form__row {
    padding: 16px;
    overflow: auto;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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.

A modal form in Firefox web browsers with the Developer Tools docked to the right

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

If you've done everything right, you should have an output similar to the image below.

A modal form in Firefox web browsers with the Developer Tools docked to the right

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 */
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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';
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

GitHub logo 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






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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .