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.
1
2
3
4
5
6
|
<
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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 JSBin so you can test the result of this technique here. Try replacing the min-height
on the html
andbody
with height
, or removing the relative
position from the body to see how the overlay gets cut off at the bottom when you scroll.
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.
1
2
3
4
5
6
7
8
9
|
.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 withposition: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.
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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 JSBin for this example:
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.
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:
1
|
<
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
.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.
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
.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.
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):
- 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.
- 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.
- 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.
1
|
<
dialog
class
=
"modal"
>This is the dialog!</
dialog
>
|
It can be styled just like you would style any other block-level element.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
.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:
1
2
3
4
5
6
7
8
|
.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:
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.