Choice & Value Pair

Published by Chris Coyier

Let's say in a form you need to allow the user to select a single unique choice. That's the territory of either a select dropdown menu or a group of radio buttons. Now let's say there is direct follow-up question to each particular choice. For example, an order form for some kind of scientific liquid. It comes in Ounces, Cups, or Pints, and once that's selected, the number of Ounces, Cups, or Pints to be ordered needs to be specified. That's an interesting design pattern that could be accomplished in a number of ways.

For deep browser compatibility, you'd probably use some JavaScript to hide/show particular form elements. But for fun, let's do it with just CSS.

Radio button and number input both within label

For each unique choice, we'll have a label wrap both the radio button, the text for the label itself, and the number input for quantity.

<label for="oz">
  <input type="radio" name="choice" value="oz" id="oz" data-label="Ozs"> 
  <input type="number" name="oz-val">

Hide number input by default

Because the radio button and number input are now adjacent siblings, we can use the adjacent sibling selector to target those input specifically and hide then by default.

input[type='radio'] + input[type='number'] { 
    display: none;

Show input when radio button selected

We can show the number input when the radio button is selected with nearly the same selector, just by appending the pseudo class selector :checked to the radio button. We'll then position it off to the right.

input[type='radio']:checked + input[type='number'] { 
    display: inline; /* unhide */
    float: right;
    margin-right: 20px; /* space for "label" */
    width: 100px;

"Label" the number inputs

To be a bit more specific about what we are asking for with those number inputs, we can apply text near that input. Note the "data-label" attribute on the radio buttons, we'll snag that and drop in pseudo element text.

input[type='radio']:after {
    position: absolute;
    right: 9px;
    top: 9px;
input[data-label]:checked:after {
	content: attr(data-label);

Highlighting entire selected choice/value

To make it very visually clear which option we have selected and that that selection goes hand-in-hand with the quantity value, we'll apply a highlighted row to cover both areas.

input[type='radio']:checked:before { 
    background: #ebdcad; 
    content: ""; 
    position: absolute;
    top: 0; right: 0; bottom: 0; left: 0;
    border-radius: 8px;
    z-index: -1;

Accessibility? Browser compatibility?

An order form is one of those important things where you would want as much accessibility and as deep of browser compatibility as possible. This was just a fun CSS exercise that might work great someday, but is probably a bit ahead of it's time.

Accessibility wise, it might be OK. Display: none on the unused inputs is fine, since we literally want them hidden until a selection is made, but I'm not sure how obvious the un-hiding of them is. If someone wants to speak on behalf of the accessibility folks in the comments, I'd love to hear it.

Browser compatibility is a bit trickier. To make it work at all, the browser needs to support the adjacent sibling selector, which we're talking only IE 8+. Although if that selector fails, all the number inputs are shown which wouldn't be a show-stopper. You'd have to move the styling to a selector in which doesn't use the "+" though. The field highlighting and pseudo-labeling require pseudo elements, so that has it's own set of compatibility as well. Last but not least, it's slightly unusual that pseudo elements are allowed on radio input elements at all. Inputs are "no content" style elements (they self-close, like an img element). Opera and WebKit happen to support it, but it's not clear whether or not they should. Firefox does not, so you won't see highlighting or labels in that browser.

If you need a 100% bulletproof solution, you could use a Wufoo form, and use Field Rules to hide/show additional fields.


View Demo   Download Files

Thanks to Bradley Staples for emailing in about the idea.