How To Hide and Reveal Things With CSS

Hiding things in Roll20 using CSS is easy when you know what you’re doing, but there are several ways to stumble for people still figuring this out. You need five elements:

  • Something to be made visible or invisible. For the rest of this description, we’ll assume it’s a div but it can be any element.
  • A conditional element, like a checkbox or select. This has a value.
  • A hidden input with the same name as the conditional element, and a class.
  • The hidden input must be placed next* to the element in the first point.
  • Finally. a CSS rule that responds to the hidden input’s value changing.

* What “next to” means depends on your understanding of the CSS hierarchy. For simple examples like this one though, it literally means exactly that: the hidden input must be next to (and before) the element it affects.

Here’s how it works:

  1. The user checks or box, and sets that value.
  2. The hidden input is immediately set to the same value.
  3. The CSS rule responds to that hidden input’s change, and the div is made visible or invisible.

Here are some examples of how to use this technique, from the simpler to the more complex. These examples only have the CSS needed to make this code work – nothing else. There’s no attempt to make the examples look pretty – far too often, when people post examples, they include a lot of extraneous code that makes it hard to figure out which parts are relevant. There’s none of that here.

In these examples, we’ll usually be checking if something needs to be hidden. If it is meant to be visible, we can leave it untouched since that’s the default status and there’s no need to check for that.

Also, these codes assume you are using CSE (as everyone should be if making a sheet now). If using Legacy code, these examples all work, but you must add sheet- to each class name in the CSS.

You have a single element you want to show or hide, based on a toggle.

For example: if you own a feat, its description is shown. If you don’t own it, the feat it’s not.

Somewhere in the sheet, you have a checkbox. This might be a configuration tab:

<input type="checkbox" name="attr_has_feat" value="1">
Code language: HTML, XML (xml)

Somewhere else in the sheet you have a hidden input and the feat description, right next to each other. These two elements don’t have to be near the checkbox, but they do need to be next to each other.

<input type="hidden" name="attr_has_feat" class="toggle" value="0" > <span class="feat-description">I have a feat</span>
Code language: HTML, XML (xml)

Notice it is this input that has the class. The checkbox doesn’t need one.

Pay attention to the hidden input value. This sets the default value of the checkbox, and whether the description is displayed or not.

Then you have the CSS that makes it work.

.toggle[value="0"] ~ .feat-description { display: none; }
Code language: CSS (css)

There’s no need to check for value=”1″ since the element will be visible by default.

Imagine your system has many feats, but you only want to show the ones owned. You can use the technique from the previous tab for all of them. But the clever part is that if you use the + selector, you can reuse the same toggle class, thus keeping your CSS short.

So, somewhere you have a complete list of feats and a checkbox for each of them. In this example, these are in a div and each has a span for a label.

These don’t have to be together, but they do need a different name for each checkbox.

<div class="config-feats"> <input type="checkbox" name="attr_has_feat1" value="1"><span>Feat 1</span> <input type="checkbox" name="attr_has_feat2" value="1"><span>Feat 2</span> <input type="checkbox" name="attr_has_feat3" value="1"><span>Feat 3</span> </div>
Code language: HTML, XML (xml)

Then somewhere else in the sheet, you have the feats where they will be displayed. Each feat needs its own hidden input and span for displaying description.

<div class="feat-display"> <input type="hidden" name="attr_has_feat1" class="toggle" value="0" > <span class="feat-description">I have feat 1</span> <input type="hidden" name="attr_has_feat2" class="toggle" value="0" > <span class="feat-description">I have feat 2</span> <input type="hidden" name="attr_has_feat3" class="toggle" value="0" > <span class="feat-description">I have feat 3</span> </div>
Code language: HTML, XML (xml)

And finally, you have the CSS. It’s surprisingly short.

.toggle[value="0"] + .feat-description { display: none; }
Code language: CSS (css)

This is very similar to the previous example but uses the Next Sibling selector (+). This applies only to the very next element, so the span and the toggle that affects it must be right next to each other. But this means you can have as many toggles as you need in the same block.

You have two things you want to swap based on a toggle. When a checkbox is checked, show one thing, and when not checked, show the other.

Let’s say you are healthy if unhurt, but if you have injuries you are bloodied. First, you need a checkbox for the user to check. (If you are using sheet workers, the hidden input can be checked automatically and you don’t need the checkbox. That’s a topic for a different post.)

<input type="checkbox" name="attr_injured" value="1">
Code language: HTML, XML (xml)

Then just like before you need a hidden input, but you need two elements – one to show and the other to hide. I am using divs here to emphasise the fact that they can any kind of element.

<input type="hidden" name="attr_injured" value="0" class="toggle"> <div class="bloodied"> I am hurt! </div> <div class="healthy"> I am not hurt! </div>
Code language: HTML, XML (xml)

Then you have the CSS. This time, you check both values:

.toggle[value="1"] ~ .healthy, .toggle[value="0"] ~ .bloodied { display: none; }
Code language: CSS (css)

Note we use the Sibling Selector (~). This can apply to any following element within the same block. So the toggle class can affect both the healthy and bloodied divs. When using the Next Sibling selector (+), it only applies to the single element directly after the toggle.

Again, the code only checks for what needs to be hidden.

You can also use the :checked state of a checkbox. I prefer using the value of hidden inputs, but if you use the checked status, you can sometimes discard the hidden input. Let’s look at the swap areas from the previous tab.

In this case, both the checkbox and the wound display are next to each other:

<input type="checkbox" name="attr_injured" class="toggle"> <div class="bloodied"> I am hurt! </div> <div class="healthy"> I am not hurt! </div>
Code language: HTML, XML (xml)

The CSS here looks for :checked and :not(:checked). There is no :unchecked state, so using :not is necessary. But it’s very useful as you’ll see in the next tab.

.toggle:checked ~ .healthy, .toggle:not(:checked) ~ .bloodied { display: none; }
Code language: CSS (css)

This only works if the checkbox and the elements to be hidden are in the same block.

If you want the checkbox in one part of the sheet, and the health displays in a different part of the sheet, you’ll need a copy of that checkbox in the same block as the display elements. It can be hidden, but then you might as well use the value approach.

If you want a set of tabs on your sheet, where players click a tab and show a section of their character sheet and hide all the others, you can use exactly the same techniques as before. The difference is instead of using a checkbox, use something which can have more than two values, like a radio group or a select.

Conventional tabs would use a radio group, with clever styling to make the radio group appear as a set of tabs. The code for that would distract from the purpose of this code, so I’ll use a simple dropdown (select).

In this example, your sheet has three variant character sheets – werewolf, mage, and vampire. First, you have a dropdown for the selection.

<select name="attr_playbook"> <option>Vampire</option> <option>Werewolf</option> <option selected>Mage</option> </select>
Code language: HTML, XML (xml)

Then, as before, you have a hidden input and the divs that represent your different sheets. make sure the default value of your hidden input matches the default selection above, including capitalisation.

<input type="checkbox" name="attr_playbook" class="toggle" class="Mage"> <div class="vampire"> I vill drink your blud! </div> <div class="werewolf"> Raawwr </div> <div class="mage"> Tremble before the might of my magic! </div>
Code language: HTML, XML (xml)

Then in the CSS, we test each value, and if it’s not equal to that sheet, we hide it.

.choice:not([value="Mage"]) ~ .mage, .choice:not([value="Werewolf"]) ~ .werewolf, .choice:not([value="Vampire"]) ~ .vampire { display: none; }
Code language: CSS (css)

Using :not here makes the code half as long as it would be otherwise.


The CSS Wizardry page on the Roll20 Wiki also covers most of this, and the examples there are a little different – there are also sheet worker-based approaches for some of these. The sheet worker tabs approach is IMO better than pure CSS (formatting the tabs is a lot easier).

Series Navigation<< Understanding SelectorsInheritance and Specificity >>

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.