Charlotte Jackson
Charlotte Jackson
24th April 2015

I wrote a post a while ago when I learnt how to use the :checked pseudo class to build a toggle feature. I was pretty excited because I didn’t have to use any JavaScript to build it. This week, I have created another toggle feature, still using CSS only, which is more exciting than the first. Why is it more exciting? Firstly, I was able to change the text on the link for each toggle state, which I haven’t done before. Then I added flexbox, which is kind of my favourite thing at the moment, to position the link under the revealed content

Here is how it works.

The HTML consists of a checkbox input, a label and some content to toggle. As we can add text and styles to the label, we can hide the input element and let users click the label to select the checkbox. Note: the label’s for attribute must match the ID on the input so that the label can be used to select the checkbox. When the checkbox is selected, the pseudo class :checked is added to it. So, by adding styles to :checked, we can style the different states of the toggle.

<div class="toggle"> 
   <input type="checkbox" value="selected" id="someID" class="toggle-input"> 
    <label for="someID" class="toggle-label">
show me <span class="toggle-more">more</span> <span class="toggle-less">less</span> stuff </label> 
    <div class="toggle-content"> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit...</p> </div> 

You may be wondering why there are two spans inside the label. One has the class “toggle-more” and the other “toggle-less”. This allows us to show and hide the text based on the :checked status. The aim is that when the toggle is closed, the label will read “Show me more stuff”, and when it is open it’ll say “Show me less stuff”.


Step 1: Hide the input element.

We will look at how to show and hide the content on the :checked state shortly.

.toggle-input {
    display: none;

Step 2: Style the label in any way desired.

I made mine look like a button. You will also see that I have added a “+” before the label. Later on, I will explain how to change this to a “-” on toggle. An image could be used instead, or even nothing at all. It is completely optional. All of the text that the user needs to see is in the HTML, where it should be, so the content generated here in the CSS is supplementary.

.toggle-input + label {
background-color: #D65D5D; border-radius: 5px; color: #fff; cursor: pointer; font-szie: 0.8em; margin-top: 0.5em; margin-bototm: 0.5em; padding: 0.5em; text-align: center; width: 12em; } .toggle-input + label:before { color: #fff; content: "+"; font-size: 1em; padding-right: 0.3em; width: 1em; }

Step 3: Show content when the input is checked, and hide it when it is not.

We can do this using the :checked pseudo class. This is where the fun starts.

.toggle-input:checked ~ .toggle-content
display: block; } .toggle-input:not(:checked) ~ .toggle-content { display: none; }

Note: Browsers that don’t recognise the :checked pseudo-class may not display it correctly. To solve this, I have used the negation pseudo class :not(), so browsers that don’t support :checked don’t follow these rules. Therefore, if :checked is not supported, the content will not be hidden.

Step 4: Change the “+” to a “-” when the checkbox is checked.

If you look back at step 2 where we added “+” to .toggle-input + label:before, you will see that the only difference here is the addition of the :checked class and the new content specified.

.toggle-input:checked + label:before {
  content: "2212";

Step 5: Swap the less and more text.

Remember adding “less” and “more” to the label inside spans? One had a class .toggle-less and the other .toggle-more. So on the unchecked state we can hide .toggle-less, like below. When the box is checked, you can see that it will be displayed inline. Then we hide the .toggle-more text.

.toggle-input + label .toggle-less, 
.toggle-input:checked + label .toggle-more  {
    display: none;
.toggle-input:checked + label .toggle-less {
    display: inline;

Step: 6: Make the button appear underneath the revealed text.

Up to now, the text is displayed underneath the button, which is fine, unless you want the revealed content to be a continuation of some content above it. In this case, we want the link to stay where it should be in the markup (above the revealed content), but we want it to appear underneath the revealed content. So, flexbox is your friend.

In the HTML, I added a wrapper div called .toggle. By making this a flex container using display: flex; I can change the order of the content. Flex-direction column-reverse will tell the label and content to stack vertically and in reverse order, so we don’t have to rearrange the HTML. Nice!

.toggle {
    display: flex;
    flex-direction: column-reverse;

As usual, I have posted an example on Codepen

See the Pen CSS toggle feature by Lottejackson (@lottejackson) on CodePen.

Browser support

:checked seems to be well supported in browsers, including IE9 upwards. I have used to check this.

This was also originally posted on my site