CSS Overlay Techniques

There are several techniques for creating overlays: from using an absolutely positioned element to outlines and pseudo-elements. In this article we are going to explore each technique’s styles with their pros and cons.

Design patterns, a set of best practices and techniques that aim to solve some of the most common design “problems”, are usually presented in the context of design principles. One of these design principles is the “Stay On Page” principle. This principle is based on the fact that page refreshes are disruptive to the user’s mental flow, causing what is known as “change blindness”, and that we need to be able to avoid breaking the visual flow of the user wherever and whenever we can.

We can decide intelligently when to keep the user on the page and model his process. One way to keep the user on the same page is by trying to include some of the experiences in the context of the current page, by displaying a “mini page”, or a pop-up dialog, in a lightweight layer over the current page. This lightweight layer is what we call an overlay.

Lightweight overlays can be used for asking questions, obtaining input, introducing features, indicating progress, giving instructions, or revealing information. They can be activated directly by user events (e.g., clicking on an action, hovering over objects) or be provided by the web application at various stages in the completion of an action.” — Designing Web Interfaces

When the user interaction is only accepted in the pop-up modal, a Lightbox effect is usually applied and the rest of the page is dimmed, indicating its inactivity.

The aim of this tutorial is to introduce you to several techniques that can be used to create this dimmed overlay with CSS, and determine the pros and cons of each technique as we go over them.

Technique #1: Absolutely positioned element

The first way that an overlay can be created is by absolutely positioning an HTML element on the page. There would be an empty div in the markup, and with CSS this div is positioned absolutely and given a high z-index value to make sure it stays on top of all other elements on the page, except the modal which is opened on top of this overlay, which will get a even higher z-index than the overlay.

<html>
  <body>
   <div class="overlay"></div>
   
  <body>
<html>

Supposing we have already added an empty div to the markup and given it a class .overlay, the CSS to position this overlay on the page is:

html, body{
  min-height: 100%;
}
body{
  position: relative;
}
.overlay{
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 10;
  background-color: rgba(0,0,0,0.5); /*dim the background*/
}

The code is very simple, but there are a few things to be aware of when using this technique.

First, you need to make sure that the overlay is positioned absolutely with respect to the body. So, if the overlay is contained in another div for example and that other div has a position set to relative, then the overlay will be positioned absolutely with respect to its container, not the page body. So you have to either let the overlay be a direct child node of the body, or make sure none of its ancestors has a position set to relative.

Also, you need to make sure the content of the page expands down to the bottom of the viewport or more, because the body expands to fit the height of its content (assuming the content is not positioned absolutely, of course), and if there isn’t enough content to expand the body height to the bottom of the viewport, then the overlay, which is getting 100% the height of the body, won’t reach the bottom of the viewport either, and so it won’t be covering it.

To avoid this, and not have to worry about the amount of content on the page, and still get an overlay which covers the entire viewport size, you should set a height on the root html element and the body. There is another thing to remember, though, when you set a height on the html and body elements:

If you give the html element a height of 100% (100% height relative to the viewport), and give the body a 100% height too (which is computed relative to the root html), then you’re setting the height of both of them to be 100% the height of the viewport, and so, no matter how far down the content of the body extends, their height remains equal to that of the viewport, and so will the height of the overlay. In this case, if you scroll down the page, the overlay will scroll up and you’ll see the content below it without an overlay, as if the overlay’s been cut off.

The solution here is to set a minimum height on the root element and on the body instead of setting a height value, which is preferable in most situations. By setting a minimum height, you’ll make sure that their height reaches the bottom of the viewport, and increases as the content increases. And lastly, to make the overlay’s height increase and have it expand to cover all the content on page scroll, you must set a position:relative; on the body so that the overlay’s height expands as the body’s height expands.

Another thing to note about this technique is not to use unnecessarily high z-index values. A lot of developers tend to use very high z-index values like z-index: 999999; when they position an overlay, or any other element, on top of other elements on a page. This is not necessary. Most of the times a z-index value of 10, or sometimes even less, is more than enough to keep an element on top of others on the page. You just need to know if there are other elements getting a z-index, and if there are, just set the z-index of the overlay higher than the highest of the other elements.

And finally, you should also remember that with this technique you’re adding an empty div to your markup, which of course is non-semantic.

An advantage of using this technique is that it is supported in all major browsers and also older ones, down to IE8.

I have set up a pen on Codepen so you can test the result of this technique here. Try replacing the min-height on the html and body with height, or removing the relative position from the body to see how the overlay gets cut off at the bottom when you scroll.

See the Pen Creating an Overlay with an Absolutely Positioned Element by Sara Soueidan (@SaraSoueidan) on CodePen

Technique #2: Element with fixed position

The second way you could add an overlay is very similar to the previous one, and uses the same .overlay element in the markup, but instead of positioning the overlay absolutely, you give it a fixed position, and a full width and height to cover the entire viewport. And because the overlay in this case is fixed, no matter how far down you scroll, the overlay will stay in position, covering the whole viewport area, which if of course what we want.

.overlay {
  position: fixed;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  z-index: 10;
  background-color: rgba(0,0,0,0.5);
}

Unlike absolutely positioned elements which are positioned relative to a container with a position:relative, fixed elements are positioned relative to the viewport:

Whereas the position and dimensions of an element with position:absolute are relative to its containing block, the position and dimensions of an element with position:fixed are always relative to the initial containing block. This is normally the viewport: the browser window or the paper’s page box. — W3C Wiki

Normally, when using fixed position, you don’t have to worry about where to put the overlay div in the markup. No matter where you put it, it’ll get a fixed position with respect to the viewport, unless you’re transforming one of the overlay’s ancestors, in which case the transformed element creates a containing block for all its positioned descendants, even those that are getting a fixed position. This fact has tripped off a lot of developers, including myself. So, if you ever find yourself fixing an element and the result is not as you expected it to be, check for whether this fixed element is a descendant of an element which is being transformed.

See the Pen Creating an Overlay with an Element with Fixed Position by Sara Soueidan (@SaraSoueidan) on CodePen

Again, with this technique we’re adding an empty element to the markup, which is against markup semantics. So how can we avoid this?

Technique #3: Using a pseudo-element

In order to avoid adding empty elements into our markup, we can use pseudo-elements to create the overlay instead.

The styles and considerations in this technique are pretty much the same as the previous ones, except that instead of styling and positioning an empty element with a class .overlay, we’ll be styling the :before or :after pseudo-element on the body.

html, body {
  min-height: 100%;
}

body {
  position: relative; /* needed if you position the pseudo-element absolutely */
}

body:after {
  content: "";
  display: block;
  position: fixed; /* could also be absolute */ 
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  z-index: 10;
  background-color: rgba(0,0,0,0.2);
}

You can choose to position the pseudo-element absolutely with respect to the body, or give it a fixed position. Either way you choose, you’d have to consider the points we mentioned in the first two techniques.

And here’s the pen for this example:

See the Pen Creating an Overlay with a Pseudo-Element by Sara Soueidan (@SaraSoueidan) on CodePen

An important thing to note here is that transitions on pseudo-elements still don’t work on Safari and Mobile Safari, so this is a huge drawback if you’re going to use a pseudo-element to create the overlay, because you won’t be providing your users with entirely smooth overlay effects. This is an important thing to keep in mind when you’re creating web apps that you want to feel like native apps on all devices and smartphones.

Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.

Technique #4: Applying a large outline to a modal

This technique does not require any extra elements to create the dimmed background overlay. Instead, you can apply a large outline to the modal window to achieve the dimming effect over the rest of the page.

Credit for this technique goes to Lea Verou, she was the first one to share it via a tweet on twitter.

So suppose you have an element in the markup representing the modal window that will appear on the overlay:

<div class="modal">I'm the Modal Window!</div>

When the modal pops up on top of other elements on the page, you can apply a big outline to it which will act as the dimming layer “behind” it. The outline is usually set to a very large value, but it only needs to be large enough to make sure it covers the whole viewport area no matter what the size of the viewport is.

.modal {
    /* some styles to position the modal at the center of the page */
    position: fixed;
    top: 50%;
    left: 50%;
    width: 300px;
    line-height: 200px;
    height: 200px;
    margin-left: -150px;
    margin-top: -100px;
    background-color: #f1c40f;
    text-align: center;
  
    /* needed styles for the overlay */
    z-index: 10; /* keep on top of other elements on the page */
    outline: 9999px solid rgba(0,0,0,0.5);
}

Of course, you don’t want to forget to set the z-index for the modal to keep it on top of other elements on the page.

See the Pen Creating an Overlay with an Outline by Sara Soueidan (@SaraSoueidan) on CodePen

One thing to keep in mind here when you use this technique is that the dark overlay behind the modal will not prevent mouse interactions with the other elements on the page. You can not prevent the pointer and mouse events from firing if you click on other elements on the page, which may be an undesirable effect in most cases. So you have to consider whether you want this effect for your app or not before you decide on whether or not to use this technique.

Also note that an outline is drawn outside of any borders and it will not follow a border-radius. If you have a border radius like we have in the examples, you’ll notice a gap. So this might not be a good choice if you use a modal window with a border radius.

Technique #5: Applying a large box shadow to a modal

The only difference between this technique and the previous one is that instead of applying a large outline to the modal, we’re going to apply a large box shadow to it.

.modal {
    /* some styles to position the modal at the center of the page */
    position: absolute;
    top: 50%;
    left: 50%;
    width: 300px;
    line-height: 200px;
    height: 200px;
    margin-left: -150px;
    margin-top: -100px;
    background-color: #f1c40f;
    text-align: center;
    border-radius: 5px;
  
    /* needed styles for the overlay */
    z-index: 10; /* keep on top of other elements on the page */
    box-shadow: 0 0 0 9999px rgba(0,0,0,0.5);
}

The result is also, of course, pretty much the same.

See the Pen Creating an Overlay with a Large Box Shadow by Sara Soueidan (@SaraSoueidan) on CodePen

Of course, the dark overlay resulting from this technique also does not prevent the interaction with the rest of the elements on the page behind the modal.

Now, despite mentioning this technique as one of the possible ways to create overlays, I strongly advise you not to use it to create overlays. Even more, don’t use too many box shadows on your web pages/apps in general.

Box shadows in combination with other styles like border-radius or when heavily used can cause a huge performance bottleneck, and can even render your app unusable on smartphones and tablets, as they tend to become very unresponsive with box-shadow-heavy applications.

Box shadows are costly to render, and it gets even worse when large box shadows are applied to fixed elements, as it can force the browser to redraw large portions of the page when scrolling. This is particularly bad in Firefox, where fixed elements and large CSS box shadows can bring it to a crawl, slowing it down to 2 frames/second for scrolling and DOM manipulation.

So, try to avoid using large box shadows in your applications or too many of them, as it will have a significantly bad impact on your app’s performance. If you find yourself in a situation where you need to use a lot of box shadows, you can at least try to remove those box shadows on smartphones and tablets by using one of the feature-detection techniques out there and providing fallback styles for these devices.

Technique #6: Using the HTML <dialog> element

The last technique we’ll talk about is a fairly new one, and a really awesome one! And it is the most semantic of all techniques if you’re creating an overlay for a dialog box or modal window.

Modals can now be easily created and styled using the HTML dialog element. The dialog element provides in-page dialog box functionality. A dialog exists in the DOM tree and can be styled using ordinary CSS.

The dialog element represents a part of an application that a user interacts with to perform a task, for example a dialog box, inspector, or window. —WHATWG HTML Spec

The HTML dialog element has four main features, three of these features are what we’re most interested in when creating overlays (the fourth one is not yet implemented at the time of writing this article):

  1. By default, a dialog is centered vertically in the viewport when opened. It is still absolutely positioned so it can be scrolled away. The viewport centering occurs regardless of the dialog’s position in the DOM tree.
  2. Dialogs can be modal. When a modal dialog is opened, it blocks the rest of the document. There is a “pending dialog” stack to handle the case of multiple modal dialogs.
  3. A new stacking layer on top of the existing CSS stacking contexts handles “always on top” behavior. Dialog and the Fullscreen spec both use the top layer. Modal dialogs reside in the top layer. So you don’t need to worry about setting a z-index manually to keep your modal on top of other elements on the page.

Pretty awesome, right? The dialog element is centered by default, but as mentioned in the first point, it uses absolute positioning, so it can be scrolled away. You can override the default absolute position by setting the position to fixed in your style sheet. If you do decide to change the position to fixed, you’ll also have to set the top and left values and center it as well.

The dialog element can be placed anywhere in the DOM.

<dialog class="modal">This is the dialog!</dialog>

It can be styled just like you would style any other block-level element.

.modal{
  /* arbitrary styling */
  background-color: white;
  border-radius: 5px;
  box-shadow: 2px 2px 2px rgba(0,0,0,0.2);
  height:200px;
  width:300px;

  /* change position to fixed if you want to prevent the dialog from scrolling away, and center it */
  position:fixed;
  top:50%;
  left:50%;
  margin-left: -150px;
  margin-top:-100px;
}

The dialog element also comes with a pseudo element called ::backdrop, which allows you to style the background behind a modal, thus creating the dimming overlay effect that we created in the previous five techniques, only this time, you can create that overlay easily by styling the pseudo-element which comes by default with the dialog element.

So, to create an overlay effect using the HTML dialog element, you just apply a background color to its associated backdrop pseudo-element and position it to cover the area of the viewport like we did in a previous techniques:

.modal::backdrop {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0,0,0,0.5);
}

Here’s how the result would look like:

dialog-demo

And that’s all you need to create an overlay using the dialog element!

To make working with it even easier, dialog comes with an API which makes showing and hiding a modal a piece of cake, using functions like show() and hide(), among others.

You can learn more about the dialog element and its API and see different live examples in this demo by Eiji Kitamura. The demo runs in a polyfill mode when you’re viewing it in a browser that does not yet support the dialog element.

Final Words

I think we’ve covered pretty much all techniques that you can use to create a page overlay for modal windows. As you probably have guessed, the last technique using the dialog element is the best one for creating modal window or dialog overlays, but at the time of writing this article, it is only supported in Chrome Canary, behind a flag, with a polyfill available. So you need to be careful about browser support if you attempt to use it today. But once support for it has been added in all major browsers, it will be the best way to create page overlays, with an awesome bunch of features available to work with it.

I hope you liked this article and find it useful. Thank you for reading!

Do you know other techniques for creating overlays in CSS? If you do, make sure you share them in the comments below!

Sara Soueidan

Sara is a Lebanese freelance front-end web developer and speaker, specializing in semantic markup, CSS, SVG, and progressively enhanced, responsive design, with a strong focus on accessibility and performance. She is the author of our CSS Reference and has co-authored the Smashing Book 5, a book that covers time-saving, practical techniques for crafting fast, maintainable and scalable responsive websites. Sara also runs in-house workshops about SVG and about building accessible UI patterns. Her client list includes Netflix, The Royal Schiphol Group @ Amsterdam Airport, TELUS Digital, and more. Learn more about some of her work or hire her.

Stay in the loop: Get your dose of frontend twice a week

Fresh news, inspo, code demos, and UI animations—zero fluff, all quality. Make your Mondays and Thursdays creative!

Feedback 104

Comments are closed.
  1. using the first technique, I have a problem when the “modal” element is in another or several “sub” div.

    my content

    The modal does not appear in front of the overlay but behind even if i increase the z-index of the modal.. any idea?

  2. HI SARA SOUEIDAN,

    This is looking good, but i have tried in IE 9.0 browser it is not working for me. Its working in Chrome, Firefox etc…
    Kindly suggest me some other way to implement this on IE browser.

  3. Great techniques. I’ve particularly liked the use of pseudo element on technique#3. It is a pity that it doesn’t work on Safari and IOS browsers.

    Great article, great work.

  4. Awesome tutorial… After wasting 3 days atlast found this useful technique. It helps me to develop custom popup plugin.. Now I’m going to implement this technique to all projects…………………… Thanks SARA SOUEIDAN

  5. Hi Sara,
    Thanks for sharing this…

    …because I’m stupid how would one go about closing the modal (the whole overlay), if for example a close button is present but the user opts to click the overlay instead..how would you close it all? Bit of jquery?

    Thanks
    Darren

  6. Hi, very nice work.
    is it posible to call the modal windows from another html page?

    for example to have a list of diferent modal windows…

  7. I used Technique #2 (fixed window) and it worked perfectly. What I am having trouble figuring out is how to close the”window” and eliminate or hide the overlay. I was trying to do it using a simple image and link but so far nothing I have done has worked. Do you have a suggestion?

  8. Hey,

    great article – THANKS! 🙂

    I used “Technique #4: Applying a large outline to a modal” and added a ::before to my modalbox, which is absolute positioned with the borderradius.

    &::before {
    content: ”;
    display: block;
    position: absolute;
    background-color: $gray-base;
    border-radius: 20px;
    height: 100%;
    width: 100%;
    bottom: 0;
    z-index: -1;
    left: 0;
    }

    My modalbox get a transparent background-color (same color as the overlay-Outline) without any border-radius.
    So there is no gap between the border-radius and the outline.

    What do you think about this?

  9. Thank you so much for the article….It really covers pretty much everything there is to creating an overlay.

  10. this is a great article I love this thank you so much for publishing this use full article.