Christian Heilmann

Quick tip: conditional form fields with CSS

Thursday, January 8th, 2015 at 1:06 pm

As part of a tool I just wrote, I had the issue that one form field was dependent on another and I didn’t want to go the full way and create the field on demand with JavaScript but keep it to CSS. You can see the result here:

showing and hiding extra form fields when a checkbox is activated

A simple way to achieve this visually is to use the :checked pseudo and + selectors as you can see in this Fiddle:

The HTML is the following:

<form>
    <input type="checkbox" id="moo">
    <label for="moo">Condition</label>
    <label>
        value for condition: 
        <input type="text" value="20">
     </label>
</form>

And the important CSS are these few lines:

label + label {
    padding-left: 1em;
    opacity: 0.1;
    transition: 0.5s;
}
:checked + label + label {
    opacity: 1;
}

Questionable accessibility

Once you publish something like this, you will get a lot of tweets that this is not accessible. The same happened here, and it doesn’t matter that I wrote about the shortcomings in the first version of this post. The debate is interesting and relevant. So let’s break it down:

  1. The form field that is dependent on the activation of the checkbox is available at all times. It is not taken out of the tabindex or removed or deactivated – all CSS can do is change it visually. This can be an issue as users can access the field, change its value and more or less wasted their time as it’d never get applied unless they also activate the checkbox.
  2. The proper way of doing this would be to make the field unavailable until the checkbox is activated. This needs a JavaScript solution. You need to either change the tabindex dynamically or remove it from the DOM.
  3. It might be enough to change the visibility to hidden or the display to none (it is – see below). I feel not safe doing that though, as I rely on implementation details of assisstive technology that is not standardised. Should a screen reader not access an element that is visbility: hidden?
  4. Is a JavaScript solution really more accessible or could it be more fragile? If something happens and your JavaScript doesn’t run (a resource wasn’t loaded, an earlier script failed) end users will never get the extra field.
  5. Is this the better solution? Or does it mean we rely on a very complex solution because we are used to having to apply it? A lot of feedback I got to this was “Real dynamic forms are only possible with JavaScript”, which sounds cargo cultish to me – and I’ve been a JS advocate for a long time.

It is disappointing that a simple problem like this still needs a lot of DOM manipulation and/or ARIA to really be accessible. Maybe we think too complex – a different way of solving this issue would be to cut this problem into several steps and reload the page in between. Overkill for one field, but for very complex JS-driven forms this might be a much saner solution. And it could result in smaller forms as we don’t need a framework or lots of libraries.

In this – simple – case I wonder what harm there is in the extra form field to be accessible to non-visual users. The label could say what it is. Say the checkbox has a label of “convert to JPG”. A label of “JPG quality” on the text field would make things understandable enough. Your backend could also be intelligent enough to realise that when a JPG quality was set that the user wanted a JPG to be generated. We can be intelligent in our validation that way.

Update: Visibility fixes the issue

Turns out that adding visibility to the earlier example has the same visual presentation and fixes the tabbing issue:

The HTML is still the following:

<form>
    <input type="checkbox" id="moo">
    <label for="moo">Condition</label>
    <label>
        value for condition: 
        <input type="text" value="20">
     </label>
</form>

And the updated CSS:

label + label {
    padding-left: 1em;
    opacity: 0.1;
    visibility: hidden;
    transition: 0.5s;
}
:checked + label ~ label {
    opacity: 1;
    visibility: visible;
}

UPDATE: Safari fix.

As Safari doesn’t support multiple + selectors, replacing the second one with a ~ works. Get with it, Safari!

Share on Mastodon (needs instance)

Share on Twitter

My other work: