Friendlier HTML form controls with a little CSS magic. Designed for IE9+, as well as the latest Chrome, Safari, and Firefox.

Checkboxes and radios

Check this custom checkbox

<label class= "control checkbox" > <input type= "checkbox" > <span class= "control-indicator" ></span> Check this custom checkbox </label>

Toggle this custom radio Or toggle this other custom radio

<label class= "control radio" > <input id= "radio1" name= "radio" type= "radio" > <span class= "control-indicator" ></span> Toggle this custom radio </label> <label class= "control radio" > <input id= "radio2" name= "radio" type= "radio" > <span class= "control-indicator" ></span> Or toggle this other custom radio </label>

Each checkbox and radio is wrapped in a <label> for three reasons:

It provides a larger hit areas for checking the control. It provides a helpful and semantic wrapper to help us replace the default <input> s. It triggers the state of the <input> automatically, meaning no JavaScript is required.

We hide the default <input> with opacity and instead use the <span class="control-indicator"> within the <label> to build a new custom form control.

With the sibling selector ( ~ ), we use the :checked state to trigger a makeshift checked state on the custom control.

In the checked states, we use base64 embedded SVG icons from Open Iconic. This provides us the best control for styling and positioning across browsers and devices.

Alternate icons

By default, checkboxes use a checkmark and radios use an filled circle. Also included are two modifier classes, .control-x and .control-dash , to change things up should the need arise.

Add the modifier classes to the <label> , like so:

<label class= "control checkbox control-x" > ... </label>

Want to customize the icons further, or use other ones? Download Open Iconic—included are font files, PNGs, and SVGs.

Select menu

Open this select menu One Two Three

<div class= "select" > <select aria-label= "Select menu example" > <option selected > Open this select menu </option> <option value= "1" > One </option> <option value= "2" > Two </option> <option value= "3" > Three </option> </select> </div>

We wrap the <select> in a <div> as a wrapper that we can generate custom styles on with CSS's generated content.

Previously, we used a <label> as the wrapper, but this can result in unintended behavior with assistive technologies. For instance, some screen readers will end up reading all <option> s contained in the <select> as one long label for the form control. To still provide label text for assistive technologies, we instead use an appropriate aria-label attribute.

The <select> has quite a few styles to override and includes a few hacks to get things done. Here's what's happening:

The appearance is reset to none for nearly all styles to correctly apply across modern browsers (meaning not IE9).

is reset to for nearly all styles to correctly apply across modern browsers (meaning not IE9). The :-moz-focusring is overridden so that on focus there's no inner border in Firefox.

is overridden so that on focus there's no inner border in Firefox. The arrow is hidden in Firefox with a media query hack. (There's a longstanding open bug for a native method of addressing this.)

The arrow is hidden in IE10+ with a simple selector.

The arrow is hidden in IE9 with a separate media query hack which generates another pseudo-element to literally mask it. Not ideal, but doable.

Heads up! This one comes with some quirks right now:

Clickability is limited in IE9.

Firefox's dropdown of option s looks rather ugly.

s looks rather ugly. The custom caret is unable to receive the selected state's color .

. Styling of select[multiple] elements is basically impossible because <option> s are presently unstylable in all browsers.

Any ideas on improving these are most welcome.

File browser

<label class= "file" > <input type= "file" id= "file" aria-label= "File browser example" > <span class= "file-custom" ></span> </label>

The file input is the most gnarly of the bunch. Here's how it works:

We wrap the <input> in a <label> so the custom control properly triggers the file browser.

in a so the custom control properly triggers the file browser. We hide the default file <input> via opacity .

via . We use :after to generate a custom background and directive (Choose file...).

to generate a custom background and directive (Choose file...). We use :before to generate and position the Browse button.

to generate and position the Browse button. We declare a height on the <input> for proper spacing for surrounding content.

on the for proper spacing for surrounding content. We add an explicit label text for assistive technologies with an aria-label attribute.

In other words, it's an entirely custom element, all generated via CSS.

Heads up! The custom file input is currently unable to update the Choose file... text with the filename. Without JavaScript, this might not be possible to change, but I'm open to ideas.

Progress

25% 50% 75% 100%

<progress class= "progress" value= "25" max= "100" > 25% </progress> <progress class= "progress" value= "50" max= "100" > 50% </progress> <progress class= "progress" value= "75" max= "100" > 75% </progress> <progress class= "progress" value= "100" max= "100" > 100% </progress>

The <progress> element is actually pretty straightforward to style, but it does have some gotchas. Here's the deal:

Unlike most other custom inputs, we don't wrap it in an extra element. We just add .progress .

. Using pseudo selectors, we target aspects of the <progress> element. For example, in WebKit browsers, ::-webkit-progress-bar is the background bar and ::-webkit-progress-value is the colored progress bar within.

element. For example, in WebKit browsers, is the background bar and is the colored progress bar within. Firefox has it's own pseudo selectors that must be applied with duplicate rulesets. Chaining ::-webkit- and ::-moz- pseudo selectors will nullify your styles. (It's worth noting this happens with other pseudo selectors like placeholder ).

and pseudo selectors will nullify your styles. (It's worth noting this happens with other pseudo selectors like ). IE10 is the only version of IE to support <progress> . The only quirk there is that you must set the color property on the <progress> element to colorize the progress bar within.

For more information, read the rather thorough CSS Tricks article. There you'll find gotchas around generated content, browser quirks, and more. The MDN also has an informative article on the progress element.

Custom width

The width is automatically set by the browser, but you can easily change it by adding some CSS to .progress .

.progress { width : 100% ; }

Internet Explorer 9 support

25%

<progress class= "progress" value= "25" max= "100" > <div class= "progress" > <span class= "progress-bar" style= "width: 25%;" > 25% </span> </div> </progress>

IE10 natively supports the <progress> element, but IE9 doesn't. Instead, they simply show as plain text values. However, IE9 support is just a few hacks away:

We add custom markup within the existing <progress> element to simulate the background and inner progress bar.

element to simulate the background and inner progress bar. We copy-paste the styles from the pseudo selectors to a IE9-specific media query (like I said, a few hacks) to only apply when necessary.

Lastly, to simulate the native <progress> behavior, we add text-indent: -999rem; to the custom .progress-bar to hide the text value.

Once applied, you can use the IE9-compatible snippet in all browsers. However, I encourage you to only do so should you need IE9 support.

FAQs

What about every other form control?

For the time being, WTF, forms? is limited to checkboxes, radio buttons, select menus, file inputs, and progress bars. Additional custom inputs will depend on browser support.

Why are there no for attributes?

We nest our <input> s and <select> s within a <label> , so there's no need to specify a for attribute as the browser will automatically associate the two.

What about hover states?

Basic hover styles have been included, but they've been commented out because they are sticky on iOS. Uncomment if you really need it.

Does this require JavaScript?

Not for the time being, however, the file input might be better off with it.

Will this be added to Bootstrap?

Possibly, but not until v4 at the earliest.

Is this screen reader friendly?

Initial testing done by @patrick_h_lauke (on version 2.2.0) with various standard combinations (Internet Explorer/Firefox with JAWS15/NVDA on Windows, Safari with VoiceOver on OS X and iOS, Chrome with TalkBack on Android) does not show any adverse effects.

Changelog

For a full changelog, visit the releases page on GitHub.

This project utilizes SemVer for versioning releases for maximum backward compatibility.