Grow your CSS skills. Land your dream job.

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"> 
  
  Ounces
  
  <input type="number" name="oz-val">
</label>

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.

Done

View Demo   Download Files

Thanks to Bradley Staples for emailing in about the idea.

Comments

  1. Nice example and tutorial, but is this validated? I mean the inputs inside the label…:))

  2. Bradley Rosenfeld

    This technique does not appear to work in Firefox 4.0.1

    When you try to click on the text input in order to type in it, it does not allow you to do so. Instead it just clicks on the radio button again.

    • Ryan King

      The problem with FF 4 is that they implemented a system where clicking on the label will select the element that it is attributed to. Double clicking the input allows for selecting it, but that isn’t exactly useable. To fix this, you can simply remove the for attribute from the label. If you want to keep the ability to select the radio by clicking on the label’s text, you can move both inputs outside of the label and then redo the positioning.

    • bill

      Doesn’t work in IE8.

    • Here’s a version with improved HTML that should have deeper support.

      http://jsfiddle.net/chriscoyier/3YdN4/7/

      No more double nested inputs and rule filters to prevent older IE’s from screwing it up.

    • peter

      @Ryan King
      Isn’t that what the label + for-attribute is for? Click on the label => select (or focus) the input which is mentioned in the for-attribute?

    • Ryan King

      @Peter Yes, that is exactly what the for attribute is for in the label, that is why I suggested moving the number input out of the label instead of removing the for attribute if you still want the ability to select the radio by clicking on the text. I probably could have worded my response a little more clearer though :). A better explanation is that clicking on the number input (while it is in the label) triggers the click on the input, then the action bubbles up to the label, triggering the click on the label, which then triggers the focus on the radio. Apparently FF4 is the only one that allows the propagation (or at least recognizes it).

  3. I guess having more than one input in a label is invalid ;-) .

  4. Thomas Ahle

    Talking usability, I think it would be fine to make the clickable area of the check boxes wider. Why not make it as big as the colored box they are in.

    An interesting design pattern for a commonly occurring problem. Specially if you have enough space to work in.

  5. Ryan King

    The sibling selector works on IE7 also: http://msdn.microsoft.com/en-us/library/cc351024%28VS.85%29.aspx#combinators

    Unfortunately there is no way of knowing if a radio button is selected with CSS in IE<9, but that is where team JS can come in and just add a class name to the selected radio button.

    But using CSS for some basic branch logic is a pretty sweet idea.

  6. Why would you want to use radio buttons if you can only select one option anyway? Wouldn’t you be better of using a drop-down and save a lot of space? You’re also displaying the unit of measurement twice again taking up extra space.

    If the user decides to change his first input, he’ll have to do the second one over again. This is where you could add some extra usability by automatically converting to another unit of measurement or whatever the first option is.

    • Radio button: 1 look 1 click

      Dropdown: 1 click, look throught the list, another click

      It’s more user-friendly.

    • peter

      What would you use a radio buttons for then?

    • Bram Cordie

      I avoid using radio buttons unless I can really afford to waste the space. It works here because there are only 3 options and just one group of buttons. If you have multiple groups with at least 5 options your page quickly turns into a scroll fest.
      Most forms with radio buttons won’t have the selected option highlighted like it is here. Which is really a pain if you want to overview your choices after filling out the hole form.

  7. I’m not going to lie, I’m new to CSS, but every time I hit your blog I learn something new I want to implement/try — here’s something new to add to the pile.

  8. Miles Carmany

    I found a few quirks:

    If you use the buttons to increment or decrement the value and it is still blank, it jumps to either +/- 1.7 something (is that e or some other special constant?) If you type a value, then the buttons work properly, increasing by integers.

    If you have the input field active (with a bliking cursor) you cannot select a different radio button. I assume this is because the input field isn’t “free” to be hidden yet. Once you click “out” of the input, you can select another radio button fine.

    The value of the input field is remembered even if you switch to another radio button and back. Would this affect what data is sent when the form is submitted? I don’t know enough about how things like GET or POST work, but it seems like you’d have to include some extra server-side logic to ensure that only the value that matches with the selected radio button is used in the case a user changed thier mind and had data in another input field.

    I am using the Beta of Reeder for Mac and I assume that its just using a webkit view, but if that’s not the case than any CSS usability issues may not be that big of a deal.

    I really like this, even if its not production ready.

    • The weird number spinner thing is related to a Safari 5.0.x bug (http://wufoo.com/html5/types/7-number.html)

      Yep, seems a little buggy. I think the solution is going to need to pull the number input out of the label. Maybe.

      And yes, the unused number value, if filled out, will submit with the form, you’d need to check for the radio button group value and only do anything with the related number input value. Kind of the responsibility of the programmer dealing with the form.

  9. This is freaking amazing!

    Gotta love this chat box lol.

  10. I love all these neat little tricks you blog about Chris, although it does make me sad to see great ones like this not being supported by many browsers lol.

  11. To be able to validate your code, can’t you take the second input out of the label and target it with the ~ sibling selector ?

  12. I’d also use JS for web accessibility (screen readers) beside the “deep browser compatibility” reason for doing it in JS . I’d drop the display:none; in CSS and just manipulate the DOM to hide the background and drop-down menu pair of unchecked radio inputs. That way, people and browsers that can interpret JS still get the slick interaction, without the interaction being a barrier to universal access; in other words, progressive enhancement.

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".