The “Checkbox Hack” is where you use a connected
<label>
and
<input type="checkbox">
and usually
some other element
you are trying to control, like this:
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.
.control-me {
/* Default state */
#toggle:checked ~ .control-me {
/* A toggled state! No JavaScript! */
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.
Some of this stuff crosses the line of what you “should” do with CSS and introduces some questionable semantics. It’s still very fun to play with and cool that it’s possible, but in general, functional behavior should be controlled by JavaScript.
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).
@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.
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 “clip:rect(0 0 0 0);” (seen in Lea Verou presentation). “” is also ok if IE browsers aren’t concerned.
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.
@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.
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!
You should add user-select: none; for label to prevent selecting text in buttons and to improve usability.
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.
Verified the above. Even with the user-select on the label and the input, clicking rapidly on the label does not register every click.
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.
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.
To make it work in IE7/8 you can add a little jquery script:
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.
The IE 8 ignores it while all other borwser (chrome,FF,Safari and Opera) handle it easily.
what should i do?
Is there any way to prevent the page from jumping when a user selects the label? If there is any amount of scrolling required to get to the label, clicking it will make the page jump to the top.
Any ideas as to how to get around this?
It’s also helpful to note that putting the label before the checkbox doesn’t allow it to be styled, since CSS cannot traverse up the DOM (only down) AFAIK. Something like this:
Will not work, but the exact same code with the two HTML lines reversed will work fine. This is because the label element doesn’t exist for the CSS :checked selector.
Since the ~ selector only has access to immediate siblings, nesting the checkbox inside a label also will not work, unless the target is inside the label as well.
Very useful post, I’m using it for a responsive fly-down navigation pane for mobile devices which seems to be working great!