Customizing form elements was always pretty frustrating and the select element is one of those elements who can make you want to pull your remaining hair out while trying to style it. As you may already know, when talking about its customization, there isn’t too much CSS stuff you can apply to it, just properties like color, background, font or border.
In this article, in order to customize the look of the native HTML dropdown select, we’ll be using a different approach based on some cutting edge techniques like CSS @supports
, pointer-events
and appearance
.
The need for custom dropdown selects
The browser defaults were never enough, that’s why web developers are constantly trying to push the limits and possibilities when it comes to customizing native HTML elements. Lately, we’ve seen so many custom checkboxes and radios, custom progress bars and many more but nothing new related to dropdowns or file inputs for example.
Having this in mind, and inspired by Lea Verou’s technique, I decided to share with you my take on customizing an HTML dropdown. Just as a disclaimer, I’m aware there are a lot of awesome jQuery plugins in the wild that can help you build beautiful and sophisticated dropdowns, but sometimes it feels like it is just too much for a dropdown.
The markup
To get started, below is the markup needed for a custom HTML dropdown.
You may wonder why I chose a span
as a wrapper and not a label
which might seem more appropriate. The reason is that we don’t want to waste the label
’s functionality for this customization only. Also, this way you can still wrap everything inside a label to improve usability like so:
BEM naming convention
In case you noticed the namespacing, I’m using the BEM naming convention which I’m sure you’ve seen before. Needless to say, this rocks!
The CSS
Check the styles below to see how everything works. The CSS @supports
rule does all the magic:
Summary
At first sight, the CSS might seem a bit too overwhelming, so let’s deconstruct it.
The CSS appearance
property
The appearance: none
declaration is used here to reset the default look for the native dropdown. The appearance
property is very useful when you want to add a specific styling to an element that doesn’t have it by default or to remove the specific styling entirely, as in this case.
The ::before
& ::after
pseudo-elements
Having applied the above declaration which clears the default look, the dropdown triangle is made using CSS pseudo-elements. There isn’t too much to say here, you know pseudos, they are everywhere.
The CSS pointer-events
If you aren’t familiar with pointer-events
, you should know that with pointer-events: none
, which is the most encountered declaration, you can remove mouse events for current page navigation for example. In this case, we removed mouse events for the right triangle CSS pseudo-element to avoid accessibility issues.
As a side effect, if an element has pointer-events: none
applied to, its hover state can’t be styled anymore.
The CSS @supports
at-rule
Last but not least, the latest CSS @supports
at-rule helps with CSS feature detection. Feature detection using JavaScript isn’t something new and Modernizr is the best example here. So, if you already heard about Modernizr, then think about the @supports
rule as its CSS equivalent.
To prevent browsers inconsistencies, the following rule allows you to target only browsers that have support for both pointer-events
and appearance
. The best examples here are the IE9 and IE10 browsers, both support a lot of CSS3 stuff from the above code but do not support (yet) cutting edge stuff like pointer-events
or appearance
which makes the technique inefficient.
Some JavaScript
The truth is that you may use the CSS only keyword to name this styling solution. But, to cover also the case when a select is disabled, you’ll need some JS to target the parent node for it and apply an HTML class like custom-dropdown--disabled
.
- Returns the list of the elements within the document that match
.custom-dropdown__select
. - For each disabled select, go to its parent node and append the
custom-dropdown--disabled
HTML class. This way, we’ll be able to use CSS in order to customize the arrow when a select is disabled.
Of course, none of the above will be necessary if the select element, which is a replaced element, would have allowed pseudo-elements on it or if a CSS parent selector would have existed. But that’s another story. :)
On browser support
As far as I tested, the demo is working on Firefox Nightly/Aurora, Chrome Canary and Opera Next. Also, this means that very soon you’ll be able to see this working also in the release versions, considering how often new browser versions are launched. Check the compatibility table for support of CSS Feature Queries in desktop and mobile browsers.
Besides the above browsers versions, while preparing to publish this article, Firefox 22 just launched as well. And guess what? Support for CSS Conditionals (@supports and CSS.supports) has been enabled by default. So, we’re on the right track! :)