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:
<label for="toggle">Do Something</label>
<input type="checkbox" id="toggle">
<div class="control-me">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.
.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.
See the Pen
The Checkbox Hack by Chris Coyier (@chriscoyier)
on CodePen.
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.
Custom Designed Radio Buttons and Checkboxes
See the Pen
Custom checkboxes with CSS only by Geoffrey Crofte (@GeoffreyCrofte)
on CodePen.
You can hide the default UI of a radio button or checkbox, and display a custom version right on top of it.
File system like “tree menu”
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).
Demo from Functional CSS tabs revisited:
See the Pen
Functional CSS Tabs by Chris Coyier (@chriscoyier)
on CodePen.
Dropdown Menus
See the Pen
Radio Button Dropdowns by Chris Coyier (@chriscoyier)
on CodePen.
Push Toggles
A toggle can take the form of ON/OFF, which can be done with a single <input type="checkbox">
. Like emoji toggles!
See the Pen
CSS Checkbox Toggle Switch by Chris Coyier (@chriscoyier)
on CodePen.
Or it could be multiple <input type="checkbox">
elements to switch between differnet distinct values.
FAQ Answer Revealing
You’d probably just use a <details>
/<summary>
combo for this these days, but expandable sections can be done with the checkbox hack.
See the Pen
FAQ without Details/Summary (checkbox hack) by Chris Coyier (@chriscoyier)
on CodePen.
Hide the Sidebar
Like the old school Octopress theme.
See the Pen
Hide the Sidebar with Checkbox Hack by Chris Coyier (@chriscoyier)
on CodePen.
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.
@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 everydetails
/summary
instance, as that would be the worst of both worlds.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?
Like your reference to Soul Coughing in there! Nice!
Soul Coughing rocks! Very nice!
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.
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.
Actually. Scrap that. Different method, but still pretty cool.
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 hasoverflow: hidden
.visibility:hidden
for checkbox is bad idea because theninput.checked > label
won`t work in IE7/8These 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.
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.
Well, I dont see any “hack”…
thats pure css :) Stuff in it I use almost every day…
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.
Add:
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.
What is the ‘~’ mean in this line:
input[type=checkbox]:checked ~ div { … }
Thanks.
That’s the general sibling combinator. See here.
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.
Why should I obey semantics? Isn’t it better to use what is available to make the best possible solution. (optimization, duplication, readability, etc)
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 thep
element in Safari.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:
and just copy style and relpace
:checked
with.checked
: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.
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/
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.
i like the comment style how can i have this style on my site
If anyone is having trouble applying label-clicks in iOS, try adding an empty onclick=”” to a parent element (ie the <form>)…
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.
I thought inputs nested in labels was the way to go now
And off the top of my head, this might do away with the need for the other extra div….?
I have a problem to this in IE 8….
Does someone know how to make it work in this Evil browser??
I have This code:
input[type=checkbox]{
position: absolute;
left: -9999px;
overflow: hidden;
}
input[type=checkbox].checked ~ div {
background-position: 5% -19px;
background-color: #E5F6FF;
border: 1px solid #D1EFFF;
}
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!
Why not just use
display:none
for the input.checkbox element? Why do we have to push it across the page?