Cloud Four Blog

Technical notes, War stories and anecdotes

Two pretty-good techniques for styling tricky form elements

Confession time: For most of my career, I despised form elements. Checkboxes, radios, selects and file inputs seemed to gleefully defy what little control I expected from an HTML element. Their penchant for idiosyncracy drove me to almost as much hair-pulling and teeth-gnashing as IE6 or web-safe fonts.

These days, my frustration with form elements has quieted. Partly that’s because browsers and development tools are so much better. But more significantly, I now understand the benefits of surrendering some control to the operating system. As devices continue to accept a greater and greater variety of input methods (keyboard, mouse, touch, voice, gesture, remote, etc.) while browsers adopt an astounding variety of new input types , it’s a gift for vendors to provide default experiences consistent with the user’s expectations of the platform.

So I no longer strive for “pixel perfection” when styling form elements. I don’t need absolute control. All I want is something easy to tap that feels intentional.

When the browser defaults don’t get me there, here are my go-to workarounds.

Checkboxes and Radios: Styled Sibling

This technique works in any browser that supports CSS3 selectors (basically IE9+). If you read Radio-Controlled Web Design a few weeks ago, this should feel familiar. Let’s start with a checkbox example.

We’ll need a few HTML elements:

  • The <input> itself.
  • A dummy element to style (right next to the <input>).
  • A containing <label> that passes click events to the aforementioned <input>.

I like to wrap the <input> and dummy elements in a container to keep everything nice and tidy, but strictly speaking it isn’t required. Here’s what that markup might look like:

<label>
  <span class="checkbox">
    <input type="checkbox">
    <span class="checkbox-value" aria-hidden="true"></span>
  </span>
  Set phasers to stun
</label>

We’re now free to visually hide the checkbox, styling .checkbox-value however we like:

/* hide the "real" checkbox visually */
.checkbox input {
  border: 0;
  clip: rect(0 0 0 0);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  width: 1px;
}
 
/* style the "fake" checkbox */
.checkbox-value {
  /* default/unchecked styles */
}
input:checked + .checkbox-value {
  /* checked styles */
}

When the user clicks the label, the click is passed along to the <input>, which toggles the state of :checked, which affects the appearance of .checkbox-value.

Here’s an example that styles the checkbox like an iOS-style switch:

See the Pen Styled checkbox by Tyler Sticka (@tylersticka) on CodePen.

Here’s the same idea applied to radio buttons with a slightly more conventional design (incorporating a base64-encoded SVG checkmark):

See the Pen Styled radios by Tyler Sticka (@tylersticka) on CodePen.

This technique has a few drawbacks. It requires some extra markup. It won’t work in IE8 or earlier without a fallback. It could probably use another pass for accessibility. But compared to most of the JavaScript solutions I’ve tried, this feels straightforward, consistent and predictable.

Selects and File Inputs: Transparent Overlay + JavaScript

For more complex elements like <select> and <input type="file">, we can’t get by on CSS alone (though it gets us further than one might expect).

Our markup is similar to the previous set of checkbox/radio examples, except we won’t need a <label> for click events:

<div class="select">
  <select>
    <option>Option 1</option>
    <option>Option 2</option>
    <option>Option 3</option>
  </select>
  <span class="select-value" aria-hidden="true"></span>
</div>

Instead of hiding the <select> entirely, we want to position it over the rest of our element, allowing it to intercept click events and correctly position any dropdown it may display. Because this technique relies on JavaScript, we’ll qualify some of our selectors with .js (since you’re probably already using Modernizr).

.js .select {
  position: relative;
  /* default styles */
}
.js .select:hover {
  /* hover styles */
}
.js .select.focus {
  /* focus styles */
}
 
/* nicer default styles for "real" <select> */
.select select {
  cursor: pointer;
  display: block;
  width: 100%;
}
/* hide and overlay when JavaScript is enabled */
.js .select select {
  left: 0;
  height: 100%;
  min-height: 100%;
  min-width: 100%;
  opacity: 0;
  position: absolute;
  top: 0;
}

Already, this “works.” Options will display on click. But there are some problems. The value doesn’t update. There are no hover or focus styles. That’s where JavaScript comes in!

(Although I’ve chosen to write this in jQuery for the sake of readability, remember: You Might Not Need jQuery!)

// For each .select element
$('.select').each(function(){
  // Save some elements as variables
  var $element = $(this);
  var $select = $element.find('select');
  var $value = $element.find('.select-value');
  // Bind event handlers to <select>
  $select.on({
    // On change or keyup, update the value text
    'change keyup': function () {
      $value.text($select.val());
    },
    // On focus, add the focus class
    'focus': function () {
      $element.addClass('focus');
    },
    // On blur, remove the focus class
    'blur': function () {
      $element.removeClass('focus');
    }
  });
  // Trigger the change event so the value
  // is current
  $select.trigger('change');
});

Here’s how all of that comes together:

See the Pen Styled select by Tyler Sticka (@tylersticka) on CodePen.

With some tweaks, the same basic technique can also work for file inputs (assuming experimental WebKit/Blink features aren’t your thing):

See the Pen Styled file input by Tyler Sticka (@tylersticka) on CodePen.

This idea isn’t new. Peter-Paul Koch wrote about it quite a while back. Yet I rarely see it in use outside of a few large mobile frameworks. I’m honestly not sure why.

…and beyond?

What do all of these examples have in common? They don’t mess with the form element too much! By worrying less about customizing behavior and more on simply triggering it, we can indulge some of our designerly impulses without discarding all a given platform has to offer.

Consistency and functionality… no hair-pulling or teeth-gnashing required!

Update: September 8, 2014

A reader pointed out that the select example wasn’t responding to keyboard input in Firefox. I discovered that Firefox doesn’t fire the change event for selects like other browsers do, so I’ve updated the demo and example code so that it binds to both change and keyup.

I also learned that Firefox doesn’t show the full dropdown on any keypress, but this seems to be true of unstyled <select> elements as well. I encourage developers to use these examples as a starting point, and to augment usability shortcomings on a case-by-case basis if the default browser behavior isn’t cutting it.

iPhone’s Magical Volume Buttons

A recent project pushed the boundaries of what is possible in mobile browsers. We learned a lot including some surprisingly simple things that everyone takes for granted like the volume buttons on the iPhone and how they work.

One of the features was a quiz that people could take on their phone. The quiz design was handled by another agency and they provided us with some cool sound files to use when the person got an answer right or wrong.

We built the site using HTML5 and quickly found that Android didn’t support HTML5 audio. Android 2.3 now supports HTML5 audio, but it was too late for this project.

On the iPhone side, we discovered what appeared to be a strange bug related to audio. Adjusting the volume didn’t seem to have any affect on the volume of the HTML5 audio clips. We began to fear that people playing the quiz would be upset when an ear drum piercing buzz came from the quiz and they couldn’t turn it down.

After nearly two weeks of believing HTML5 audio was broken, we encountered a phone that used to be loud that was suddenly quiet. What’s going here?

It was then that we learned about iPhone volume contexts. If you own an iPhone, you’re already aware of these volume contexts, but never consciously think about them because 99.9% of the time, it just works.

What do I mean by volume contexts? There is only one set of volume buttons and depending on what your iPhone is doing, they adjust different volume settings.

For example, if no audio or video is playing, the volume buttons adjust the ringer volume. However, when audio or video is playing, the volume buttons control the media audio level.

iPhone volume indicators

The iPhone provides feedback on what volume you’re adjusting by adding “ringer” above the volume level when you’re adjusting the ring level and leaves that out when you’re adjusting the volume of other audio.

But it’s not simply ringer or media contexts. The iPhone also keeps track of the volume level separately in each of these contexts:

* Headphone
* Headphone w/ microphone
* Speakers
* Bluetooth headsets

There may be more that I’m not aware of. I also don’t own a bluetooth headset so I’m relying on what I’ve been told.

The point is that it was nearly four years after owning my first iPhone before I gave any thought to how the iPhone was doing this. This is truly a remarkable design. It handles at minimum six different volume settings without the user ever giving thought to what volume they want to control.

They simply use the two volume buttons and 99.9% of the time the phone magically knows what they want it to do. You never have to think about it.

Until you build a quiz that falls into the .1% of the time when it doesn’t work the way you expect.

Our problem? the audio files were very short. Around a second each.

And unless you’re a speedster, you can’t hit the volume buttons quickly enough during that one second of audio.

Once that second passes, you’re back to adjusting the ringer volume and mistakenly wondering why the volume buttons don’t work on HTML5 audio.

Webkit: The Dominant Smartphone Platform

Based on Q2 sales of smartphones, Webkit-based browsers may soon ship on 85% of all smartphones sold.

Pie chart showing market share for webkit based on smart phone OS

Please keep in mind, this is not the reality right now. This number assumes RIM’s purchase of Torch Mobile really means that future Blackberry Browsers will based on Webkit.

There are some other caveats as well:

  • This understates Opera’s mobile market position. Opera has a large installed base of users.
  • It assumes market percentages will stay the same. We know this won’t be true.
  • It assumes that all of the “other” smartphone OS browsers are not using Webkit currently.
  • Mobile Firefox is just getting started. It is unclear how that will change the landscape.
  • Just because it is Webkit, doesn’t mean that it is the latest version of Webkit.

Caveats aside, you would be hard pressed to find another smartphone development platform with any where close to Webkit’s market share.

More importantly, this means that HTML5 for mobile is looking great. If Blackberry joins the ranks of Webkit-based browsers, that will means Symbian, iPhone, Android, Palm and Blackberry will all be on the path to HTML5 support.

The only laggard will be Mobile Internet Explorer, and even for Windows Mobile there are options like Google Gears which adds some HTML5 support to IE or installing other browsers like Opera or Firefox.

Google Argues for Mobile Web

At the recent MobileBeat Conference, Google VP of Engineering Vic Gundotra told the audience that the Mobile Web will be the larger portion of mobile application development in the long run instead of native applications.

Mr. Gundotra argued the web was the only technology to provide the potential for cross platform development. He said not “that even Google was not rich enough to support all of the different mobile platforms from Apple’s AppStore to those of the BlackBerry, Windows Mobile, Android and the many variations of the Nokia platform.”

His comments echo those that he made earlier at Google I/O. I recommend reading Tim O’Reilly’s summary of his presentation.

This is very similar to what I’ve been saying in my presentations at Web 2.0 Expo and Web Visions.

I’m not arguing that native applications will not continue to be important. I’m simply arguing that there will be far more mobile web sites and mobile web applications than native applications over the long term.

HTML 5 Resources & Discussion

I wanted to include a series of links at the end of my previous post on HTML 5, but I was afraid they would get lost after such a long post. Here they are:

HTML 5 Developer Group in Portland

A new HTML 5 developer group has formed in the Portland area called HTML 5 PDX.

The first meeting was Monday and had a representative from Firefox speaking (Dietrich Ayala) as well as a designer (Bram Pitoyo) who spoke to Safari’s HTML 5 support. Igal Koshevoy posted detailed meeting notes.

HTML 5 Resources and Demos

HTML 5 / XHTML2 Discussion