Now we can concentrate on the radio buttons, but how can we render them in a better way? There is a cool technique for this; it takes advantage of the fact that you can check a radio button even by clicking on its linked label. What we can do is hide the input elements and style the corresponding labels, maybe using icons that represent chips and beers.
Let's begin by removing the text from the radio button labels and changing the cursor appearance when it's hovering over them:
.preferences label{ float: right; text-indent: -100px; width: 40px !important; line-height: normal; height: 30px; overflow: hidden; cursor: pointer; }
Well done! Now we have to hide the radio buttons. We can achieve this by placing a patch with the same color as the background over the radio button. Let's do that:
input[type=radio]{ position: absolute; right: 30px; margin-top: 10px; } input[type=radio][name=chips]{ margin-top: 35px; } span + input[type=radio] + label, legend + input[type=radio] + label{ clear: right; margin-right: 80px; counter-reset: checkbox; } .preferences input[type="radio"]:required + label:after{ content: ''; position: absolute; right: 25px; min-height: 10px; margin-top: -22px; text-align: right; background: #6cbf00; padding: 10px 10px; display: block; }
If we now try to submit the form either using WebKit-based browsers or Firefox, we can appreciate that the validation balloons related to radio buttons are displayed correctly on both of them.
Let's move on and work on the radio button labels that, at the moment, are completely empty because we moved the text away using the text-indent
property. What we are going to do now is to put a tiny placeholder image within each label, and by taking advantage of the CSS3 ~
selector, create a pseudo-star rating system with a nice mouse-over effect.
Due to the fact that we have to work with different images (for beers and chips), we have to duplicate some statements. Let's start with the .beers
labels:
.preferences label.beers{ background: transparent url('../img/beer_not_selected.png') no-repeat center center; } .preferences label.beers:hover ~ label.beers, .preferences label.beers:hover, .preferences input[type=radio][name=beers]:checked ~ label.beers{ background-image: url('../img/beer.png'); counter-increment: checkbox; }
The elem1 ~ elem2
selector applies to all the elem2
labels that are siblings of the elem1
label and that follow it (the elem2
labels don't have to be adjacent, though). This way, we can target all the labels that follow a label that is in the hover state (when the mouse is over the element) with the selector .preferences label.beers:hover ~ label.beers
.
Using the CSS3 :checked
pseudo-class selector, we can identify the radio button that has been checked, and by applying the same trick that we just discussed, we can target all the labels that follow a checked radio button by using .preferences input[type=radio][name=beers]:checked ~ label.beers
. By putting together these two selectors and a classic .preferences label.beers:hover
selector, we are now able to change the placeholder image reflecting the user interaction with the radio buttons. Now let's add a final cool feature. We have used the
counter-increment
property to keep track of the number of selected labels, so we can take advantage of this counter and display it.
.preferences input[type=radio][name=beers]:required + label.beers:after{ content: counter(checkbox) " beers!"; }
Let's try the result in a browser:
Now, we have to duplicate the same statements
for the .chips
labels too:
.preferences label.chips{ background: transparent url('../img/frenchfries_not_selected.png') no-repeat center center; } .preferences label.chips:hover ~ label.chips, .preferences label.chips:hover, .preferences input[type=radio][name=chips]:checked ~ label.chips { background-image: url('../img/frenchfries.png'); counter-increment: checkbox; } .preferences input[type=radio][name=chips]:required + label.chips:after { content: counter(checkbox) " chips!"; }
All of the styling we did in this chapter has one big problem; if the browser doesn't support CSS3, it successfully hides both radio buttons and text labels but fails to add their image replacements, making everything unusable. There are a few ways to prevent this. The one introduced here is to use media queries.
Media queries, which will be covered in detail in a later project, basically consist of a statement that describes some conditions required to apply some styles. Let's consider the following example:
@media all and (max-width: 1000px){ body{ background: red; } }
In this example, the body background is turned into red only if the size of the browser window doesn't exceed 1000px
. Media queries are really useful to apply specific styles only to target devices (smartphones, tablets, and so on), but they have another interesting property; if a browser supports them, it also supports the CSS3 rules we used, so we can place all of the CSS written in this and in the previous sections within a media query statement:
@media all and (min-device-width: 1024px){ /* --- all of this and previous sections' statements --- */ }
With this trick, we solved another subtle problem. Trying the project on an iPad without this media query statement would have resulted in some problems with clicking on the radio buttons. This is because labels do not respond to clicks on iOS. By implementing this media query, we force iOS devices to fall back to regular radio buttons.