A modal. A small box that pops up to tell you something important. How hard can it be? Wellllll. Medium hard, I’d say. There’s quite a few considerations and a few tricky things to get just right. Let us count the ways.
Where in the DOM?
I typically plop a modal’s HTML before the closing </body>
tag.
<div class="modal" id="modal"></div>
</body>
</html>
That’s primarily for styling reasons. It’s easier to position the modal when you’re dealing with the page-covering body rather than an unknown set of parent elements, each potentially with their own positioning context.
How does that fair for screen readers? I’m not an accessibility expert, but I’ve heard modals are pretty tricky. Rob Dodson:
For anyone who has ever tried to make a modal accessible you know that even though they seem pretty innocuous, modals are actually the boss battle at the end of web accessibility. They will chew you up and spit you out. For instance, a proper modal will need to have the following features:
- Keyboard focus should be moved inside of the modal and restored to the previous activeElement when the modal is closed
- Keyboard focus should be trapped inside of the modal, so the user does not accidentally tab outside of the modal (a.k.a. “escaping the modal”)
- Screen readers should also be trapped inside of the modal to prevent accidentally escaping
Rob suggests The Incredible Accessible Modal Window demo. I’ve also seen a recent example by Noah Blon and one by Nicolas Hoffman. Also, ARIA Live Regions are relevant here.
If you’re handling focus yourself, having the modal be at the bottom of the document seems acceptable.
Centering
We have a complete guide to centering in CSS.
One of my favorite tricks is in there. The one where you can center both vertically and horizontally without explicitly knowing the width or height:
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
This is great for modals, which are typically exactly centered and might have different versions of different widths. Heights are even more likely to change since height always related to the content inside.

If you are absolutely certain of the height and width of the modal, you could consider other centering methods. I bring that up because there is a chance of slightly-blurry text with transforms, so if you are having trouble with that, look into the other methods in the centering guide (e.g. negative margins).
Fixed?
Notice we used position: fixed;
. The point of this is that a user could be scrolled down the page, trigger a modal, and have the modal be just as centered-and-visible as it would be if they weren’t scrolled.
I guess, in general, I’d consider fixed positioning fairly safe these days, even on “mobile”. But if you know you’re dealing with a pretty healthy population of very old mobiles, fixed positioning can be a problem and you might consider something like position: absolute;
and forcing a scroll to the top of the page. Or something, I dunno; do testing.
Dealing with Width
On large screens, the typical modal look is not only centered but also of limited width (like the above screenshot).
.modal {
/* other stuff we already covered */
width: 600px;
}
That’s red flag territory. We got what we wanted on large screens, but we know there are plenty of screens out there that aren’t even 600px wide all the way across.

Easily fixable with max-width
:
.modal {
/* other stuff we already covered */
width: 600px;
max-width: 100%;
}

Dealing with Height
Setting a height is even more red flaggy. We know content changes! Plus, the transforms-centering technique is more than happy to cut off the top of the modal with no scrollbar to save you:

Setting a max will save us again:
.modal {
/* other stuff we already covered */
height: 400px;
max-height: 100%;
}

Dealing with Overflow
Now that we’re in the business of setting heights, we need to consider overflow. It’s tempting to use an overflow
value right on the .modal
itself, but there are two problems with that:
- We might want some elements that don’t scroll
- Overflow will cut off a
box-shadow
, which we may want
I’d suggest an inner container:
<div class="modal" id="modal">
<!-- things that don't scroll -->
<div class="modal-guts">
<!-- things that scroll -->
</div>
</div>
In order for the guts to scroll, it will need a height. There is a variety of possibilities here. One is to position it to cover the entire modal, and then add the overflow:
.modal-guts {
/* other stuff we already covered */
/* cover the modal */
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
/* spacing as needed */
padding: 20px 50px 20px 20px;
/* let it scroll */
overflow: auto;
}

The Buttons
The point of a modal is to force an action before anything else can be done. If you aren’t forcing an action, consider UI other than a modal. There needs to be some kind of way out of the modal. Option buttons are common (i.e. “Delete” / “Cancel”). A close button is also common. Let’s give ours a close button.
Having a close button that is always visible seems smart, so the user can’t be in a state where it’s not visibly clear how to close the modal. That’s why we made the non-scrollable area.

Dealing with the overlay
A modal is often accompanied by a full-screen-covering overlay. This is useful for a number of reasons:
- It can darken (or otherwise mute) the rest of the screen, enforcing the “you need to deal with this before you leave” purpose of a modal.
- It can be used to prevent clicks/interactions on stuff outside the modal.
- It can be used as a giant close button. Or “cancel” or whatever the most innocuous action is.
Typical handling:
<div class="modal" id="modal">
<!-- modal stuff -->
</div>
<div class="modal-overlay" id="modal-overlay">
</div>
.modal {
/* stuff we already covered */
z-index: 1010;
}
.modal-overlay {
/* recommendation:
don't focus on the number "1000" here, but rather,
you should have a documented system for z-index and
follow that system. This number should be pretty
high on the scale in that system.
*/
z-index: 1000;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
Closed with a class (rather than open with a class)
I find it highly tempting to make the default .modal
class be hidden by default, likely with display: none;
. Then to open it, add an open class, like .modal.open { display: block; }
.
See the display: block;
there though? I’d consider that a problem. display: none;
is quite useful because it hides the modal both visually and from assistive technology. It’s easier to apply it on top of an existing display
value than it is to override with a guess of a value. Meaning your .modal
could use display: flex;
or display: grid;
or whatever else is useful. Different variations of modals could use whatever they want without worry they’ll be reset to display: block;
.
.modal {
/* for example... */
display: flex;
}
.modal.closed {
display: none;
}
Toggling Openness
Here’s the most basic possible way of opening and closing with what we have so far:
var modal = document.querySelector("#modal");
var modalOverlay = document.querySelector("#modal-overlay");
var closeButton = document.querySelector("#close-button");
var openButton = document.querySelector("#open-button");
closeButton.addEventListener("click", function() {
modal.classList.toggle("closed");
modalOverlay.classList.toggle("closed");
});
openButton.addEventListener("click", function() {
modal.classList.toggle("closed");
modalOverlay.classList.toggle("closed");
});
This does not deal with accessibility yet. Remember that’s a consideration we talked about above. Here’s a demo that deals with moving focus, trapping focus, and returning focus back from whence it came.
Style Considerations Demo
See the Pen Considerations for Styling a Modal by Chris Coyier (@chriscoyier) on CodePen.
What did I miss? Feel free to fork that thing and let me know.
Shouldn’t it be
max-height:100%
when styling for flexible heights?Covered a lot of aspects..have to say an excellent guide on modals.
Yes indeed! Covered that in the [#article-header-id-4](Dealing with Height) section.
I was trying to point out if that
max-width:100%
in Height section was a typo? :Dah ha! Got it. Yes.
One important UX consideration you missed is regarding scrolling the background while a modal is open. Typically, if you are on a long, scrolling page, while the modal is open you can still scroll the background, which in most cases is undesirable. The fix to this, is to add
overflow-y: none
to<body>
via a class toggle when opening/closing the modal.Interesting. Why does it matter that you can scroll the page? Cause it feels/looks weird? I’d worry that hiding the vertical overflow would scroll them back to the top, which also seems weird?
I could maybe see an argument similar to the aforementioned accessibility issue; returning the viewport focus to where it was before the modal was opened. If you prevent additional scrolling you can be assured that the user will return to their previosu place when the modal has finished.
I’m not sure if that would be helpful universally, but if you’re in a database or application-like interface I could see the benefit of being able to more granularly set navigational stages for users. So, probably depends on the goals and scope of the platform.
Also as a note, adding overflow:hidden to freezes the viewport where it currently is, though you’d probably want to deal with the scrollbar disappearing as well.
I recently encountered this, and that has two problems:
If the page was scrolled, when you return to it then it is on the top. Totally undersirable for infinite scrolling pages.
If you don’t hide everything with the modal (such as a navbar), and even if so, the page bumps horizontally from the scrollbar being visible/invisible.
Seems the currently Chrome (51.0.2704.103 on OSX) does not scroll back to top when overflow-y is hidden on the body. Just try it out on this page by modifying it via inspector.
I see this best handled with Javascript, capturing and cancelling the scroll event.
I have never experienced the issue where the page scrolls back to the top if you make
body
overflow-y: none
. In Chrome the page just stays where it is. And obviously when you remove that rule the scrollbars come back just like normal. Are there specific browsers where it scrolls to the top? I definitely agree that if this was the case that would be poor UX. I just have never seen it.On a related note, several popular modal libraries add this style to the body tag.
Here is a pen for testing: http://codepen.io/jakobud/pen/rLdjpX
And regarding why scrolling the background is undesirable, I guess to me, its important that when a modal pops up and I close it later I expect the page to be right back to where it was. If I accidentally scrolled the background without immediately realizing it then I have to find where I was on the page previously.
It’s pretty easy to accidentally scroll the background if your modal content is long enough that it has it’s own scrollbar (whether or not that itself is bad UX is a whole other conversation ;-)
Also, depending on how opaque your modal overlay is, the user might not even realize they are scrolling the background.
I thought that was just the way it is, but your test works perfectly so apparently I have a bug somewhere else that makes it jump to the beginning. Thanks a lot for debugging my code without even seeing it (:
As an aside, I’m curious to what a “documented system for z-index” would be like?
Maybe like this (just one way) ;)
Here’s a good example of using SCSS to manage a z-index system: https://www.sitepoint.com/better-solution-managing-z-index-sass/
Wouldn’t it be better to use the following instead of translate:
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
seen at this pen:
works just as well all the way down to IE8 without prefixing.
Seems clever to me! That should probably be used more often since the browser support seems so good and the risk of blurry text is gone.
The downside of this is that it only works when a fixed height is specified, which is less than ideal.
My preferred, non-blurry solution is using flexbox on the container for vertical centering:
And
margin: 0 auto;
on the floating window for horizontal centering (which provides a nice fallback for browsers that can’t handle flexbox, otherwise we could just usejustify-content: center;
on the container to keep all the centering code in one place).That’s what i did at first, then i switched to the flexbox approach. It kinda feels more natural and the right way to me.
Lately i also developed a tiny modal plugin using this technnique.
Maybe i will update it making it more accessible.
The key problem with this approach is that you have to hard code your modal width and height.
left: 50%; top: 50%; translate(-50% -50%)
rules ;-)True, however since it is a modal, generally you want a gutter around it, so that you can see to the content behind, and so the user knows its a modal and not another page, or some other ui element.
In my opinion if there is no gutter its not a modal, it becomes a slide out, or something else.
Since you need a gutter you can define the top, left, etc. as 5% or something, then you can define a max-width, max-height, width and height to get a great look for all devices. So, the only downside to this method is if your modal has a small amount of content, then the modal would be larger than its content. Not necessarily perfect, but you get a consistent look across all browsers with easy setup and no browser issues, unlike the translate method.
Further, again in my opinion, having a modal open with a consistent size is much nicer than it opening at different sizes, even if there is some whitespace. This is especially true when the modal is centered because the close button will always be in the same spot increasing usability. If it changes size and is centered, the close button will always be in a different place.
The flexbox method seems good too, however the fall back aligns the modal toward the top, for IE10-, and requires all kinds of prefixing mess to get consistent support. And again if you are going for a consistent size for better usability, my example is better, because it works in IE8+ without any issues.
Oops, just tested, it does not center vertically in IE8, but it does in 9 & 10.
What about working with animating the modal and overlay on .open and on .close ? I think that is the real issue to discuss when it comes to modals, when you say display: none, transform: translate(x) doesn’t work anymore, etc etc etc. I know what you’re going to say… “use javascript” :P but what if there are cleaner ways using only css and adding/removing classes ?
You could probably do
instead… (because that will animate). But probably should mess with aria-hidden too then so it’s hidden from AT as well.
Relevant: https://css-tricks.com/snippets/css/toggle-visibility-when-hiding-elements/
I usually use line-height to center my modal, which allow it to overflow in the bottom and allow it to move in its wrapper if required.
This is up for discussion I guess, but I find it a better UX when the overlay scrolls instead of the modal window itself. This changes the way you would implement
max-height
though, making it easier of the modal window node is inside the overlay node. Not sure what impact this might have on accessibility though (if any).There is that whole thing with main document scrollbar that Bootstrap does when it sets
overflow: hidden
on body so content outside modal can’t be scrolled but then adds offset to modal position so there is no layout shift during opening/closing of modal. So. much. bikeshedding.We use the translation method for centering as well but there’s one critical gotcha that’s been a nightmare – Chrome has problems with odd-height containers which causes subpixel issues – fonts can wind up looking blurry and elements inside may shift awkwardly. It’s been a real pain in my neck to deal with.
As usual an excellent article Chris.
Given that you would like the modal code to go next to the closing body tag, how would you handle modals in a CMS like WordPress?
I actually did some modal design recently: http://codepen.io/fauxserious/pen/mEPYrB
Is there any difference between
and
?
That’s interesting I’ve never really thought about that…
I guess… no.
Yes, there’s a huge difference between them.
The code,
Here the width of the element will be 600px wide irrespective of the width of the container. When you apply,
max-width: 100%
the width of element will change to 100% if the size of container is reduced than width of the element.Whereas,
This code will keep the element’s width equal to the container’s width until the container’s width is less than 600px. Once the container spans more than 600px, say 960px, the element will remain 600px wide.
To center it, apply
margin: auto;
Hope this helps!
I think I have explained it wrong. Chris is Right!
Ignore my previous reply.
Thanks!
What do you think of the HTML5 element
<dialog>
, supported in Blink and with a polyfill available?The default styling is still pretty bad, you need custom CSS to get it to work well on mobile.
You could add closing it when clicking outside. I also prefer writing the overlay inside the .modal, so you can toggle them just with one simple change instead of two.
I created a vanilla css modal for Picnic CSS in http://picnicss.com/documentation#modal , another thing I recommend developers can do is add an even listener for
ESC
to close it.Thanks for the article, I missed a couple of things so I’ll make sure to enhance it.
What you miss.. Well, on an iPhone you have to “lock” the scroll for the main page.
If the page is scrollable then you can still scroll that page even when there is a modal on top of it. Resulting in strange behaviour.
But you have to do it with JavaScript. On open = lock main page (overflow hidden with an offset) and On close = remove the lock.
And offcourse the ” -webkit-overflow-scrolling: touch;” cannot be missed.
Maybe modal overlay could be a pseudo-element too :)
ooo good idea!
I’ve also seen a giant box-shadow. I guess it depends on how useful the second element is. Is it more of a pain to manage two elements? Or is it useful because of the click handling?
Came here to say this also. ::after is something i’ve used before for modals and it works nicely. Honestly I think from a DOM perspective it makes more sense to have it as a pseudo-selector as it an overlay doesn’t really belong in the flow of the document anyway. It’s purely presentational in nature so is best contained and controlled directly through CSS.
@Elliot, unless you want to prevent the click through to content below the overlay (then it kind of serves more of a purpose than just presentation). I like to bind a click even to the overlay to close the modal.
I’ll typically wrap the modal in a
<div>
and then use that containing<div>
as the overlay with the modal placed “fixed” inside of it. That way you can add a click-event to the overlay if you want and your still only dealing 1 element that shows and hides instead of 2. Also, Z-index becomes a non-issue using this method which, in my experience, is a real problem with Bootstrap and other such library implementations.@Todd Yes of course :)
A couple additional things I like to do are close on click outside and close on ESC press. I feel like some people expect that functionality, who knows though.
I am with You dude! I done the ESC and close outside on my latest webpage. I also added ENTER key for acceptation of default option – for example I have modal which popups when people press print icon on the webpage. The modal then reminds people of responsible printing – do not waste paper – and ENTER is print and ESC kills the modal.
Hi,
Do you think it´s ok to make modal-overlay using :before of the modal window itself? I usually do it so I dont have to make another div in HTML and I dont have to think about it in Javascript code as well.
Hope it makes sense and please excuse my English :). Thank you
Oh, I just read the comments :D I think this is better idea as well :)
Since we are putting the overlay element next to the modal element, we can just toggle the modal class name with JavaScript in order to show/hide the modal, and use the
+
CSS selector to show/hide the overlay:What a great article about modals, nice one Chris.
Regarding accessible-modal-dialog, unfortunately it is no longer maintained (last commit 10 months ago, no reply on issues and PRs).
We forked it at Edenspiekermann to create a11y-dialog, a super tiny yet accessible modal script (without any styling consideration so your post comes in handy for anyone using it). We improved a lot over the initial library, starting with removing jQuery dependency. :)
In case it helps.
Regarding the responsiveness:
I was wondering what’s better, to set the width/height to 100% and then limit it by a certain number, or do what you did here – set it to a certain number and limit it to 100%. Both solutions seem to work similarly.
What do you think? Can you give me some reasons for choosing one of those?
(Just noticed that someone else asked the exact same thing. Feel free to delete this one :) )
:target
to open modal https://codepen.io/mihdan/pen/GqdKQpI ran into this a few times. If you want the scroll in the modal to be natural on iDevices, you might want to consider https://css-tricks.com/snippets/css/momentum-scrolling-on-ios-overflow-elements/
We can avoid blurry text with the follow code in the element: