Stuff you can do with the “Checkbox Hack”

Dec 21 2011

The "Checkbox Hack" is where you use a connected label and checkbox input and usually some other element you are trying to control, like this:

<label for="toggle-1">Do Something</label>
<input type="checkbox" id="toggle-1">
<div>Control me</div>

Then with CSS, you hide the checkbox entirely. Probably by kicking it off the page with absolute positioning or setting its opacity to zero. But just because the checkbox is hidden, clicking the <label> still toggles its value on and off. Then you can use the adjacent sibling combinator to style the <div> differently based on the :checked state of the input.

input[type=checkbox] {
   position: absolute;
   top: -9999px;
   left: -9999px;
   /* For mobile, it's typically better to position checkbox on top of clickable
      area and turn opacity to 0 instead. */
}

/* Default State */
div {
   background: green;
   width: 400px;
   height: 100px;
   line-height: 100px;
   color: white;
   text-align: center;
}

/* Toggled State */
input[type=checkbox]:checked ~ div {
   background: red;
}

View Demo

So you can style an element completely differently depending on the state of that checkbox, which you don't even see. Pretty neat. Let's look at a bunch of things the "Checkbox Hack" can do.

Disclaimer: Some of this stuff crosses the line of what you "should" do with CSS and introduces some bad semantics. It's still wicked fun to play with and cool that it's possible, but in general functional behavior should be controlled by JavaScript.

Custom Designed Radio Buttons and Checkboxes

Hide the default UI of a radio button or checkbox, and display a custom version right on top of it. Probably not generally good practice, as users are familiar with default form elements and how they work. But can be good for enforcing cross browser consistency or in situations where the UI is so obvious anyway it's just for fun.

File system like "tree menu"

Demo by Ryan Seddon

Tabbed Areas

The "tabs" design pattern is just toggling on and off of areas, perfect for the checkbox hack. But instead of checkboxes, in which any checkbox can be on or off independently of one another, these tabs use radio buttons in which only one per group can be on at a time (like how only one tab can be active at a time).

Functional CSS tabs revisited

Dropdown Menus

Original by paullferguson and then Forked for betterness by me

Push Toggles

From What's My MPG?

Options from Dabblet

FAQ Answer Revealing


View Demo

Subscribe to The Thread

  1. The “FAQ Answer Revealing” example is the perfect use case for the HTML5 details/summary elements.

    • Heck yeah, and with somebody’s jQuery fallback that’s a great choice.

    • Tim Molendijk

      @Mathias: My initial thought was the same. But then on second though; I don’t think so. How would that work? For browsers that support details/summary, no such thing is needed. For all other browsers, we would need a polyfill script anyway. I’m assuming you’re not suggesting that we manually include a checkbox alongside every details/summary instance, as that would be the worst of both worlds.

  2. This isn’t practical and pretty much teaches you how to duct tape your way through web development.

    Please put the disclaimer towards the top.

  3. Rafał Krupiński

    Nice trick indeed, but semantics suffer :)

    • More suffering: I just used it to make a color picker for a custom blog editor I just made for me. It works beautifully everywhere except iOS. How do I fix it?

  4. Justin Lee

    Like your reference to Soul Coughing in there! Nice!

  5. Maksim Chemerisuk

    I was especially impressed in past by the demo with modal dialogs done by css ninja (take a look here) which uses the same trick.

  6. I made an iOS style on/off toggle using this very technique about a month ago.http://forrst.com/posts/iOS_Pure_CSS_On_Off_switch_Now_with_100_less_i-rMA Worth a check out.

  7. That’s a very bad idea: to use styles like “left: -9999px”. Once you turn direction to rtl there will be large horizontal scroll. Perhaps, it would be there already if someone with system RTL settings such as hebrew, arabian or hindi visits your site—I haven’t tested this.
    It’s a much better idea to use styles like “position:absolute;clip:rect(0 0 0 0);” (seen in Lea Verou presentation). “visibility:hidden” is also ok if IE browsers aren’t concerned.

    • I used the clip method on Whats My MPG for the radio buttons example featured in this post.

    • left: -9999px is OK if parent element has overflow: hidden.
      visibility:hidden for checkbox is bad idea because then input.checked > label won`t work in IE7/8

  8. These are great fun! I’d be really interested to read more on people whose opinion is that even playing around with these hacks is bad for the industry. To me, they just seem like a really fun thinking exercise.

    • Felipe

      There’s a lot to learn from the CSS part but the HTML part is nonsense: form elements are to be used in forms (*), not in navigation lists, tabs or treelists.

      Simple questions: where’s the form element? Where’s the submit button?
      Advice: if it’s not a form, don’t use form elements.

      The other “problem” is that CSS Tricks is a successful website with many visitors: Chris Coyer deserves this success but on the other part comes “some” responsibility for beginners and not so beginners that are not sure about their skills and knowledge of good practice.
      In my opinion, this is “Stuff you must not do but still can learn from”. Chris added a disclaimer (even huge disclaimers wouldn’t stop few people to copypaste everything they see but it’s their problem) but I still read the title as “This is stuff I can do? OK I’ll do that”. And people like me will complain that “some of this stuff crosses the line” and “some bad semantics” is too vague (I didn’t see good semantics here though, again, there’s a lot to learn from the CSS part).
      If you need tabs or a treelist on your site, use JS to hide and show content and check http://hanshillen.github.com/jqtest/

      (*) I know that due to limitations in the HTML4.01 recommendation (there isn’t a way to express the fact that an input can be nested in a %block% like p or a fieldset or a fieldstet into a fieldset, etc), many things like a fieldset outside a form are valid but it still doesn’t make sense in a website.

    • I generally agree that inputs shouldn’t be used outside forms, but there’s nothing wrong with using a <button type="button"> outside a form, so there are certainly situations for everything. It’s a focusable element and, in maybe cases, a more accessible solution for hiding/showing content than current popular methods.

      Your comment was a little harsh and I think you’re taking this proof of concept stuff too seriously.

      The Internet is mostly a bunch of links, forms and text. It’s nice to see something a little different from time to time.

  9. @Felipe Chris did put a disclaimer there, and people who want to learn can do whatever you want. Nowadays forms are used so much that they could hardly be called that anymore. This is even more of a blogpost than a tutorial anyway.
    People can do what they want with what they’ve learned; if you’re working on your own, you technically only have to be as semantic as you need.

  10. Well, I dont see any “hack”…
    thats pure css :) Stuff in it I use almost every day…

  11. For me, the disclaimer is right in the title. “Hack”. Chris makes it plainly clear that this is something that will gain you an even deeper understanding of the canvas you’re painting on and, due to its non-semantics should really only be implemented in the funnest of cases (or those that relate to us as web manipulators, like Dabblet).

    Nice writeup Chris!

  12. You should add user-select: none; for label to prevent selecting text in buttons and to improve usability.

    Add:

    -webkit-user-select: none;
    	-khtml-user-select: none;
    	-moz-user-select: none;
    	-o-user-select: none;
    	user-select: none;
    • Was Just about to say the same thing (yes, I’m a different person than Arto, just a coincidentally similar name). If I remember right, though, you still end up with a situation where clicking on the label quickly will not register every click as toggling the associated checkbox.

      In light of this, I typically have to use JavaScript and a non-label trigger with two-way state change (clicking the non-label trigger updates the checkbox state, changing the checkbox state updates the trigger’s appearance). Combined with leaving the checkbox positioned off the page but not visible, this still leaves the checkbox accessible via keyboard shortcuts (which is always a concern when replacing an existing element).

      I’d love to see a version of this hack work with rapid-succession clicks on the label, though. The JavaScript for the above isn’t exactly simple.

  13. What is the ‘~’ mean in this line:
    input[type=checkbox]:checked ~ div { … }
    Thanks.

  14. Verified the above. Even with the user-select on the label and the input, clicking rapidly on the label does not register every click.

    http://dabblet.com/gist/1510457

    • This was tested in Firefox 8, but I know I’ve witnessed the same behavior in other browsers.

  15. Clatan

    Why should I obey semantics? Isn’t it better to use what is available to make the best possible solution. (optimization, duplication, readability, etc)

  16. Cant seem to get the tabs to work if they are nested. Does anyone have any ideas how to fix this problem?

    http://dl.dropbox.com/u/14080718/CSSTabs/index.html

    • Works for me on Safari 5.1.2 and Firefox 8.0.1.
      One thing that I noticed the last time I played around though was that on Safari, any selector with more than one combinator didn’t work. Firefox would accept two combinators, but nothing beyond that. So for example :checked + div p would not match the p element in Safari.

  17. I made a lion css ui kit recently and used the checked state for the source list so radio buttons track the selected item and checkboxes handle the treeview.

    Writing the html for a tree menu by hand gets laborous pretty quickly. For segmented push toggles the technique does feel kinda right though.

  18. While I agree most of these examples probably shouldn’t be used in practice, I think the “Push Toggle” examples are actually a perfect use case for this technique. Semantically, those are radio buttons, so it makes sense to have actual radio buttons in your markup. You could fall back to plain old radio buttons w/ labels in unsupported browsers.

    I’ve accomplished the same effect using extra markup for the styled toggles and javascript to manage the state of the hidden radio buttons, but this approach is much cleaner.

  19. To make it work in IE7/8 you can add a little jquery script:

    
    if ($.browser.msie && parseInt($.browser.version) < 9) {
      $('input[type="checkbox"]').change(function() {
        $(this).toggleClass('checked', this.checked).parent().hide().show();
      }).each(function() {
        $(this).toggleClass('checked', this.checked).parent().hide().show();
      });
    }
    

    and just copy style and relpace :checked with .checked:

    
    input[type="checkbox"]:checked + label { ... }
    input[type="checkbox"].checked + label { ... }
    
  20. Would be great if as part of the title area for all articles there was a little sub-header similar to the author one, or an icon or similar to classify what versions of html and css are being targeted in the article (eg. HTML5 & CSS3 or HTML4 & CSS3 etc).

    Unfortunately for some of us (maybe just me) its not immediately obvious what version is being targeted.

  21. Really great and helpful article. Hacks always for shortcut but that always work and became long time solution. I found nice article about CSS hacks.
    http://wordpressapi.com/2011/01/24/50-css-tequniqe-tips-web-developer-check/

  22. Does anyone know if it would be possible to apply some transitions to the tabs using only css. Im trying to make the tabs fade in and out. Ive tried a couple of things and I cant seem to get it to work.

  23. i like the comment style how can i have this style on my site

  24. If anyone is having trouble applying label-clicks in iOS, try adding an empty onclick=”" to a parent element (ie the <form>)…

  25. Thanks for pointing out the usecases of input@type=checkbox besides the obvious.

    I once wrote a post about how to rotate images using checkboxes to controll which image is currently active.

Speak, my friend

At this moment, you have an awesome opportunity* to be the person your mother always wanted you to be: kind, helpful, and smart. Do that, and we'll give you a big ol' gold star for the day (literally).

Posting tips:
  • You can use basic HTML
  • When posting code, please turn all
    < characters into &lt;
  • If the code is multi-line, use
    <pre><code></code></pre>
Thank you,
~ The Management ~