Accessible, Stylish Modal Windows With Modern CSS

Published Wednesday 12th of August 2009

With this tutorial I will try to show you a way to do accessible, stylish modal windows (complete with an overlay-div) without using a single image and just a couple of div-elements.

Our goal is to create a generic modal that can be re-used and that has these basic features:

  • Modals should be hidden by default, except if the user has JavaScript disabled

  • The modal should be centered in the view port, even as the user scrolls

  • The modal should have those semi-transparent, rounded corners I've been seeing a lot of lately :)

  • There should (obviously) be an overlay behind the modal, preventing the user from interacting with anything behind it

  • Clicking outside the modal (i.e. on the overlay) should hide the overlay as well as all open modals (could be optional)

  • Clicking a link pointing to a modal (a[href=#login] for example) should open the modal and display the overlay

  • Don't use any images and keep it accessible!

You can check out the example here. We'll kick off simple with the HTML.

The HTML Code

All we need for the actual modal window styling is one div-element with the class 'modal'. The div should have an ID as well so we can style, script and link to it uniquely. The ID should explain what the modal is (#login or #sign-up for example).

<div class="modal" id="login">

    <
h2>Log in</h2>

    ... 
form code ...

</
div>

The overlay will also need a div but since it's not useful without JavaScript we'll use JS to add it, keeping it unobtrusive.

The CSS

I'll use some modern CSS to center the modal as well as give it a shadow and rounded, transparent corners.

body.js-enabled div.modal {
    
background#fff;
    
background-image: -webkit-gradient(linearleft topleft bottomfrom(#ccc), to(#fff));
    
-moz-background-clippadding;
    -
webkit-background-clippadding;
    
background-clippadding;

    
positionfixed;
    
left50%;
    
top50%;
    
z-index10;

    
displaynone;
    
width400px;
    
margin: -150px 0 0 -200px;
    
padding15px;

    
border15px solid rgba(20601000.7);

    -
moz-border-radius10px;
    -
webkit-border-radius10px;
    
border-radius10px;

    -
webkit-box-shadow0 0 20px rgba(3030300.7);
    -
moz-box-shadow0 0 20px rgba(3030300.7);
    
box-shadow0 0 20px rgba(3030300.7);
}

Phew! quite a lot of code, but we'll break it up. As you can see I'm styling the entire modal under body.js-enabled. I add the 'js-enabled' class to the body-element using JavaScript so that I can style differently depending on whether the user has JavaScript enabled or not:

<body class="js-disabled">

    <
script type="text/javascript">
        
document.body.className document.body.className.replace('js-disabled''js-enabled');
    
</script>

    ...

The Centering

We want this modal to stay in the middle even if the user scrolls so we'll be using position: fixed.

As you may or may not know our all-time favorite IE6 doesn't have a clue what position: fixed means but if you haven't stopped spending time on that monster yet it's about time :)

We'll use JavaScript to center the modal properly as it's displayed later on. Right now I'll just use a random number.

/* Here we position the top-left corner of the element in the absolute middle of the view port (50% 50%), in other words, it won't be centered just yet, its top-left corner will be in the center. You could add a _position: absolute; for IE unless you, like me, serve it with the Universal IE6 CSS in which case it won't ever see this code anyway */
positionfixed;
left50%;
top50%;
z-index10;

/* Could be anything you want, but we're going to divide it by two and use for the negative left-margin so if you change it make sure to change both */
width400px;

/* This, together with position fixed is the centering magic. We simply use negative margin to pull the element half of its width to the left, and "half" of its height to the top (I'm using a random number as we can't know the height of the modal without JS) */
margin: -150px 0 0 -200px;

The Semi-Transparent, Rounded Corners and Shadow

The rounded corners and transparent border will be super-easily accomplished using zero images but some of the latest (and some old) CSS:

/* We need to use background-clip so the border isn't drawn on top of the background but on its outside  */
background#fff;
-moz-background-clippadding;
-
webkit-background-clippadding;
background-clippadding;

/* Give Safari 4 (hopefully other browsers as well soon :) users a gradient background "image" */
background-image: -webkit-gradient(linearleft topleft bottomfrom(#ccc), to(#fff));

/* The only news here is the use of rgba(). It's much like rgb() except it takes a forth argument for the opacity. Brilliance! */
border15px solid rgba(3030300.7);

/* Border radius should be self-explanatory but if not it gives the element's border a radius */
-moz-border-radius10px;
-
webkit-border-radius10px;
border-radius10px;

/* Finally we give it some shadow, I recommend you Google CSS box shadow for more info on this one */
-webkit-box-shadow0 0 20px rgba(3030300.7);
-
moz-box-shadow0 0 20px rgba(3030300.7);
box-shadow0 0 20px rgba(3030300.7);

IE, of course, doesn't understand either rgba(), box-shadow or border-radius. But we're all working with progressive enhancement here, right?

Finish It Off With an Overlay

Like I mentioned earlier the overlay is nothing more than a div-element. We'll use JS to append it to the body-element a little further down, but we can put this CSS in place right now.

We'll simply position the overlay fixed in the top-left corner as well as give it 100% width and height. This way it will always cover the entire body-element.

The background and opacity can of course be tweaked to your liking. A nice touch is to use some sort of background-pattern. Preferably a semi-transparent PNG-image. I've seen some nice effects done this way.

#modal-overlay {
    
background#000;
    
opacity.2;
    
displaynone;

    
positionfixed;
    
left0;
    
top0;
    
z-index5;

    
width100%;
    
height100%;
}

Finally, the JavaScript

We'll use JS to add the overlay-div as well as "hijax" all the links pointing to modals. I'll wrap all our code in a MyModal-object so as not to create any unnecessary global variables.

var MyModal = {
    
/**
     * This function will be run 'onload'
     **/
    
init: function () {
        
this.addOverlay();
        
this.hijaxModalLinks();
    }, 

    
/**
     * Adds the overlay-div and attaches a click-event that hides
     * all the modals as well as the overlay itself
     **/
    
addOverlay: function () {
        var 
overlay document.createElement('div');

        
overlay.id 'modal-overlay';

        
document.body.appendChild(overlay);

        
// You may want to remove this bit if you don't want the user to be able to remove the modal at all
        
overlay.onclick = function () {
            
MyModal.hideAllModals();

            
overlay.style.display 'none';
        };
    },

    
/**
     * Looks for modals with IDs, then looks for links
     * pointing to those IDs and hijaxes those links
     **/
    
hijaxModalLinks: function () {
        var 
divs        document.getElementsByTagName('div');
        var 
numDivs        divs.length;
        var 
modalIDs    = [];
        var 
i;

        for (
0numDivsi++) {
            if (
divs[i].id && divs[i].className.indexOf('modal') != -1) {
                
modalIDs.push(divs[i].id);
            }
        }

        var as            = 
document.getElementsByTagName('a');
        var 
numAs        = as.length;
        var 
modalIDsStr    modalIDs.join(',');

        for (
0numAsi++) {
            if (
modalIDsStr.indexOf(as[i].getAttribute('href'false).substr(1)) != -1) {
                as[
i].onclick = function () {
                    var 
modal document.getElementById(this.getAttribute('href'false).substr(1));

                    
modal.style.display        'block';
                    
modal.style.marginTop    '-' + (modal.offsetHeight 2) + 'px';

                    
document.getElementById('modal-overlay').style.display 'block';

                    return 
false;
                };
            }
        }
    }, 

    
/**
     * Hides every div-element with a class of 'modal'
     **/
    
hideAllModals: function () {
        var 
divs    document.getElementsByTagName('div');
        var 
numDivs    divs.length;

        for (var 
0numDivsi++) {
            if (
divs[i].className.indexOf('modal') != -1) {
                
divs[i].style.display 'none';
            }
        }
    }
};

The JS was more of a bonus and I realize in many cases it's a bit too limited. It doesn't support bringing up ajax:ed content or showing modals without actually clicking a link. If you're familiar with JS you shouldn't have any problem extending my example (or writing your own) though.

If there's interest I could perhaps expand on the JS a bit and add support for more features. Perhaps a jQuery plug-in would make sense?

Example

You can check out an example here. All the code is in the .htm-file.

Enjoy and let me know what you thinks!

Tags
Comments
7 comments

Bookmark this Article

  • del.icio.us
  • Digg
  • Furl
  • Google
  • Technorati
  • Ma.gnolia
  • Blinklist
  • Blogmarks
  • Rojo
  • Stumbleupon
blog comments powered by Disqus

Random jQuery Plug-ins

  • Live Validation

    Use this plug-in to add live validation to any form on your page. The plug-in indicates whether a form control is valid or not by switching between an...

    Details

  • Favicons

    This little plug-in will insert favicons to all external links found on your site. The plug-in scans the URL the link is pointing to for a favicon and...

    Details

  • View More

    This plug-in allows an element's contents to be hidden untill the user clicks a certain element. It works exactly like the HTML5 details and summary e...

    Details

More Plug-ins

Recent Comments

Powered by Disqus
Page cached. Loaded in: 0.0582 second(s).
Last DB change: 2012-04-02 11:06:05
Last file change: 2012-04-25 20:30:39
Cache created: 2012-05-18 02:16:41