Building a Fully Functional Modal Without JavaScript
Tags: html, css, a11y, webdev
Published: 2021-08-25 02:40:54

There is a common misconception that in order to create a [modal](http://web-accessibility.carnegiemuseums.org/code/dialogs/) (sometimes also called a dialog), it is necessary to use JavaScript.

In this post, we'll build a JavaScript-free modal.

## Using the details tag

HTML has a [details tag](https://html.com/tags/details/), which can be used to used to build all kinds of powerful dropdowns and toggle menus.

Let's create a simple HTML file that uses a details tag and summary tag (which is a necessary counterpart to the details tag).

I'm saving mine as `index.html`:

```html
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>JavaScript-Free Modal</title>
        <link rel="stylesheet" href="modal.css" type="text/css">
    </head>
    <body>
        <details class="modal">
            <summary class="modal__toggle"></summary>
            <div class="modal__background">
                <div class="modal__body" tabindex="-1" role="dialog" aria-labelledby="modal__label" aria-live="assertive" aria-modal="true">
                    <p id="modal__label">The modal is open! 🎉</p>
                </div>
            </div>
        </details>
    </body>
</html>
```

Some of the attributes I've added are important to ensure our modal is accessible and WCAG-compliant: `tabindex`, `role`, `aria-labelledby`, `aria-live`, and `aria-modal`.

As you might expect, our modal won't work quite yet, because we haven't added any styles to it.

## Styling our modal

The snippet I provided references a CSS file in the same directory called `modal.css`.

Let's walk through what that file could look like.

First, we'll style the toggle button. The following code uses the `[open]` attribute to place the `<summary>` tag, which toggles the modal, atop the modal when it is opened:

```css
@charset "UTF-8";
.modal[open] .modal__toggle {
    left: calc(50vw + 140px);
    top: calc(15vh + 20px);
    position: fixed;
    z-index: 2;
}
.modal[open] .modal__toggle:focus {
    outline: 2px solid #00f;
}
```

We can then dynamically modify the text within the `<summary>` tag, depending on whether the modal is open:

```css
.modal__toggle::before {
    content: 'Open Modal';
}
.modal[open] .modal__toggle::before {
    content: '✖';
}
```

We can also remove the default arrow that browsers add to the left of `<summary>` tags:

```css
.modal__toggle {
    color: #00f;
    list-style: none;
}
.modal__toggle::-webkit-details-marker {
    display: none;
}
```

And last but not least, we can improve the hover state of our toggle button:

```css
.modal__toggle:hover {
    cursor: pointer;
    opacity: .8;
}
```

Next, we'll style the modal background:

```css
.modal__background {
    background-color:rgba(0, 0, 0, .25);
    display: flex;
    height: 100vh;
    justify-content: center;
    left: 0;
    opacity: .8;
    position: fixed;
    top: 0;
    width: 100vw;
    z-index: 1;
}
```

This background dims the rest of the page, to focus the user's attention on the modal.

Lastly, we'll style the modal body:

```css
.modal__body {
    background-color: #fff;
    box-shadow: 0 0 10px rgba(0, 0, 0, .25);
    font-size: 1.5rem;
    font-weight: 700;
    padding: 40px 20px;
    position: fixed;
    text-align: center;
    top: 15vh;
    width: 300px;
    z-index: 1;
}
```

That's all! You have a working modal, without JavaScript.

Here is a [demo](https://noscript-modal.vercel.app/) if you want to try it out.

## Accessible modals without JavaScript

Our modal has a slight problem.

Because we aren't using JavaScript, we can't listen for whether the user has tried to exit the modal with by pressing <kbd>ESC</kbd>.

Our modal is otherwise considered accessible, and this particular feature is not a requirement for a [WCAG-compliant modal](https://www.w3.org/WAI/GL/wiki/Using_ARIA_role%3Ddialog_to_implement_a_modal_dialog_box).

If you don't absolutely have to avoid JavaScript altogether, I'd strongly recommend a simple script to listen for <kbd>ESC</kbd> key presses:

```js
document.addEventListener(`keydown`, (e) => {
    if (e.keyCode === 27) {
        document.querySelectorAll(`.modal`).forEach((modal) => {
            modal.open = false;
        });
    }
});

```

## Conclusion

I think it'd be cool to see a widely adopted tag specifically for modals in the spec, *especially* considering that so many CSS frameworks that wouldn't otherwise need JavaScript use it just for modals.

The [dialog tag](https://www.w3schools.com/TAGS/tag_dialog.asp) is an attempt at this, but [it still lacks support in several major browsers](https://caniuse.com/?search=dialog).

Anyway, this is just one of countless little tricks we can use `<details>` and `<summary>` for, and I hope you enjoyed it!

The [full code](https://github.com/natclark/noscript-modal) is available as a repository on my GitHub account.