Toggle CSS state, no JavaScript
Using :checked and the sibling selector to toggle CSS states with no JavaScript at all.
This is a particularly useful little trick which makes use of CSS’s sibling selector (~) as well as the pseudo-class :checked on an HTML checkbox.
The purpose behind this is to create buttons that can be used to toggle CSS "states" without the need for additional javascript.
The following demo & code demonstrates this principle in action. Click the button to see it working.
2026 update The CodePen embed is being re-hosted here; the interactive version will return.
This works by using the sibling selector, ~ to target an element on the same level as the checkbox, and apply one particular set of rules based on the status of the :checked pseudo-class. As you can see from the code, you can also use :not(:checked) to target the inactive state.
Using the following code as the basis,
.checkbox:checked ~ .target .descendant {
/* Apply some CSS to .descendant in .target
whenever the checkbox is _active_ */
}
.checkbox:not(:checked) ~ .target .descendant {
/* Apply some CSS to .descendant in .target
whenever the checkbox is _inactive_ */
}
you can see how this allows you to control the state of any descendant of the .target element, opening up possibilities for many complex interactions. Another code demo on my site, Impossible Isometrics has been built using this as the basis for the button interactions.
It’s also required to include the label element, otherwise Firefox doesn’t render this method properly.
There are some limitations to this. It does restrict you in the structure of your HTML. The checkbox does need to live on the same level and within the same parent element as the element you wish to "control"
Internet Explorer 8 also doesn’t support the :checked pseudo-class. This is something you could add a Javascript workaround for though, using something like selectivizr
It also gets quite complex if you’re trying to perform complex animations, and you might be better off using Javascript for more complicated things.
However, it is useful for things like responsive menus on websites, or other situations where you need some complex functionality without relying on Javascript.
As a fun extra exercise (woo! fun extra work. yay) see if it’s possible to do something similar to the above but with the select element, to allow for multiple states
Heads up Two dated caveats first: Internet Explorer was retired in 2022, so the IE8 :checked limitation is moot, and selectivizr is long dead — don’t reach for it.
More importantly, the hack itself is now mostly historical. Modern CSS has :has(), so you can style based on a control’s state directly, without the sibling-structure gymnastics or a hidden checkbox at all:
/* style anything when the toggle is checked */
.panel:has(> input[type="checkbox"]:checked) .descendant {
/* active state */
}
For the common cases this hack was used for, there are also native elements now: <details>/<summary> for disclosure widgets, and the popover attribute for toggled overlays and menus. Keep the checkbox hack in your back pocket as a curiosity; reach for these first.
I help content-rich organisations understand their data and pull it back into one place. Tell me what specific problems you're having and I'll be happy to talk solutions with you.
Tell me what specific problems you’re having and I’ll be happy to talk solutions with you.