Advanced styling for HTML forms

  • Revision slug: Advanced_styling_for_HTML_forms
  • Revision title: Advanced styling for HTML forms
  • Revision id: 326277
  • Created:
  • Creator: Jeremie
  • Is current revision? No
  • Comment

Revision Content

In this article, we will see how to use CSS with HTML forms in order to style some difficult form widgets. As we saw in the previous article, text fields and buttons are perfectly okay with CSS. Now we will dig into the dark part of the HTML Forms.

Before going further, let's remind us about two kinds of HTML form widgets:

  • The bads: those elements can hardly be styled and required some hard tricks, using sometimes advanced CSS3 knowledge.
  • The uglies: Just forget CSS to style those elements :( At best you'll be able to do a few things but it will not be reliable cross-browser and it will never be possible to take full control over their appearance.

CSS Expressiveness

The main problem with form widgets other than text fields and button is that in many case CSS is not expressive enough to properly style complex widgets.

The recent evolution of HTML and CSS extended the CSS expressiveness:

  • CSS 2.1 was very poor and gave us only three pseudo-classes:
    • {{cssxref(":active")}}
    • {{cssxref(":focus")}}
    • {{cssxref(":hover")}}
  • CSS Selector Level 3 add a few new pseudo-classes relates to HTML forms:
    • {{cssxref(":enabled")}}
    • {{cssxref(":disabled")}}
    • {{cssxref(":checked")}}
    • {{cssxref(":indeterminate")}}
  • CSS Basic UI Level 3 also add a few pseudo-classes to describe a widget state:
    • {{cssxref(":default")}}
    • {{cssxref(":valid")}}
    • {{cssxref(":invalid")}}
    • {{cssxref(":in-range")}}
    • {{cssxref(":out-of-range")}}
    • {{cssxref(":required")}}
    • {{cssxref(":optional")}}
    • {{cssxref(":read-only")}}
    • {{cssxref(":read-write")}}
  • CSS Selector Level 4 which is under heavy discussion do not plan to add much more about froms:
    • {{cssxref(":user-error")}} which is just an improvement of the {{cssxref(":invalid")}} pseudo-class.

All of this is a good start but there are 2 issues with this: First, some browsers do not necessarily implement things beyond CSS 2.1; Second, it's not good enough to style complex widgets such as date pickers for example.

There is some experiment by browser vendors to extend CSS expressiveness about form and in some cases it's good to know what's available.

Warning: even if it's interesting, it's not standard which mean it's not reliable. You use this at your own risk and you take a responsibility to the Web itself by using non-standard properties.

Gaining control over the appearance

WebKit (Chrome, Safari) and Gecko (Firefox) based browsers offer the highest degree of customization for HTML widgets. There are also available cross-platform so they need a mechanism to switch from widgets with native look & feel to widgets that are stylable by the user.

To that end, they use a proprietary property: {{cssxref("-webkit-appearance")}} or {{cssxref("-moz-appearance")}}. Those properties are not standard and should not be used. Actually, they behave differently between WebKit and Gecko. However, there is one value that is good to know: none. With this value, you are able to gain (almost full) control over the style of a given widgets.

So, if you have some difficulties to apply a style to an element, try to use those proprietary properties. We'll see some examples below but the best know use case for this property is to style search fields on WebKit browsers:

<form>
    <input type="search">
</form>
<style>
input[type=search] {
    border: 1px dotted #999;
    border-radius: 0;
    
    -webkit-appearance: none;
}
</style>

Note: It's always hard to predict the future when we talk about web technologies but extending CSS expressiveness is difficult and there is some exploratory work with other specification such as Shadow DOM that offer some perspective. The quest for the full stylable form is fare from over.

Examples

Check boxes and radio buttons

Styling a check box or a radio button by itself is kind of messy. For example, the size of check boxes and radio buttons are not made to be changed, and browsers can react very differently if you try to do it. Let's imagine the following test case:

<span><input type="checkbox"></span>
span {
    display: inline-block;
    background: red;
}

input[type=checkbox] {
    width : 100px;
    height: 100px;
}

Here is the way different browsers handle this:

Browser Rendering
Firefox 16 (Mac OSX) Rendering of a sized check box on Firefox
Chrome 22 (Mac OSX) Rendering of a sized check box on Chrome
Opera 12.01 (Mac OSX) Rendering of a sized check box on Opera
Internet Explorer 9 (Windows 7) Rendering of a sized check box on IE9
Internet Explorer 7 (Windows XP) Rendering of a sized check box on IE7

Because Opera and Internet Explorer do not have features such as {{cssxref("-webkit-appearance")}} or {{cssxref("-moz-appearance")}}, using them is not suitable. Fortunately, we are in a case where CSS is expressive enough to find solutions. Let's take a common example:

<fieldset>
  <p>
    <input type="checkbox" id="first" name="fruit-1" value="cherry">
    <label for="first">I like cherry</label>
  </p>
  <p>
    <input type="checkbox" id="second" name="fruit-2" value="banana" disabled>
    <label for="second">I can't like banana</label>
  </p>
  <p>
    <input type="checkbox" id="third" name="fruit-3" value="strawberry">
    <label for="third">I like strawberry</label>
  </p>
</fieldset>

Now, let's style this to have a custom check box.

The plan is to replace the native checkbox by an image of our own. So, at first, we need prepare an image with all the states required by a check box. Those states are: unchecked, checked, disabled unchecked, disabled checked. This image will be used as a CSS sprite:

Check box CSS Sprite

Let's start by hiding the original check boxes. We will simply move them outside the page viewport. There is two things important here

  • Do not use display:none to hide the check box because as we'll see below, we need the check box to be available to the user. With display:none, the check box is no longer accessible to the user which means that it's impossible to check or uncheck it.
  • We will use some CSS3 selectors to perform out styling. In order to support legacy browsers, we can prefix all our selector with the {{cssxref(":root")}} pseudo-class. In the current state of implementation, all browsers that support what we need also support the {{cssxref(":root")}} pseudo-class, others don't. It's, for example, a convenient way to filter legacy IE. Those browsers will see the regular check box where modern browsers will see the custom check box
:root input[type=checkbox] {
  /* original check box are push outside the viexport */
  position: absolute;
  left: -1000em;
}

Now that we get rid of the native check box, let's add our own. To that end, we will use the {{cssxref(":before")}} pseudo element of the {{HTMLElement("label")}} element that follow the original check box. So in the following selector, we use the attribute selector to target the check box; Then we use the adjacent sibling selector to target the label following the original check box; Finally, we access the {{cssxref(":before")}} pseudo-element and style it in order to have it displaying our custom unchecked check box.

:root input[type=checkbox] + label:before {
  content: "";
  display: inline-block;
  width  : 16px;
  height : 16px;
  margin : 0 .5em 0 0;
  background: url(checkbox-sprite.png) no-repeat 0 0;
}

We use the {{cssxref(":checked")}} and {{cssxref(":disabled")}} pseudo-class on the original check box to change the state of our custom check box accordingly. Because we use a CSS sprite, it's just about changing the position of the background.

:root input[type=checkbox]:checked + label:before {
  background-position: 0 -16px;
}

:root input[type=checkbox]:disabled + label:before {
  background-position: 0 -32px;
}

:root input[type=checkbox]:checked:disabled + label:before {
  background-position: 0 -48px;
}

Last thing but very important: When a user use the keyboard to navigate from one form widget to another, each widget show he focus visually. Because we hide the native check boxes, we have to set back this feature to let the user know where he is in the form.

:root input[type=checkbox]:focus + label:before {
  outline: 1px dotted black;
}

See the example in action

Dealing with the {{HTMLElement("select")}} nightmare

The select element is considered as an "ugly" widget because it's impossible to style it consistently cross platform but some things are possible. Rather than a long explanation, let's take an example:

<select>
  <option>Cherry</option>
  <option>Banana</option>
  <option>Strawberry</option>
</select>
select {
  width   : 80px;
  padding : 10px;
}

option {
  padding : 5px;
  color   : red;
}

The following table shows the way different browsers handle this in two cases. The first two columns is just the example alone. The second two columns use some tweak to gain more control on the widget appearance. To do this, we add the following CSS rule:

select, option {
  -webkit-appearance : none; /* To gain control over the appearance on WebKit */
  -moz-appearance : none; /* To gain control over the appearance on Gecko */

  /* To gain control over the appearance on Presto (Opera) and Trident (IE)
     Note that it also work on Gecko and has partial effects on WebKit */  
  background : none;
}
Browser Regular rendering Tweaked rendering
closed open closed open
Firefox 16 (Mac OSX) Select closed on Firefox on Mac OSX (No tweak) Select open on Firefox on Mac OSX (No tweak) Select closed on Firefox on Mac OSX Select open on Firefox on Mac OSX
Firefox 16 (Windows 7) Select closed on Firefox on Windows 7 (No tweak) Select open on Firefox on Windows 7 (No tweak) Select closed on Firefox on Windows 7 Select open on Firefox on Windows 7
Chrome 22 (Mac OSX) Select closed on Chrome on Mac OSX (No tweak) Select open on Chrome on Mac OSX (No tweak) Select closed on Chrome on Mac OSX Select open on Chrome on Mac OSX
Chrome 22 (Windows 7) Select closed on Chrome on Windows 7 (No tweak) Select open on Chrome on Windows 7 (No tweak) Select closed on Chrome on Windows 7 Select open on Chrome on Windows 7
Opera 12.01 (Mac OSX) Select closed on Opera on Mac OSX (No tweak) Select open on Opera on Mac OSX (No tweak) Select closed on Opera on Mac OSX Select open on Opera on Mac OSX
Internet Explorer 9 (Windows 7) Select closed on IE9 on Windows 7 Select open on IE9 on Windows 7 N/A N/A
Internet Explorer 7 (Windows XP) Select closed on IE7 on Windows XP Select open on IE7 on Windows XP N/A N/A

As you can see, even with the help of the -*-appearance properties, there is still remaining issues:

  • The {{cssxref("padding")}} property is handled inconsistently across OS and Browsers
  • Legacy Internet Explorer does not allow smooth styling
  • Firefox does not have a way to style the dropdown arrow.
  • If you want to style the option elements inside the dropdown list, the behavior of Chrome and Opera change from one system to another.

And with our example, we are just talking about 3 CSS properties; imagine the mess with all the CSS properties. So yes, in that case, CSS is not suitable to change the look and feel of this widgets consistently but it still allows you to tweak some things, but only if you are okay to live with differences from one browsers to another and from one OS to another.

We will try to figure which properties are suitable in the next article: Properties compatibility table for forms widgets

The road to nicer forms: useful libraries and polyfills

If CSS is expressive enough for check box and radio button, it is fare from the case for more advanced widgets. If a few things are possible with the select element, the file widget cannot be styled at all as well as the color picker, etc.
If you want to gain full control over form widgets, you have no choice but to relay on Javascript. In the article How to build custom form widgets we will see how to do it on our own but there are some very useful libraries out there that can help you already:

  • Uni-form is a framework that standardizes form markup and styles it with CSS. It also offer a few tweak with jQuery but it is optional.
  • Fromalize is an extension to common JavaScript framework (jQuery, Dojo, YUI, etc.) to normalize and to help you customize your forms.
  • Niceforms is a standalone JavaScript method that allows complete customization of web forms. You can use some of the build in theme or create your own.

The following are not just about forms but they have very interesting part to deal with HTML forms:

  • jQuery UI offer some very interesting advanced and customizable widgets, such as date pickers (with an effort made on accessibility)
  • Twitter Bootstrap can be really helpful if you want to normalize your forms.
  • WebShim is a huge tool to help you dealing with browser HTML5 support. The web forms part can be really helpful.

Remember that binding CSS and JS can have side effects. So if you choose to use one of those libraries, you should always have a fallback style sheets in the case the script fail (and there is many reason for that, especially in the mobile world)

Conclusion

So in the end, yes, there are still dark parts when CSS comes to HTML Forms. There is no clean universal solution but modern browsers offer new possibilities. For now the best solution is to learn more about the way the different browser support CSS on HTML forms widgets. 

In the next article of this guide we will explore the support of the most important CSS properties on HTML form widgets: Properties compatibility table for forms widgets

See also

Revision Source

<p>In this article, we will see how to use <a href="https://developer.mozilla.org/en-US/docs/CSS" title="/en-US/docs/CSS">CSS</a> with <a href="https://developer.mozilla.org/en-US/docs/HTML" title="/en-US/docs/HTML">HTML</a> forms in order to style some difficult form widgets. As we saw <a href="/en-US/docs/HTML/Forms/Styling_HTML_forms" title="/en-US/docs/HTML/Forms/Styling_HTML_forms">in the previous article</a>, text fields and buttons are perfectly okay with CSS. Now we will dig into the dark part of the HTML Forms.</p>
<p>Before going further, let's remind us about two kinds of HTML form widgets:</p>
<ul>
  <li><em>The bads</em>: those elements can hardly be styled and required some hard tricks, using sometimes advanced CSS3 knowledge.</li>
  <li><em>The uglies</em>: Just forget CSS to style those elements :( At best you'll be able to do a few things but it will not be reliable cross-browser and it will never be possible to take full control over their appearance.</li>
</ul>
<h2 id="CSS_Expressiveness">CSS Expressiveness</h2>
<p>The main problem with form widgets other than text fields and button is that in many case CSS is not expressive enough to properly style complex widgets.</p>
<p>The recent evolution of HTML and CSS extended the CSS expressiveness:</p>
<ul>
  <li><a href="http://www.w3.org/TR/CSS21/selector.html#dynamic-pseudo-classes" rel="external" title="http://www.w3.org/TR/CSS21/selector.html#dynamic-pseudo-classes">CSS 2.1</a> was very poor and gave us only three pseudo-classes:
    <ul>
      <li>{{cssxref(":active")}}</li>
      <li>{{cssxref(":focus")}}</li>
      <li>{{cssxref(":hover")}}</li>
    </ul>
  </li>
  <li><a href="http://www.w3.org/TR/css3-selectors/" rel="external" title="http://www.w3.org/TR/css3-selectors/">CSS Selector Level 3</a> add a few new pseudo-classes relates to HTML forms:
    <ul>
      <li>{{cssxref(":enabled")}}</li>
      <li>{{cssxref(":disabled")}}</li>
      <li>{{cssxref(":checked")}}</li>
      <li>{{cssxref(":indeterminate")}}</li>
    </ul>
  </li>
  <li><a href="http://dev.w3.org/csswg/css3-ui/#pseudo-classes" rel="external" title="http://dev.w3.org/csswg/css3-ui/#pseudo-classes">CSS Basic UI Level 3</a> also add a few pseudo-classes to describe a widget state:
    <ul>
      <li>{{cssxref(":default")}}</li>
      <li>{{cssxref(":valid")}}</li>
      <li>{{cssxref(":invalid")}}</li>
      <li>{{cssxref(":in-range")}}</li>
      <li>{{cssxref(":out-of-range")}}</li>
      <li>{{cssxref(":required")}}</li>
      <li>{{cssxref(":optional")}}</li>
      <li>{{cssxref(":read-only")}}</li>
      <li>{{cssxref(":read-write")}}</li>
    </ul>
  </li>
  <li><a href="http://dev.w3.org/csswg/selectors4/" rel="external" title="http://dev.w3.org/csswg/selectors4/">CSS Selector Level 4</a> which is under heavy discussion do not plan to add much more about froms:
    <ul>
      <li>{{cssxref(":user-error")}} which is just an improvement of the {{cssxref(":invalid")}} pseudo-class.</li>
    </ul>
  </li>
</ul>
<p>All of this is a good start but there are 2 issues with this: First, some browsers do not necessarily implement things beyond CSS 2.1; Second, it's not good enough to style complex widgets such as date pickers for example.</p>
<p>There is some experiment by browser vendors to extend CSS expressiveness about form and in some cases it's good to know what's available.</p>
<div class="warning">
  <p><strong>Warning:</strong> even if it's interesting, <strong>it's not standard which mean it's not reliable</strong>. You use this at your own risk and <a href="http://www.alistapart.com/articles/every-time-you-call-a-proprietary-feature-css3-a-kitten-dies/" rel="external" title="http://www.alistapart.com/articles/every-time-you-call-a-proprietary-feature-css3-a-kitten-dies/">you take a responsibility to the Web itself by using non-standard properties</a>.</p>
</div>
<ul>
  <li><a href="/en-US/docs/CSS/CSS_Reference/Mozilla_Extensions" title="/en-US/docs/CSS/CSS_Reference/Mozilla_Extensions">Mozilla CSS Extensions</a>
    <ul>
      <li>{{cssxref(":-moz-placeholder")}}</li>
      <li>{{cssxref(":-moz-submit-invalid")}}</li>
      <li>{{cssxref(":-moz-ui-invalid")}}</li>
      <li>{{cssxref(":-moz-ui-valid")}}</li>
    </ul>
  </li>
  <li><a href="/en-US/docs/CSS/CSS_Reference/Webkit_Extensions" title="/en-US/docs/CSS/CSS_Reference/Webkit_Extensions">WebKit CSS Extensions</a>
    <ul>
      <li>{{cssxref("::-webkit-input-placeholder")}}</li>
      <li><a href="http://trac.webkit.org/wiki/Styling%20Form%20Controls" rel="external" title="http://trac.webkit.org/wiki/Styling Form Controls">And many more</a></li>
    </ul>
  </li>
  <li><a href="http://msdn.microsoft.com/en-us/library/ie/hh869403%28v=vs.85%29.aspx" rel="external" title="http://msdn.microsoft.com/en-us/library/ie/hh869403%28v=vs.85%29.aspx">Microsoft CSS Extensions</a>
    <ul>
      <li><a href="http://msdn.microsoft.com/en-us/library/ie/hh772745%28v=vs.85%29.aspx" rel="external" title="http://msdn.microsoft.com/en-us/library/ie/hh772745%28v=vs.85%29.aspx">:-ms-input-placeholder</a></li>
    </ul>
  </li>
  <li><a href="http://www.opera.com/docs/specs/" rel="external" title="http://www.opera.com/docs/specs/">Opera has no extensions related to HTML forms</a></li>
</ul>
<h3 id="Gaining_control_over_the_appearance">Gaining control over the appearance</h3>
<p>WebKit (Chrome, Safari) and Gecko (Firefox) based browsers offer the highest degree of customization for HTML widgets. There are also available cross-platform so they need a mechanism to switch from widgets with native look &amp; feel to widgets that are stylable by the user.</p>
<p>To that end, they use a proprietary property: {{cssxref("-webkit-appearance")}} or {{cssxref("-moz-appearance")}}. <strong>Those properties are not standard and should not be used</strong>. Actually, they behave differently between WebKit and Gecko. However, there is one value that is good to know: <code>none</code>. With this value, you are able to gain (almost full) control over the style of a given widgets.</p>
<p>So, if you have some difficulties to apply a style to an element, try to use those proprietary properties. We'll see some examples below but the best know use case for this property is to style search fields on WebKit browsers:</p>
<pre class="brush: html">
&lt;form&gt;
    &lt;input type="search"&gt;
&lt;/form&gt;</pre>
<pre class="brush: css">
&lt;style&gt;
input[type=search] {
    border: 1px dotted #999;
    border-radius: 0;
    
    -webkit-appearance: none;
}
&lt;/style&gt;</pre>
<div class="note">
  <p><strong>Note:</strong> It's always hard to predict the future when we talk about web technologies but extending CSS expressiveness is difficult and there is some exploratory work with other specification such as <a href="http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html" rel="external" title="http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html">Shadow DOM</a> that offer some perspective. The quest for the full stylable form is fare from over.</p>
</div>
<h2 id="Examples">Examples</h2>
<h3 id="Check_boxes_and_radio_buttons">Check boxes and radio buttons</h3>
<p>Styling a check box or a radio button by itself is kind of messy. For example, the size of check boxes and radio buttons are not made to be changed, and browsers can react very differently if you try to do it. Let's imagine the following test case:</p>
<pre class="brush: html">
&lt;span&gt;&lt;input type="checkbox"&gt;&lt;/span&gt;</pre>
<pre class="brush: css">
span {
    display: inline-block;
    background: red;
}

input[type=checkbox] {
    width : 100px;
    height: 100px;
}</pre>
<p>Here is the way different browsers handle this:</p>
<table>
  <thead>
    <tr>
      <th scope="col">Browser</th>
      <th scope="col">Rendering</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Firefox 16 (Mac OSX)</td>
      <td><img alt="Rendering of a sized check box on Firefox" src="/files/4165/checkbox-firefox-macos.png" style="width: 107px; height: 106px;" /></td>
    </tr>
    <tr>
      <td>Chrome 22 (Mac OSX)</td>
      <td><img alt="Rendering of a sized check box on Chrome" src="/files/4163/checkbox-chrome-macos.png" style="width: 106px; height: 106px;" /></td>
    </tr>
    <tr>
      <td>Opera 12.01 (Mac OSX)</td>
      <td><img alt="Rendering of a sized check box on Opera" src="/files/4167/checkbox-opera-macos.png" style="width: 107px; height: 106px;" /></td>
    </tr>
    <tr>
      <td>Internet Explorer 9 (Windows 7)</td>
      <td><img alt="Rendering of a sized check box on IE9" src="/files/4169/checkbox-IE9-win7.png" style="width: 106px; height: 107px;" /></td>
    </tr>
    <tr>
      <td>Internet Explorer 7 (Windows XP)</td>
      <td><img alt="Rendering of a sized check box on IE7" src="/files/4171/checkbox-IE7-winxp.png" style="width: 100px; height: 100px;" /></td>
    </tr>
  </tbody>
</table>
<p>Because Opera and Internet Explorer do not have features such as {{cssxref("-webkit-appearance")}} or {{cssxref("-moz-appearance")}}, using them is not suitable. Fortunately, we are in a case where CSS is expressive enough to find solutions. Let's take a common example:</p>
<pre class="brush: html">
&lt;fieldset&gt;
  &lt;p&gt;
    &lt;input type="checkbox" id="first" name="fruit-1" value="cherry"&gt;
    &lt;label for="first"&gt;I like cherry&lt;/label&gt;
  &lt;/p&gt;
  &lt;p&gt;
    &lt;input type="checkbox" id="second" name="fruit-2" value="banana" disabled&gt;
    &lt;label for="second"&gt;I can't like banana&lt;/label&gt;
  &lt;/p&gt;
  &lt;p&gt;
    &lt;input type="checkbox" id="third" name="fruit-3" value="strawberry"&gt;
    &lt;label for="third"&gt;I like strawberry&lt;/label&gt;
  &lt;/p&gt;
&lt;/fieldset&gt;</pre>
<p>Now, let's style this to have a custom check box.</p>
<p>The plan is to replace the native checkbox by an image of our own. So, at first, we need prepare an image with all the states required by a check box. Those states are: unchecked, checked, disabled unchecked, disabled checked. This image will be used as a CSS sprite:</p>
<p><img alt="Check box CSS Sprite" src="/files/4173/checkbox-sprite.png" style="width: 16px; height: 64px;" /></p>
<p>Let's start by hiding the original check boxes. We will simply move them outside the page viewport. There is two things important here</p>
<ul>
  <li>Do not use <code>display:none</code> to hide the check box because as we'll see below, we need the check box to be available to the user. With <code>display:none</code>, the check box is no longer accessible to the user which means that it's impossible to check or uncheck it.</li>
  <li>We will use some CSS3 selectors to perform out styling. In order to support legacy browsers, we can prefix all our selector with the {{cssxref(":root")}} pseudo-class. In the current state of implementation, all browsers that support what we need also support the {{cssxref(":root")}} pseudo-class, others don't. It's, for example, a convenient way to filter legacy IE. Those browsers will see the regular check box where modern browsers will see the custom check box</li>
</ul>
<pre class="brush: css">
:root input[type=checkbox] {
  /* original check box are push outside the viexport */
  position: absolute;
  left: -1000em;
}</pre>
<p>Now that we get rid of the native check box, let's add our own. To that end, we will use the {{cssxref(":before")}} pseudo element of the {{HTMLElement("label")}} element that follow the original check box. So in the following selector, we use the <a href="/en-US/docs/CSS/Attribute_selectors" title="/en-US/docs/CSS/Attribute_selectors">attribute selector</a> to target the check box; Then we use the <a href="/en-US/docs/CSS/Adjacent_sibling_selectors" title="/en-US/docs/CSS/Adjacent_sibling_selectors">adjacent sibling selector</a> to target the label following the original check box; Finally, we access the {{cssxref(":before")}} pseudo-element and style it in order to have it displaying our custom unchecked check box.</p>
<pre class="brush: css">
:root input[type=checkbox] + label:before {
  content: "";
  display: inline-block;
  width  : 16px;
  height : 16px;
  margin : 0 .5em 0 0;
  background: url(checkbox-sprite.png) no-repeat 0 0;
}</pre>
<p>We use the {{cssxref(":checked")}} and {{cssxref(":disabled")}} pseudo-class on the original check box to change the state of our custom check box accordingly. Because we use a CSS sprite, it's just about changing the position of the background.</p>
<pre class="brush: css">
:root input[type=checkbox]:checked + label:before {
  background-position: 0 -16px;
}

:root input[type=checkbox]:disabled + label:before {
  background-position: 0 -32px;
}

:root input[type=checkbox]:checked:disabled + label:before {
  background-position: 0 -48px;
}</pre>
<p>Last thing but very important: When a user use the keyboard to navigate from one form widget to another, each widget show he focus visually. Because we hide the native check boxes, we have to set back this feature to let the user know where he is in the form.</p>
<pre class="brush: css">
:root input[type=checkbox]:focus + label:before {
  outline: 1px dotted black;
}</pre>
<p><a href="/files/4175/custom-checkbox.html" title="/files/4175/custom-checkbox.html">See the example in action</a></p>
<h3 id="Dealing_with_the_.7B.7BHTMLElement(.22select.22).7D.7D_nightmare">Dealing with the {{HTMLElement("select")}} nightmare</h3>
<p>The select element is considered as an "ugly" widget because it's impossible to style it consistently cross platform but some things are possible. Rather than a long explanation, let's take an example:</p>
<pre class="brush: html">
&lt;select&gt;
  &lt;option&gt;Cherry&lt;/option&gt;
  &lt;option&gt;Banana&lt;/option&gt;
  &lt;option&gt;Strawberry&lt;/option&gt;
&lt;/select&gt;</pre>
<pre class="brush: css">
select {
  width   : 80px;
  padding : 10px;
}

option {
  padding : 5px;
  color   : red;
}</pre>
<p>The following table shows the way different browsers handle this in two cases. The first two columns is just the example alone. The second two columns use some tweak to gain more control on the widget appearance. To do this, we add the following CSS rule:</p>
<pre class="brush: css">
select, option {
  -webkit-appearance : none; /* To gain control over the appearance on WebKit */
  -moz-appearance : none; /* To gain control over the appearance on Gecko */

  /* To gain control over the appearance on Presto (Opera) and Trident (IE)
     Note that it also work on Gecko and has partial effects on WebKit */  
  background : none;
}</pre>
<table>
  <thead>
    <tr>
      <th colspan="1" rowspan="2" scope="col">Browser</th>
      <th colspan="2" scope="col" style="text-align: center;">Regular rendering</th>
      <th colspan="2" scope="col" style="text-align: center;">Tweaked rendering</th>
    </tr>
    <tr>
      <th scope="col" style="text-align: center;">closed</th>
      <th scope="col" style="text-align: center;">open</th>
      <th scope="col" style="text-align: center;">closed</th>
      <th scope="col" style="text-align: center;">open</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Firefox 16 (Mac OSX)</td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select closed on Firefox on Mac OSX (No tweak)" src="/files/4201/select-firefox-macos.png" style="width: 82px; height: 52px;" /></td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select open on Firefox on Mac OSX (No tweak)" src="/files/4199/select-firefox-macos-open.png" style="width: 105px; height: 143px;" /></td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select closed on Firefox on Mac OSX" src="/files/4197/select-firefox-macos-custom.png" style="width: 82px; height: 52px;" /></td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select open on Firefox on Mac OSX" src="/files/4195/select-firefox-macos-custom-open.png" style="width: 108px; height: 141px;" /></td>
    </tr>
    <tr>
      <td>Firefox 16 (Windows 7)</td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select closed on Firefox on Windows 7 (No tweak)" src="/files/4209/select-firefox-win7.png" style="width: 82px; height: 50px;" /></td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select open on Firefox on Windows 7 (No tweak)" src="/files/4207/select-firefox-win7-open.png" style="width: 96px; height: 130px;" /></td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select closed on Firefox on Windows 7" src="/files/4205/select-firefox-win7-custom.png" style="width: 82px; height: 54px;" /></td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select open on Firefox on Windows 7" src="/files/4203/select-firefox-win7-custom-open.png" style="width: 96px; height: 134px;" /></td>
    </tr>
    <tr>
      <td>Chrome 22 (Mac OSX)</td>
      <td style="vertical-align: top; text-align: center;"><img alt="Select closed on Chrome on Mac OSX (No tweak)" src="/files/4183/select-chrome-macos.png" style="width: 84px; height: 21px; vertical-align: top;" /></td>
      <td style="vertical-align: top; text-align: center;"><img alt="Select open on Chrome on Mac OSX (No tweak)" src="/files/4181/select-chrome-macos-open.png" style="width: 121px; height: 81px; vertical-align: top;" /></td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select closed on Chrome on Mac OSX" src="/files/4179/select-chrome-macos-custom.png" style="width: 82px; height: 37px; vertical-align: top;" /></td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select open on Chrome on Mac OSX" src="/files/4177/select-chrome-macos-custom-open.png" style="width: 125px; height: 86px; vertical-align: top;" /></td>
    </tr>
    <tr>
      <td>Chrome 22 (Windows 7)</td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select closed on Chrome on Windows 7 (No tweak)" src="/files/4191/select-chrome-win7.png" style="width: 82px; height: 42px; vertical-align: top;" /></td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select open on Chrome on Windows 7 (No tweak)" src="/files/4189/select-chrome-win7-open.png" style="width: 84px; height: 93px;" /></td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select closed on Chrome on Windows 7" src="/files/4187/select-chrome-win7-custom.png" style="width: 82px; height: 42px;" /></td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select open on Chrome on Windows 7" src="/files/4185/select-chrome-win7-custom-open.png" style="width: 95px; height: 93px;" /></td>
    </tr>
    <tr>
      <td>Opera 12.01 (Mac OSX)</td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select closed on Opera on Mac OSX (No tweak)" src="/files/4225/select-opera-macos.png" style="width: 81px; height: 42px;" /></td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select open on Opera on Mac OSX (No tweak)" src="/files/4223/select-opera-macos-open.png" style="width: 135px; height: 94px;" /></td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select closed on Opera on Mac OSX" src="/files/4221/select-opera-macos-custom.png" style="width: 81px; height: 39px;" /></td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select open on Opera on Mac OSX" src="/files/4219/select-opera-macos-custom-open.png" style="width: 130px; height: 90px;" /></td>
    </tr>
    <tr>
      <td>Internet Explorer 9 (Windows 7)</td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select closed on IE9 on Windows 7" src="/files/4217/select-IE9-win7.png" style="width: 82px; height: 41px;" /></td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select open on IE9 on Windows 7" src="/files/4215/select-IE9-win7-open.png" style="width: 100px; height: 78px;" /></td>
      <td style="text-align: center; vertical-align: middle;">N/A</td>
      <td style="text-align: center; vertical-align: middle;">N/A</td>
    </tr>
    <tr>
      <td>Internet Explorer 7 (Windows XP)</td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select closed on IE7 on Windows XP" src="/files/4213/select-IE7-winxp.png" style="width: 81px; height: 23px;" /></td>
      <td style="text-align: center; vertical-align: top;"><img alt="Select open on IE7 on Windows XP" src="/files/4211/select-IE7-winxp-open.png" style="width: 82px; height: 74px;" /></td>
      <td style="text-align: center; vertical-align: middle;">N/A</td>
      <td style="text-align: center; vertical-align: middle;">N/A</td>
    </tr>
  </tbody>
</table>
<p>As you can see, even with the help of the -*-appearance properties, there is still remaining issues:</p>
<ul>
  <li>The {{cssxref("padding")}} property is handled inconsistently across OS and Browsers</li>
  <li>Legacy Internet Explorer does not allow smooth styling</li>
  <li>Firefox does not have a way to style the dropdown arrow.</li>
  <li>If you want to style the option elements inside the dropdown list, the behavior of Chrome and Opera change from one system to another.</li>
</ul>
<p>And with our example, we are just talking about 3 CSS properties; imagine the mess with all the CSS properties. So yes, in that case, CSS is not suitable to change the look and feel of this widgets consistently but it still allows you to tweak some things, but only if you are okay to live with differences from one browsers to another and from one OS to another.</p>
<p>We will try to figure which properties are suitable in the next article: <a class="new" href="https://developer.mozilla.org/en-US/docs/Properties_compatibility_table_for_forms_widgets" title="/en-US/docs/Properties_compatibility_table_for_forms_widgets">Properties compatibility table for forms widgets</a></p>
<h2 id="The_road_to_nicer_forms_.3A_useful_library_and_polyfills">The road to nicer forms: useful libraries and polyfills</h2>
<p>If CSS is expressive enough for check box and radio button, it is fare from the case for more advanced widgets. If a few things are possible with the select element, the file widget cannot be styled at all as well as the color picker, etc.<br />
  If you want to gain full control over form widgets, you have no choice but to relay on Javascript. In the article <a href="/en-US/docs/HTML/Forms/How_to_build_custom_form_widgets" title="/en-US/docs/HTML/Forms/How_to_build_custom_form_widgets">How to build custom form widgets</a> we will see how to do it on our own but there are some very useful libraries out there that can help you already:</p>
<ul>
  <li><a href="http://sprawsm.com/uni-form/" rel="external" title="http://sprawsm.com/uni-form/">Uni-form</a> is a framework that standardizes form markup and styles it with CSS. It also offer a few tweak with jQuery but it is optional.</li>
  <li><a href="http://formalize.me/" rel="external" title="http://formalize.me/">Fromalize</a> is an extension to common JavaScript framework (jQuery, Dojo, YUI, etc.) to normalize and to help you customize your forms.</li>
  <li><a href="http://www.emblematiq.com/lab/niceforms/" rel="external" title="http://www.emblematiq.com/lab/niceforms/">Niceforms</a> is a standalone JavaScript method that allows complete customization of web forms. You can use some of the build in theme or create your own.</li>
</ul>
<p>The following are not just about forms but they have very interesting part to deal with HTML forms:</p>
<ul>
  <li><a href="http://jqueryui.com/" rel="external" title="http://jqueryui.com/">jQuery UI</a> offer some very interesting advanced and customizable widgets, such as date pickers (with an effort made on accessibility)</li>
  <li><a href="http://twitter.github.com/bootstrap/base-css.html#forms" rel="external" title="http://twitter.github.com/bootstrap/base-css.html#forms">Twitter Bootstrap</a> can be really helpful if you want to normalize your forms.</li>
  <li><a href="http://afarkas.github.com/webshim/demos/demos/webforms.html" rel="external" title="http://afarkas.github.com/webshim/demos/demos/webforms.html">WebShim</a> is a huge tool to help you dealing with browser HTML5 support. The web forms part can be really helpful.</li>
</ul>
<p>Remember that binding CSS and JS can have side effects. So if you choose to use one of those libraries, you should always have a fallback style sheets in the case the script fail (and there is many reason for that, especially in the mobile world)</p>
<h2>Conclusion</h2>
<p>So in the end, yes, there are still dark parts when CSS comes to HTML Forms. There is no clean universal solution but modern browsers offer new possibilities. For now the best solution is to learn more about the way the different browser support CSS on HTML forms widgets.&nbsp;</p>
<p>In the next article of this guide we will explore the support of the most important CSS properties on HTML form widgets: <a class="new" href="https://developer.mozilla.org/en-US/docs/Properties_compatibility_table_for_forms_widgets" title="/en-US/docs/Properties_compatibility_table_for_forms_widgets">Properties compatibility table for forms widgets</a></p>
<h2>See also</h2>
<ul>
  <li><a href="http://diveintohtml5.info/forms.html" rel="external" title="http://diveintohtml5.info/forms.html">Dive into HTML5: Forms</a></li>
  <li><a href="http://www.smashingmagazine.com/2011/06/27/useful-ideas-and-guidelines-for-good-web-form-design/" rel="external" title="http://www.smashingmagazine.com/2011/06/27/useful-ideas-and-guidelines-for-good-web-form-design/">Useful ideas and guidelines for good web form design</a></li>
</ul>
Revert to this revision