Slide in” side panels with pure CSS and scaling

One rather neat little user interface “pattern” that comes up a lot, especially in mobile apps, is a side panel which slides in to get at extra stuff. There are approximately 1.5 million people explaining how to do this, most of which do two things: use jQuery, and animate the position of the side panel. An interesting alternative is to do neither of those things.

First, pure CSS. I’m by no means the first person to suggest this, but it’s a neat trick: the CSS :target pseudo-selector lets you style the thing that the URL hash fragment is referencing. So if you’ve got your panel as id="panel", then #panel:target { whatever } in your CSS sets the styles for it when the URL is thispage.html#panel, which means that you can summon your side panel with pure CSS, excellent. (Then dismissing it is clicking a link to some fragment other than #panel, so <a href="#"> is fine.)

The second part is a bit different, though. The way people normally have the panel be hidden and then showing is to position it off screen. Simple way to do this (remembering that the “default state” of our panel is “offscreen”, and #panel:target is “on screen”):

#panel { 
    position: absolute;
    top: 0;
    bottom: 0;
    right: -200px;
    width: 200px;
    transition: right 0.3s ease-in-out;
}
#panel:target {
    right: 0;
}

Of course, we are good people and we know that [you should only animate transform properties and opacity if you want smooth animations][], so the obvious response is to change that to

#panel { 
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;
    width: 200px;
    transform: translateX(200px);
    transition: transform 0.3s ease-in-out;
}
#panel:target {
    transform: translateX(0);
}

However, then we have a thing: you’ll get a horizontal scrollbar now because there’s stuff off the right-hand-side of the screen. You can of course avoid that by setting overflow: hidden on the container, but that’s potentially annoying, and hiding stuff with overflow always ends up screwing me up at some point. One alternative approach is to not move the panel around at all, but instead to scale it down to almost nothing (animating a scale is fast, because it’s a transform property) and then scale it up. Like this:

#panel { 
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;
    width: 200px;
    transform: scaleX(0.00001);
    transform-origin: 100% 50%;
    transition: transform 0.3s ease-in-out;
}
#panel:target {
    transform: scaleX(1);
}

See the jsbin for a simple demo.

Note that people will expect to be able to swipe the panel away on a mobile screen. You need JS for that, so this is no panacea. Similarly, scaling may have an odd appearance depending on what’s in your side panel, and so this idea might not actually work for what you’re doing. Have an experiment with the approach and tell me about it.