Form elements are some of the trickiest HTML elements to style; each browser and operating system has its own idea of how these elements should look, and these defaults are hard (if not impossible) to override.

The <select> element is perhaps the most notoriously uncooperative, and its varied appearance across browsers can disrupt the consistency of a design. On a recent project, I tried to solve this problem using various jQuery plug-ins, only to find them inflexible and bloated. After some consideration, I realized that the effect I was aiming for really wasn’t that complicated: I wanted to preserve the browser’s native functionality across devices, while customizing the appearance of the menu when not focused.

To accomplish this, what we need to do is create our own element to display the selected option, without getting rid of the original. The first step is to make the select element invisible (without actually removing it). We need to use the following CSS to get rid of all default browser styling:

select { 
	border: none; 
	background: 
	none; outline: 
	none; opacity: 0; 
	-webkit-appearance: none; 
	filter: alpha(opacity=0); 
}

Next, we use a couple lines of jQuery to give us some more elements to work with: we’re going to wrap the whole thing in a wrapping div, add a span element to display the selected option in, and an empty span to hold our dropdown arrow (we could add the arrow using :after, but it wouldn’t work in IE7).

$("select").each(function(){ 
	$(this).wrap('<div class="selectbox"/>'); 
	$(this).after(<span class='selecttext'></span><span class='select-arrow'></span>"); 
});

Now we can populate our ‘selecttext’ element with the selected option. We have to do this once when the page loads, and then again any time the selected option changes.

$("select").each(function(){ 
	var val = $(this).children("option:selected").text(); 
	$(this).next(".selecttext").text(val); 
	$(this).change(function(){ 
		var val = $(this).children("option:selected").text(); 
		$(this).next(".selecttext").text(val); 
	});
});

Now that we have the elements we need, we just have to position the selecttext element *underneath* the original select element using z-index, so that clicking on the selecttext element triggers the original select box.

.selectbox select { 
 	z-index: 10; 
 	position: relative; 
 	border: none; 
 	background: none; 
 	outline: none; 
 	padding: 4px 12px 4px 32px; 
 	opacity: 0; 
 	-webkit-appearance: none; 
 	filter: alpha(opacity=0); 
 } 

 .selecttext { 
 	z-index: 9; 
 	position: absolute; 
 	left: 5px; 
 	display: inline-block; 
 	*display: inline; 
 	zoom: 1; 
 	padding-top: 4px; 
 	background: transparent; color: #666; 
 }

All that’s left to do now is to style the menu however you like by adding styles to the wrapping div element. I’ve also added in the arrow image and positioned it to the right of the menu.

 .selectbox { 
 	position: relative; 
 	display: inline-block; 
 	*display: inline; 
 	zoom: 1; 
 	border: 1px solid #aeaeae; 
 	background: #e6e6e6; 
 	background-image: -moz-linear-gradient(top, white, #e6e6e6); 
 	background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, white), color-stop(1, #e6e6e6)); 
 	background-image: linear-gradient(top, white, #e6e6e6); 
 	border-radius: 5px; box-shadow: 0 1px 1px #d2d2d2; 
 }

And there you have it, a custom-styled select box that still behaves as a native select menu. And we did it all without a big, bloated plugin while maintaining full control over the appearance of the button.

Though some designers would surely prefer to be able to style the appearance of the dropdown list itself, this approach can compromise accessibility and usability, especially on mobile devices.

Take a look at the demo.

The source code for the demo can be found on GitHub for your perusal.

  • Ephraim

    Hi Tessa. This is a great post and resource and have found your code to be the best thus far for styling of select menus. However, I am looking to be able to scroll the options (popup for the various options) when there are over a certain number. For example, being able to show ten options but limit the height of the dropdown of options and then set the overflow for that the scroll. Any advice using your code? Thanks again.

  • Raúl

    Hola Tessa! Thanks a lot for this post. It just what i needed! 🙂

  • Thanks for the post! Good hacky solution.

  • Demo isn’t working 🙁

  • very much helping and useful guide you have and now i can customize my select list menus because i really need for a eCommerce design project.

Discover and implement your big idea with our product team

Get in Touch
Close