Custom Style All Your Form Elements with Pure CSS and No JavaScript

by in Tutorials on 26th Oct 2012 · Comments

As a designer, being able to style every HTML element with CSS is what I desire, and not having to use additional scripts to do it is an even bigger desire. We already have a good amount of control over text, paragraph fields and submit buttons, but not much control over other form elements.

This article will show you how to style all form elements and cover which techniques work in which browsers. You will need to download this zip file for the element images.

Custom Style All Your Form Elements with Pure CSS and NO Javascript


Custom Style All Your Form Elements with Pure CSS

Like the title says, we're going to style the following elements with pure CSS and without any JavaScript help:

  • check boxes
  • radio inputs
  • select boxes
  • file upload inputs
  • text area
  • text fields
  • submit buttons

First Things First

Your HTML tag should look like this:

<!–[if IE 7 ]> <html lang="en" class="ie7″> <![endif]–> 
<!–[if IE 8 ]> <html lang="en" class="ie8″> <![endif]–> 
<!–[if IE 9 ]> <html lang="en" class="ie9″> <![endif]–> 
<!–[if (gt IE 9)|!(IE)]><!–> <html lang="en"> <!–<![endif]–> 

This is Paul Irish's technique to target IE browsers without using hacks. This'll come in handy when we start coding. We're using HTML5, so make sure you have the HTML5 Shiv in your and reset your form elements like so:

input, select, textarea { 
    margin:0; 
    padding:0; 
    font-size:0.85em /* this is optional, I like the fonts a little smaller */; 
    outline:none; 
    font-family:inherit; box-sizing:border-box /* make sure you use the other vendor prefixes */; 
}

Check and Radio Inputs (works in IE9, FF, O, Webkit)

Let's get started with the check boxes and radio buttons. We'll need the element to precede the check box and radio inputs for this to work properly. Here's the markup:

<input type="checkbox" id="male" /><label for="male">Checkbox</label><br/> 
<input type="radio" name="option" id="female" /><label for="female">Radio 1</label>
<input type="radio" name="option" id="female2" /><label for="female2">Radio 2</label>

Notice the “id" variables on the inputs match the for variables on the labels. This allows us to toggle the radio and check inputs by clicking the label itself, which is larger and easier to click with a mouse arrow or a finger. This is the way labels and input fields were intended to operate.

The CSS
This moves the browser's default radio and check inputs out of view:

input[type="radio"], 
input[type="checkbox"] { 
    position: absolute; left: -999em; 
}

Now we'll replace them by adding a pseudo element with a background image to our label element. We're adding a pseudo element instead of some padding and a background image to the label itself because the pseudo method allows us to use image sprites. Image sprites cut down on HTTP requests making the site load faster. For the check and radio inputs, I have two images: one for the off state and one for the on state. Each image is exactly 25 pixels by 25 pixels.

Here's the CSS with comments:

input[type="radio"] + label:before, 
input[type="checkbox"] + label:before { 
    content:''; /* this is generated content*/  
    display: inline-block; /* make this fake elements inline block */
    position:relative; /* we need to move the element without effecting the doc flow */ 
    top:0.25em;  /* we're moving it slightly down for alignment purposes */ 
    left:-2px; /* we're moving it slightly to the left */ 
    width:25px; height:25px; /* the width and height of the fake elements */ 
    background-image:url(formelements.png); /* the background image sprite */ 
}

Now we have our images in place, but the background needs to be positioned. If sprites get complicated, you can always open them in your graphics program and measure the distance with a ruler and get the numbers that way. I keep my sprites simple and all the same size so they're easy to calculate in CSS:

input[type="checkbox"] + label:before { background-position: 0 -25px;} input[type="checkbox"]:checked + label:before {background-position: 0 0 ; }
input[type="radio"] + label:before { background-position: -25px -25px;} input[type="radio"]:checked + label:before { background-position: -25px 0;}

The second lines of code say: if the input has a :checked pseudo class, change the background position of the label's pseudo element that precedes it. As mentioned before, this technique only works when the label precedes the input element in the markup. You can learn more about advanced CSS selectors in this great Smashing Magazine article.

Browser Fallback

We want to support our IE7-8 users so we are going to remove the custom styling for these browsers because, while IE8 supports pseudo elements in markup, it doesn't support the pseudo :checked class, so our toggle won't work, and IE7 supports none of these techniques.

.ie8 label:before { display:none; content:none; /*this removes the fake content*/ }
.ie8 input[type="checkbox"], 
.ie8 input[type="radio"], 
.ie7 input[type="checkbox"], 
.ie7 input[type="radio"]{ position: static; left:0; /* this puts the inputs back in their place */ 
}

For IE8, I noticed some weirdness in the alignment of the input and the label and so I fixed it. It could've been my IE Tester software, so this code is optional. Use and adjust it if IE8's input alignment looks wacky to you.

.ie8 input[type="checkbox"], 
.ie8 input[type="radio"] { 
    position:relative; top:5px; 
    margin-right:0.5em; 
}

[ demo link ]

Select Fields (Works in IE8+, FF, Webkit)

The credit for this technique belongs to: Bovotasan

The problem with select fields are those drop down buttons. There's no way of styling them, so what we can do is hide them. Basically, we'll wrap the select box in a div, make the select box longer than the div and make the div hide the access width of the select box (which is where the drop down button is). Then we'll apply a background image with our own drop down button. Here's the image we'll be using and the markup:

<div class="styled"> 
<select> 
<option>Explorer</option> 
<option>Firefox</option> 
<option>Webkit</option> 
</select> 
</div>

Here's the CSS:

div.styled { 
    overflow:hidden; /* this hides the select's drop button */ 
    padding:0; 
    margin:0; 
    background: white url(formelements-select.png) no-repeat bottom right; 
    /* this is the new drop button, in image form */ 
    width:12em; border-radius:2px; 
    box-shadow: 0 1px 3px rgba(0,0,0,0.2); 
    border: solid 1px #ccc; 
}

Now we remove any styling from the select box within in the div:

div.styled select { 
    width:115% /* this percentage effectively extends the drop down button out of view */; 
    background-color:transparent /* this hides the select's background making any styling visible from the div */; 
    background-image:none; 
    -webkit-appearance: none /* this is required for Webkit browsers */; 
    border:none; 
    box-shadow:none; 
    padding:0.3em 0.5em; /* padding should be added to the select, not the div */ 
}

This technique doesn't work in IE7, so you'll have to remove the styling from the div and apply it to the select box itself. Basically you'll have to fallback to standard styling for IE7. IE7 Doesn't support background images on select either. IE7 is the new IE6.

.ie7 div.styled {border:none; }
.ie7 div.styled select { 
    width:100%; 
    background-color:white; 
    border: solid 1px #ccc; 
    padding:0.3em 0.5em; 
}

File Upload Field (Webkit only)

Adapted from but credited to: The Computer Whiz

Now the file upload field is the most difficult to style, but it is very customizable in Webkit. I originally had a method of creating an upload button and placing it on top of the default button using a pseudo element for Webkit only, but the Computer Whiz method is much better. When I came across it, I decided to use it, but with a few changes of my own. Here's the markup:

<input type="file" />

The CSS

This styles the text box:

input[type="file"] { 
    position: relative /* this needs to be in place for the pseudo element to position properly */; 
    -webkit-appearance: none /* this is the key to clearing the default styling */; 
    width: 40%; 
    padding:0; 
    background-color: #f5f5f5; 
    box-shadow: inset 0 2px 3px rgba(0,0,0,0.2); 
    border:solid 1px #ccc; 
}

This removes the default file upload button:

input[type=file]::-webkit-file-upload-button { 
    width: 0; 
    padding: 0; 
    margin: 0; 
    -webkit-appearance: none; 
    border: none; 
}

This styles the upload file button, you can even change the wording.

input[type="file"]:after { 
    content: ‘Upload File'; 
    margin:0 0 0 0.5em; 
    display: inline-block; left: 100%; position: relative; 
    background: white url(formelements-select.png) no-repeat center left; 
    padding:0.3em 0.5em; 
    border: solid 1px #ccc; 
    -webkit-appearance: none; 
    box-shadow: 0 1px 3px rgba(0,0,0,0.2); 
}

The Computer Whiz has knowledge of Webkit prefixes that I don't, so I began searching for an exhaustive list. Although this is a good page, I don't feel it's exhaustive. If you know of any other lists, please let us know. As Chrome has become the second most used browser, just behind Internet Explorer and passing Firefox, these extensions will come in handy when styling elements of any kind in Chrome.

Text Fields and Buttons (all browsers)

Most of us already know how to style these elements, but just in case you don't, here's the code. Of course the box shadows don't work in IE8 and earlier, so you'd either have to setup a conditional style or live without the special effects. I tend to go for the latter. Less code is better. Here's the HTML:

<input type="text" placeholder="Enter some text here" /><br/> 
<textarea placeholder="Enter some words here" ></textarea><br/> 
<input type="button" value="&laquo; Previous"> <input type="submit" value="Send &raquo;">

The CSS
For text fields and text boxes:

input[type="text"], textarea { 
    width:12em; 
    border-radius:2px; 
    border: solid 1px #ccc; 
    padding:0.4em; 
    background-color: #f5f5f5; 
    box-shadow: inset 0 2px 3px rgba(0,0,0,0.2); 
}

With HTML5 forms can have new text attributes. The widely used ones are: tel, email and number. These can be styled just as the input text field and can be selected using the below CSS:

input[type="tel"], 
input[type="email"], 
input[type="number"] {
   Your styles here... 
}

For submit buttons

input[type="submit"], input[type="button"] { 
    background: white url(formelements-select.png) no-repeat center left; 
    box-shadow: 0 1px 3px rgba(0,0,0,0.2); 
    border-radius:4px; 
    border: solid 1px #ccc; 
    padding:0.3em 0.5em; 
}

Note: For newer CSS3, make sure to include the vendor prefixes for Firefox and Webkit/Android users still on Gingerbread 2.3.

Bonus: Making the form fields responsive

First of all, let's make the form fit the viewport on mobile devices. Put the following meta in the head of the document:

<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>

The CSS

I keep my media queries in the same CSS file - right at the bottom. The following code says: If the view port is 600 pixels wide or less, change the body font size (which cascades) to 15 pixels. Because of mobile screen sizes, it's good to make the text a little smaller. The second query says: if the view port is 400 pixels wide or less, make the form elements' width 100%.

@media screen and (max-width: 600px) {
    body { width:80%; font-size:15px; } 
}/* end of query */
@media screen and (max-width: 400px) {
    input[type="text"], select, div.styled { width:100% } 
}/* end of query */

Conclusion

If you're having trouble with the media queries working in Firefox, depending on what add-ons/extensions you have installed to make Firefox better, these may prevent you from testing the responsive site. The solution is to disable your add-ons until you find the one that's causing the problem. Or it could be Firefox's menu bar: you might have to change the way it's displayed to resolve this issue. More about this here.

Neal Chester is a front end web developer of 10+ years who enjoys UI design, code and WordPress. He can be found in Detroit Michigan offering a variety of web related services. Neal loves the web and considers himself one of the many happy architects and craftsmen of the web.