Intermediate Articles

Exposing Additional Form Fields via Checked Radio Buttons

There is a :checked pseudo class in CSS. I often think of it in connection with the "checkbox hack", in which you use it on a hidden checkbox with the ~ general sibling combinator to simulate toggling behavior without any JavaScript. It's a hack because now you have these stray form elements on the page that really aren't for a form. Not a huge deal, as I'm sure you can work around it accessibility wise, but there is a way to use this same concept in a totally non-hacky way, on an actual form!

I used this technique on the CodePen job posting form to only reveal additional form fields as needed.


CSS Gradients

This article was originally published on March 2, 2010. It was updated April 1, 2011, July 20, 2011, and again March 3, 2014, each time to clarify and correct browser prefixes and best practices.

Just as you can declare the background of an element to be a solid color in CSS, you can also declare that background to be a gradient. Using gradients declared in CSS, rather using an actual image file, is better for control and performance.

Gradients are typically one color that fades into another, but in CSS you can control every aspect of how that happens, from the direction to the colors (as many as you want) to where those color changes happen. Let's go through it all.


How To Deal With Vendor Prefixes

If you write CSS you almost surely need to be using vendor prefixes on some parts of the code you write in order to ensure the best browser support. Whether that is prefixed properties like -prefix-transform, prefixed at-rules like @-prefix-keyframes, or prefixes values like -prefix-linear-gradient, it's a daily chore of CSS authors.

Not only is it part of our jobs, it's an easy thing to screw up. I recently looked around at a bunch of big websites specifically looking for little nitpicky CSS3 errors and I was able to find one on any site I looked at.

So how do we author CSS so that we always are using the correct prefixes all the time?

Hand Author - Refer to CSS3Please

Hand authoring means the CSS that your sites uses in production ("live") is the CSS file that you edit yourself in a text editor. Going this route means you'll need to be including all the right vendor prefixes right in that authored file yourself. A bit tedious and error-prone for me liking, but likely scenario for most websites in the world.

If this is the case for you, your best bet is referencing an up-to-date resource like CSS3 please!. You can edit the values right on that site and copy-and-paste right into your own stylesheets.

If you do this, it means the onus is on you to keep things up to date. Prefixes aren't as volatile they once were, but a look every few months is certainly warranted.

Hand Author - Don't Prefix - Let -prefix-free do it client side

You still hand-author the CSS, but you only use the un-prefixed properties/values/at-rules. -prefix-free is a bit of JavaScript that runs, looks through all your CSS, and appends to the page new styles with the proper prefixes needed to make those styles work, if necessary.

This is holy war territory. It makes CSS files smaller! Yeah but it runs the risk of flashes of un-CSS3'd elements! It requires JavaScript for styles, crossing the streams! Yeah but it's only 2k! It's progressive enhancement at its finest since it will stop prefixing when browsers stop needing it!

You decide.

It's also worth noting that jQuery's .css() method will do some prefixing for you.

  // Will NOT prefix
  "background": "linear-gradient(#ccc, #666)",

  // Doesn't need to, so won't
  "box-shadow": "inset 0 0 5px black",

  // WILL prefix
  "transform" : "rotate(5deg)"


I'm not a fan of applying CSS through JavaScript usually, but good to know as there are always exceptions to rules.

Hand Author - Fix up with Prefixr

If you prefer not hand authoring the prefixes but ultimately your workflow deploys hand-authored CSS, you could run your CSS through Prefixr before deploying. It essentially parses your CSS, finds things that need prefixing, and does it for you.

It's very smart in that you don't have to have CSS that is specifically void of prefixes (like -prefix-free requires). For example if you just happen to be missing one of the required three prefixes, it will see that and only add the missing one.

It also has ways to be added to workflows via the command line or popular text areas.

Preprocess - Use mixins

One of the big reasons to use CSS preprocessors at all is because of the help with vendor prefixing.

If you use Sass, Compass or Bourbon have robust sets of CSS3 mixins for you.

If you use LESS, I have a set of them or you could look at this roundup comparing some others.

Hand Author - Don't Prefix - Post Process with Autoprefixer

This is a really great way to do it. It's all laid out in this article. Essentially you just forget about prefixing and this build step will add the prefixes for you according to the lastest and greatest information from CanIUse.


One mistake I sometimes see is that people just use all the major prefixes on every CSS3 property. It appears they have a generic mixin that they just put everything through that slaps on the prefixes. If you've seen something like -o-border-radius, that's what I mean. That has never existed and never needs to go into your CSS.

Another mistake is letting CSS get stale. As I mentioned earlier, it's worth a look at CSS older than a couple of months to make sure it's up to snuff. If you see anything outdated on this site, please let me know and I'll update it right away.

Receding Background Modal Boxes

You all know Hakim El Hattab right? He creates some super crazy progressive demos over on his blog. His CodePen profile is full of amazing too.

One recent creation of his is Avgrund. It's a design pattern for dialog boxes in which the main page fades away and the modal box flies down from above (or up from below). The main page becomes smaller and blurry, making it seem further away ala depth of field in photography. The modal box sits on top, making it seem closer to you and clearly demand your attention. That's good, because the very purpose of modal boxes is to require a user to give you some input before they can do anything else.


It feels pretty magical when you see and use it. Kinda makes you want to right-click and see if it's Flash. But it's not, and like many things on the web when you start digging in, the magic is just a nice combination of simple effects.

Let's look at them in order. Note: this isn't exactly how Avgrund works, it's just me reverse engineering it.

Step 1) Separate Page Markup and Modal Markup

All the content on the entire page should be contained within a wrapper div. The modal is outside of that wrapper.


  <div id="page-wrap">
    <!-- all page content -->

  <div id="modal">
    <!-- modal box content -->


How that markup gets there is up to you. If I was using this for real, I'd probably inject it dynamically when needed through a JavaScript thingy I create just for handling dialogs.

Step 2) State Based CSS

No need to get too fancy with JavaScript. If we think "state based", all we need is a class name on the body element and we can adjust all visual design as needed with that class. This is a larger concept that is useful in big ways and warrants further discussion (like how/where/why to trigger states), but let's just keep it simple here with a bit of jQuery:

// Something happens
$("button").on("click", function() {

  // State changes


Step 3) Default State for Modal

The modal will be a fixed position box right in the middle of the screen. By default, it will be hidden (zero opacity) and unclickable (pointer-events). Let's just ignore browser support on that. If it's a big deal to you, you can hide it in any number of different other ways like positioning it off screen.

#modal {
  background: white;

  position: fixed;
  width: 50%;
  top: 50%;
  left: 50%;
  margin: -25% 0 0 -25%;

  /* Embiggen */
  transform: scale(1.5); /* prefix me */

  /* Hidden */
  opacity: 0;
  pointer-events: none;

Step 4) Active Modal State (The Magic!)

Now we have all we need to "recede" the page when the modal is open. Let's target the #page-wrap when the state is active and do the magic.

The magic is simply: transform scale the #page-wrap to make it smaller and filter the #page-wrap to make it blurry and less colorful.

.dialogIsOpen #page-wrap {

  /* Blur and de-color */
  -webkit-filter: blur(5px) grayscale(50%);

  /* Recede */
  -webkit-transform: scale(0.9);


WebKit only? Well... the filters are only in WebKit for the time being. Your call if you want to load up the vendor prefixes or not. If I was going to use this for real on a site, I'd spend a little time making sure this effect had a fallback, which shouldn't be too hard. Perhaps just an emphatic box-shadow would do.

Then: Make the dialog appear from above, enforcing the depth of field effect. Opacity makes it appear; transform scale makes it appear from above.

.dialogIsOpen #modal {
  /* Regular size and visible */
  transform: scale(1); /* prefix me */
  opacity: 1;

  /* Clickable */
  pointer-events: auto;


Step 5) Transitions

To make it feel natural and magical, toss in some transitions on both of the players involved.

#page-wrap, #modal {
  transition: all 0.4s ease; /* prefix me */


Of course Sass/Compass makes all this a bunch easier since it has @mixins for all this stuff. e.g.

@include transition(all 0.4s ease);
@include filter(blur(5px) grayscale(50%));
@include transform(scale(0.9));

Fair warning, this stuff is fairly memory/processing intensive. Sometimes little hacks like triggering 3D transforms helps in WebKit, at the risk of nasty looking text.

body {
  /* Use at your own discretion */
  -webkit-transform: translateZ(0);


A video, if you don't have access to a supported browser or whatever:

I put my reverse engineered demo from this article on CodePen, but you should really just go look at Hakim's demo, which also on CodePen.

Responsive Web Above The Fold

The following post is by Arley McBlain. Arley has written a few other great articles for CSS-Tricks in the past so I'm pleased to have him back for another!

This post's title isn't just a weak attempt at SEO stuffing, it's also a blind-folded scissor kick into a beehive convention! Few topics in web production can bring a nerd's blood to a rolling boil as quickly as "The Fold" and "Responsive Web Design", so it's high time we combine the two and bring this server to its knees under the sheer weight of trolls sharing how they really feel about me as a person.

Before the Webbys have to invent the "Best Use of Naivety" award I'd like to point out that for years I've been in the "The Fold is Dead" camp. Nothing used to please me more than comparing a website to the source of the scrolling metaphor; papyrus scrolls (bonus points when I could work a font joke in). "Sure, and when scribes wanted to avoid scrolling they probably wrote letters on fortune cookie paper, amiright?!" (clients probably love sarcasm). However, recently I've been forced to admit that maybe I should be a bit more accommodating of the Fold's proponents, rather than simply beating them over the head with my sack of used mouse scroll wheels.

Recently Google (incidentally a company whose clean "above the fold" website is merely a gateway to lots of scrolling search results) added a new feature to their Google Analytics suite that allows you to see what size of browsers users are visiting your site with. This allowed me to stop pretending that the data from the user's monitor resolution actually meant something relevant, and to begin to learn more about the way users are surfing my site.

View Browser Size analtyics in Standard Reporting > Content > In-Page Analytics, and click the "Browser Size" button. (view full size)

Google Analytics is a useful tool, and can do more than just shatter your faith in humanity's ability to upgrade their web browser. This tool can literally tell you how your site is being used. You can easily see who is scrolling and clicking those big shiny buttons you painstakingly designed, even after they've been pushed well out of sight by multiple instances of the "Ask Jeeves" toolbar. It will literally show you a percentage of who is scrolling and clicking individual links!

This new analytics tool made me start thinking about the fold afresh. I used to think of the fold as a weakness or handicap of Luddite users, but for the first time I saw that the fold has a huge bearing on the way that I (a savvy webmaster) surf the web as well. I had to admit to myself that I bounce a lot (Google Analytics probably has me pegged with the attention span of a goldfish). I'll often making the decision to leave a website within a second of arriving if I don't immediately see what I came for, or if I think the site looks like it was built by rodents. Judging books and websites by the cover doesn't usually hurt until a lack of results has me return to that same page to look harder, only to find the content in question sitting smug just below the fold.

Getting Started

The new browser size analytics feature has been around just long enough now that you can see some real data on how your current site design fares with impatient people like me! You'll see that many users aren't using their browsers full screen. While this has always been true of Mac users (most bitterly say they prefer their windows smaller after not being able to figure out what that UI "+" button does), it turns out that many PC users probably don't either (probably trying to look cool to the aforementioned Mac people).

I'm guessing the results of the reports will motivate you to do one of two things: personally confront each user on their questionable computer habits in dimly lit remote areas, or make you want to fine tune your design. The latter is where some handy vertical responsive web design comes in (for the former I recommend a sack full of used mouse scroll wheels).

Vertical Responsive Web Design

For the last couple years we've all been resizing our monitor widths like giddy accordion players to better understand RWD. The magic behind those is the now-common Media Query:

@media screen and (min-width: 768px) {  
  marquee { font-size: 43em; }

To get started with vertical RWD is a simple matter of addressing heights. Cinchy.

@media screen and (min-height: 768px) {  
  blink { color: pink; }

I wanted to make a couple practical vertically responsive demos to tantalize and delight!

Example #1: A Cuddlier, Squishier Fold

The most obvious use of vertical RWD would be to keep your all-important calls to action above the fold. To make this example extra fun I've chosen to do this on an existing horizontally responsive layout courtesy of Twitter Bootstrap. Being responsive on two axes is good fun, and is a great opportunity to make some messy code, if you're into that kind of thing.

When you vertically resize this demo site on desktop I've decided that I want to keep all four of the buttons in view as much as possible (hopefully the average site will just have one key CTA). For mobile sizes I only will require the blue CTA button to stay visible. For this demo I've decided not to worry about screens shorter than 320px. Realistically that should cover the extremely small desktop users and mobile users alike. We should be comfortable to talk about the users with smaller viewports in the tone that we usually reserve for IE6 users.

With this Twitter Bootstrap layout there are four horizontal break points - all but the last one (when the lower 3 columns get stacked) I am able to keep my buttons in view. This is all done by a few media queries which you can find in a <style> block at the bottom of the document's source code.

All in all, it was pretty cinchy to retro fit this layout to work in this way (he said with confidence despite deliberately picking a layout lacking media above the fold), especially by piggy-backing on existing horizontal break points.

Of course we also need to address the ugly elephant that basically lives in this room like an ever-unwelcome squatter: what shall we do about the old versions of Internet Explorer? There's a lovely little JavaScript library called CSS3MediaQueries.js that does the trick fairly well. I've included it in this demo in conditional IE tags. While it doesn't resize as fluidly as a modern browser, on the page load the user will see the responded version of the layout. Beauty.

Twitter Bootstrap demo:

View Demo

Example #2: WordPress Dashboard Navigation Tweak

This example is much simpler: one tiny media query with a simple bit of CSS that completely changes the ordinarily relative positioned left navigation that all WordPress admins are familiar with:

@media screen and (min-height: 500px) {  
  #adminmenuwrap { position: fixed; } 

Now, regardless of the navigation being in the collapsed or n00b mode (I jest), it will stay visible when you're scrolling through taller pages - provided your vertical resolution is taller than 500px. If your browser is shorter than that it goes back to being a relative positioned element that will scroll with the rest of the site. I've chosen 500px which is enough for the standard menu, as well as a few extra menu items from plugins or themes.

This vertical oriented RWD positioning treatment can work very well for navigation, widgets, pictures of cats, page tools, and even the lovely ads, like the ones from the charming sponsors of this website!

For this proof of concept I've made a localized HTML version of the WordPress dashboard (all of the links are broken it's a demonstration of the scrolling effect only):

WordPress Dashboard Demo (media query only):

View Demo

The 500px used in my example only works when you know the maximum height needed for the element. However, if the height of that object is unknowable or changes (an accordion navigation with multiple levels for example) you may want to consider a bit of jQuery that will compare the height of the object to the height of your window.

I have done this in the following variation of our demo by calling jQuery and using this script:

var setResponsive = function () {

  // Is the window taller than the #adminmenuwrap by 50px or more?
  if ($(window).height() > $("#adminmenuwrap").height() + 50) {

     // ...if so, make the #adminmenuwrap fixed
     $('#adminmenuwrap').css('position', 'fixed'); 
  } else {
     //...otherwise, leave it relative        
     $('#adminmenuwrap').css('position', 'relative'); 




WordPress Dashboard Demo (with jQuery):

View Demo

A Tall Order

So there you go.

A slight disclaimer: throughout this post I've been using the phrase "vertical responsive web design" - please understand that I'm just specifying the orientation of the responsivity, not coining a phrase. Whether the response is vertical or horizontal the term "responsive web design" captures it all.

I think there's a lot more we can be doing with vertical responsive web design in relation to our best practices, our content, and our users. The advent of this snappy new Google Analytics tool makes this a great time to start. In the past these wily short-browsered users may have been unpredictable and frustrating enough to drive Berners-Lee to kick a kitten, but now we can learn from their behavior, and predict the common ways they'll view important content.

Good news my friends, the fold is now undead.

Don’t Overthink It Grids

The vast majority of websites out there use a grid. They may not explicitly have a grid system in place, but if they have a "main content area" floated to the left a "sidebar" floated to the right, it's a simple grid.

If a more complex layout presents itself, people often reach for a grid framework. They assume grids are these super difficult things best left to super CSS nerds. That idea is perpetuated by the fact that a lot of the grid systems they reach for are very complicated.

Here's how I build grids. It's not hard or complicated. Even making them flexible is no big deal.


Crazy Town Selectors

I recently used this selector as an example in a blog post:

.article #comments ul > li > a.button {
  /* Crazy town */

There was a few comments to the effect that this seemed perfectly reasonable. I think if we step through it backwards the craziness comes out more clearly.


Crafting Minimal Circular 3D Buttons with CSS

The following post is written by Brandon Pierce. Brandon saw some nicely designed buttons by Wouter on Dribbble and set about building them with CSS. They came out nicely and he agreed to post about the process here.

Here's what we are going to make in this tutorial:

View Demo   Download Files

The Base HTML

We will be using a simple unordered list1 within a <nav> tag to create our buttons.

  <ul class="nav">
    <li><a href="#" class="icon-home"></a></li>

    <li><a href="#" class="icon-cog"></a></li>

    <li><a href="#" class="icon-cw"></a></li>

    <li><a href="#" class="icon-location"></a></li>



Some Resources

  • These icons in the buttons come from the Fontello icon font and web app.
  • The textured background is from Subtle Patterns.

CSS for the Icon Font

There are a few ways to go about this, but we'll go with this:

@font-face {
  font-family: 'fontello';
  src: url('../fonts/fontello.eot');
  src: url('../fonts/fontello.eot?#iefix') format('embedded-opentype'),
       url('../fonts/fontello.woff') format('woff'),
       url('../fonts/fontello.ttf') format('truetype'),
       url('../fonts/fontello.svg#fontello') format('svg');
  font-weight: normal; font-style: normal;

[class*=" icon-"] {
  font-family: 'fontello';
  font-style: normal;
  font-size: 3em;
  speak: none;

.icon-home:after     { content: "\2302"; } 
.icon-cog:after      { content: "\2699"; } 
.icon-cw:after       { content: "\27f3"; } 
.icon-location:after { content: "\e724"; }

CSS for the Buttons

The buttons will be arranged horizontally by making the list items inline-block. We'll make them butt up against each other with a little negative margin (or see these other techniques).

.nav {
  list-style: none;
  text-align: center;

.nav li {
  position: relative;
  display: inline-block;
  margin-right: -4px;

The lovely three dimensionality of the buttons comes from a subtle gradient and bevel and embossing courtesy of a pair of box-shadows, one normal and one inset. The width and height of the buttons are identical creating a square, which turns to a circle with border-radius. The line-height matches the height and text-align is set to center, so the icons will be both horizontally and vertically centered.

.nav a {
  display: block;
  background-color: #f7f7f7;
  background-image: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), to(#e7e7e7));
  background-image: -webkit-linear-gradient(top, #f7f7f7, #e7e7e7); 
  background-image: -moz-linear-gradient(top, #f7f7f7, #e7e7e7); 
  background-image: -ms-linear-gradient(top, #f7f7f7, #e7e7e7); 
  background-image: -o-linear-gradient(top, #f7f7f7, #e7e7e7); 
  color: #a7a7a7;
  margin: 36px;
  width: 144px;
  height: 144px;
  position: relative;
  text-align: center;
  line-height: 144px;
  border-radius: 50%;
  box-shadow: 0px 3px 8px #aaa, inset 0px 2px 3px #fff;

We can flatten out the buttons a bit on :hover to create a pressed look. You may want to save that for :active and do something else for :hover, but we'll leave that up to you.

.nav a:hover{
  text-decoration: none;
  color: #555;
  background: #f5f5f5;

At this point our buttons should look like this:

Not bad, but we have a ways to go.

Now on to the fun part, were going to be using pseudo elements to give us some additional elements to work with to create the rest of the effects. To create the horizontal "groove" you see between each button were going to add the pseudo selector :before to our list items. Using negative z-index we can keep it beneath everything. The double border give it the inset look. The negative margins we used on the list items earlier are what allow this groove to look continuous. We take advantage of absolute positioning to center the line.

.nav li:before {
  content: "";
  display: block;
  border-top: 1px solid #ddd;
  border-bottom: 1px solid #fff;
  width: 100%;
  height: 1px;
  position: absolute;
  top: 50%;
  z-index: -1;

For that extra three-dimensionality, we'll create a "well" around each anchor that the button sit inside. We can create this again with no additional HTML by using a pseudo element.

.nav a:before {
  content: "";
  display: block;
  background: #fff;
  border-top: 2px solid #ddd;
  position: absolute;
  top: -18px;
  left: -18px;
  bottom: -18px;
  right: -18px;
  z-index: -1;
  border-radius: 50%;
  box-shadow: inset 0px 8px 48px #ddd;

The size of the well (a bit larger than the button itself) comes from absolutely positioning itself behind the button and stretching itself out by using negative positioning values (top/bottom/left/right). The 3D look this time comes from a top border and inset box-shadow creating an internal shadow.

And we're done!

That wasn't so bad was it?

View Demo   Download Files

Browser Support

These buttons use a good bit of CSS3 and some pseudo elements which are CSS 2.1. Nothing too overly progressive though. Any version in the last few years of Safari, Chrome, Opera, or Firefox will have no trouble. IE 8 doesn't do CSS3, so it falls back to this:

Not a big deal.

IE 7 is similar only without the "groove" line as it doesn't do pseudo elements. Also not a big deal.


1 Unordered lists may not actually be ideal for navigation. We still used them here because we needed the extra element.

On :target

The :target pseudo selector in CSS matches when the hash in the URL and the id of an element are the same.

The current hash of the URL is "voters"
<section id="voters"> 
:target {
   background: yellow;

While that URL is as it is, that section element will have a yellow background, as per our CSS.

When would you use this?

One possibility is when you want style with "states." When the page has a certain hash, it's in that state. It's not quite as versatile as manipulating class names (since there can only be one and it can only be related to one element) but it's similar. Anything you could do changing a class to change state you could do when the element is in :target. For instance: change colors, change position, change images, hide/show things, whatever.

I'd use these rules-of-thumb for when :target is a good choice:

  1. When a "state" is needed
  2. When the jump-down behavior is acceptable
  3. When it's acceptable to affect the browser history

We'll touch on all these things in this article.

How do you get hashes in URLs?

The most common way is by a user clicking a link which includes a hash. Could be an internal (same-page) link or a fully qualified URL that happens to end with a hash and value. Examples:

<a href="#voters">Go To There</a>

<a href="">Go To There</a>

Jumping Behavior

Regardless if it's a same-page link or not, the browser behavior is the scroll the page until that element is at the top of the page. Or, as far as it can if it can't scroll that far. This is rather important to know, because it means exploiting this "stated" behavior is a bit tricky/limited.

For instance, I once tried a variety of techniques to replicate functional CSS tabs, but ultimately decided using the checkbox hack was a better idea because it avoids the page-jumping issues. Ian Hansson at CSS Science has some examples of tabs as well. His third example uses :target, and absolutely positioned elements hidden above the top of the page to prevent page jumping behavior. It's clever, but not really a solution, because that would mean the page would jump upwards should the tabs be down further on a page. The anchors are actually fixed position, meaning they scroll with the page and don't exhibit top-jumping behavior. Extra clever!

A perfect use: highlighting sections

Here's a problem: When a hash-link sends you flying down the page to the relevant section, it will try and make that section snug against the top of the browser window.

But what if there isn't enough room to scroll beneath that section? That section will be visible, but it won't be snug against the top, which can be weird and confusing.

It can be disorienting.

I'm not just making that up. From personal experience, page-jumping links that don't take me to somewhere where was I was linking to is exactly on the top, I get all out of sorts. I find it happens all to often on things like FAQ pages where the linked-to sections often aren't very tall.

So let's solve that!

One historical method was called the Yellow Fade Technique. It was employed by 37 signals in situations where new content is added to the page, and they were trying to draw the user's attention to it. Jonathan Snook ported that idea to CSS and combined it with :target.

Instead of yellow fade, we'll indicate which section the link we just clicked was referring to by nudging it over to the right and flashing a red border. Instead of making you think, here you go:

The structure is a bit of navigation that links to sections by ID:

  <a href="#one">1</a>
  <a href="#two">2</a>
  <a href="#three">3</a>

  <div id="one"><h2>One</h2>Pellentesque habitant morbi ...</div>
  <div id="two"><h2>Two</h2>Pellentesque habitant morbi ...</div>
  <div id="three"><h2>Three</h2>Pellentesque habitant morbi ...</div>

When the sections become in :target, the scoot over to the right a bit via translateX transform (prevents any weird text wrapping or anything we might get with padding) and a red border flashes on via keyframe animation.

:target {
  animation: highlight 1s ease;  
  transform: translateX(20px);     
@keyframes highlight {
  0% { border-left-color: red; }
  100% { border-left-color: white; }
section > div {
  border-left: 40px solid white;
  padding: 10px;
  transition: all 0.5s ease;     
  padding-right: 50px;
  margin-left: -20px;    

That's all there is too it really. I'd chalk this up under progressive enhancement, if you're worried about browser support. As in, it's just a nice touch, not vital.

View Demo

Fighting the Jump!

Let's say you like the idea of using :target for states but dislike the page jumping behavior, you can change the hash link in a URL without a page jump.

Using jQuery, you could target all hash-links, prevent their default behavior, and use pushState (or replaceState, I suppose) to change the URL (which won't move the page).

$("a[href^=#]").on("click", function(e) {
  history.pushState({}, "", this.href);

You could interchangeably use replaceState there too, which would change the URL without adding an entry to the browser history. Sometimes you might want that, sometimes you might not. At least you have a choice here, which you don't with the default behavior of clicking a hash link, which always adds.

But there is bad news

When the URL changes to a new hash, you'd think the current target would change and new CSS would take effect. It doesn't (tested current WebKit and Firefox at time of this writing). It's a bug.

Theoretically, you could measure and save the current scroll position of the page, let the link move it naturally, then set it back to where it was. But that just sounds so awful I couldn't even bring myself to make a test page for it.