Front-end logic without Javascript
It is surprising how few front-end developers are aware of this little trick (but maybe I meet the wrong ones).
There is an easy way to manage the state of a user interface using only HTML and CSS.
You can easily make expanders, tabs, hamburger menus or even apply multiple complex states onto an HTML element, and everything without a single line of JavaScript.
The basics
To get started you use the CSS pseudo selector input:checked
in combination with a sibling selector +
or ~
. This can be used to have a different element reflect the state of the input.
For instance if you check the checkbox the text in the div will turn red:
<input type="checkbox">
<div>red</div>
input:checked + div {
color: red;
}
The input:checked
state applies only to checkbox
and radio
types.
You can do this with other siblings as wel or their children.
Add a label
The input must always precede the targeted element. But we can get around that by hiding the input and triggering it with a label. But we cannot simply display:none
the input because that would make it stop working. Instead we add a class called visuallyhidden
(by convention).
<input type="checkbox" id="you" class="visuallyhidden">
<div>red</div>
<label for="you">green</label>
input:checked + div {
color: red;
}
.visuallyhidden {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
Simple examples
At this point you know enough to apply this to numerous UX patterns. Here are some examples.
An expander
An element that reveals more content when clicked.
Tabbed panels
To created a tabbed interface we use a radio
instead of a checkbox
. If you check the source you might notice that the sections all have the className visuallyhidden
. The reason is that, contrary to display:none
, hiding it this way still allows for search engines to read it's content.
A hamburger menu
This is really just an expander. But just for examples sake: here is a nested one. And a bit of UX advice on hamburgers: try not to use them especially if you only have three menu items.
Here we have is also a small issue that is hard to resolve without resorting to Javascript. The submenus in this example use input[type=radio]
and unlike type=checkbox
radios cannot be disabled when clicking the selected one. Resolving this issue requires adding an extra unrelated input which will be checked by Javascript when a checked radio is clicked.
A carousel
Note that this example is merely to illustrate the technique. There are better ways of showing content than using a carousel.
It's easier with variables
These sibling>child selectors can get a bit tedious or complex. And what if you start moving stuff around, or try to generalise it into a component.
Luckily CSS also has variables. We can also use those and make things even more interesting.
:root {
--color: green;
}
input:checked ~ main {
--color: red;
}
.text {
color: var(--color);
}
This might not look much different from what we started with but variables allow you to have only one :checked
declaration that applies to multiple var(...)
implementations and (even better) multiple different :checked
declarations can be used in a single rule: transform: translate(var(--x), var(--y));
.
If you use variables you can declare them as high as possible and never worry about moving your variable affected components around. So put your hidden inputs at body>input
and overwrite them at body>input~main
for instance.
Here's a little box. It is a relatively simple example how three individual states can amount to sixteen possibilities. Click on the side to rotate it. Click on the top to open it. And click the inside to look inside.
The topleft checkbox shows all the labels and checkboxes.