Pure CSS Custom UI Elements Part 1 – Customizing Default Form Elements

This tutorial will teach you how to customize the default UI of some of the most basic form elements using CSS. Specifically, we will customize the default radio buttons, checkboxes, select (aka dropdown) lists, and buttons in Chrome, Safari, Firefox, and Internet Explorer.

With CSS3 we have the ability to customize the appearance of almost any element we want. That’s awesome! Why? Because that means you don’t need to know (or, more importantly, use) any JavaScript. Now all this customization may sound daunting, and granted it does take a few extra steps to get these beautiful customizations actually looking beautiful in all major browsers, but do not be intimidated! The end result is well worth the effort.


First things first, we need to add the markup for the elements that make up our form. This is all of the HTML we will need. Copy and paste this code into your working file:

<!-- Radio Buttons - using images -->
  <input id="male" type="radio" name="gender" value="male">
  <label for="male">Male</label>
  <input id="female" type="radio" name="gender" value="female">
  <label for="female">Female</label>
<!-- Checkboxes - using images -->
  <input id="check1" type="checkbox" name="check" value="check1">
  <label for="check1">Checkbox One</label>
  <input id="check2" type="checkbox" name="check" value="check2">
  <label for="check2">Checkbox Two</label>
<!-- Dropdown List - using images -->
    <option value="1">01</option>
    <option value="2">02</option>
    <option value="3">03</option>
    <option value="2000">2000</option>
    <option value="2001">2001</option>
    <option value="2002">2002</option>
<!-- Buttons - no images! -->
<!-- Two-tone Button -->
  <a href="#">next</a>
  <span class="buttonArrow">&#9656;</span>
<!-- Gradient Button -->
<a href="#" class="grayButton">Cancel</a>


Next comes the fun part: the styling, the customization, the CSS! We are going to keep the basic functionality of our elements but, with just a few rules, change their entire appearance and better integrate them into our overall design. When we’re done, they will look and feel like a well-thought-out, well-executed design element rather than an afterthought.

Radio Buttons

Let’s start with the radio button. We are going to customize the browser’s default UI to look like the screenshots below:

For reference, this is what we are starting out with, by default, after only adding the HTML:

First, we need to indicate that the radio button and checkbox inputs are clickable by changing their label cursors to pointers. We should also adjust their spacing properties to fit our design. Note: The following styles are shared between both the radio buttons and checkboxes:

/* Shared Label Styles */
label {
  cursor: pointer; /* indicates the element is clickable */
  display: inline-block;
  position: relative;
  padding-left: 0px;
  margin-right: 10px;  /* margin between input types */
label:before {
  content: "";
  width: 15px;
  height: 15px;
  position: absolute;
  left: 0;

Now we hide the input radio type, which hides the browser’s default element and style:

/* Radio Buttons */
input[type=radio] {
  display: none;  /* hides the default input element */

Next, to achieve the circle effect of the radio button, we must adjust its border-radius to half of its height and width. We also need to link our custom unselected (or empty) image by replacing its URL with the background-images’s default one below:

.radio label:before {
  border-radius: 8px;	
  background: url(your_unselected_image.png) left center no-repeat;

Lastly, we must link our custom selected (or checked) image to our “checked” radio input:

input[type=radio]:checked + label:before {
  background: url(your_selected_image.png) left center no-repeat;

Keep reading to learn how to style checkboxes, dropdown lists, and buttons.


Now that we’ve finished styling our radio buttons, let’s work on styling our checkboxes. Our final product will look like this:

And this is what we start with by default:

Change the checkbox’s display to none, which hides the browser’s default input:

/* Checkboxes */
input[type=checkbox] {
  display: none; /* hides the default input element */

Add your custom unselected (or empty) image as the checkbox label’s background, and adjust any margins as necessary:

.checkbox label:before {
  background: url(your_unchecked_image.png) left center no-repeat;
  margin-top: 1px; /* aligns the input element to the label's baseline */

Remember to also add your custom selected (or checked) image as the input’s “checked” background:

input[type=checkbox]:checked + label:before {
  background: url(your_checked_image.png) left center no-repeat;

Dropdown List

Another major form element is the dropdown (or, technically, the “select”) list. Although this element may seem a bit trickier to get right, don’t worry—it’s not. Once we are done, our dropdown list will look like this:

And by default, this is what we start with after adding the HTML:

/* Dropdown List */
.dropdown-wrapper {
  position: relative;
  overflow: hidden;

First, we remove the browser’s default border and styling:

.dropdown {
  border: none; /* removes default border */
  -webkit-appearance: none; /* removes default chrome and safari style */
  -moz-appearance: none; /* removes default firefox style */

After removing the default styling, this is what we are left with:

We now add our custom dropdown, or arrow, image. Adjust the image’s background-position until it aligns correctly with your design. Note: Make sure to save your image as a PNG with a clear background. It also helps to save your image at the same height of your select field:

  background: url('your_image_name.png') no-repeat;
  background-position: right top;

Set your dropdown’s height and width, and remember to add enough width to include your custom image:

  width: 100px; /* width of dropdown list */
  height: 40px;

Lastly, we apply our final custom elements. In this case, we are using a non-rounded, light gray border and indenting our text 15 pixels. Note: use the last two styles for Firefox compatibility:

  color: #000;
  border: 1px solid #cccccc; 
  border-radius: 0px; /* removes rounded corners */
  background-color: #fff;
  text-indent: 15px; 
  /* use these styles for firefox
  text-indent: 0.01px; 
  text-overflow: ""; */

Note: include this style for Internet Explorer compatibility:

select::-ms-expand {
  display: none;


The last element we are going to customize is our button. Because we are creating our buttons with pure CSS (no images used here!) they are 100% editable and scalable. We will be using two buttons and creating custom looks for each of them: one two-tone button, and one gradient button. When we’re done, our buttons will look like this:

But we see this by default:

Like earlier, we need to indicate that our buttons are clickable by turning their label cursors to pointers. This rule applies to both buttons. We will also determine our buttons’ heights and widths, which again happen to be the same, but please adjust accordingly to your needs. Note: your line-height should always equal your height in order to guarantee your text aligns vertically correctly:

/* Buttons */
/* Two-tone Button */
.twoButton {
  cursor: pointer; /* indicates the element is clickable */
  display: inline-block;
  height: 40px;
  line-height: 40px; /* vertically aligns the text */

To create the two-tone background, we need to set a darker color for the background-color and use a box-shadow to create the lighter color on top:

  background-color: #1EB896; /* darker (bottom) color */
  box-shadow: inset 0px 40px 0px -20px #32CCAA; /* lighter (top) color */
  -moz-box-shadow: inset 0px 40px 0px -20px #32CCAA; /* for firefox */
  -webkit-box-shadow: inset 0px 40px 0px -20px #32CCAA; /* for chrome and safari */

Here is where we get to play around with our text. We use padding to determine the space between our text and our button’s edge, or the overall width of our button:

.twoButton a {
  padding: 0px 45px; /* left and right padding around text */
  font-family: 'Open Sans', Arial, sans-serif;
  font-size: 16px;
  font-weight: 600;
  color: #fff;
  text-transform: uppercase;
  text-shadow: 1px 1px 0px rgba(0,0,0,0.25);

Since the button is a link we need to clarify that there should be no text underline. To do this we set text-decoration to none:

  text-decoration: none;

And to get our small right arrow to appear correctly, we give it the following properties:

.buttonArrow {
  color: #fff;
  position: relative;
  right: 15px; /* arrow's width from the button's right side */

With our two-tone button complete, we can now work on our gradient button. We will also be including an inverted hover class. Exciting stuff!

Again, indicate your button is clickable with a pointer cursor, and include the height of your button:

/* Gradient Button */
.gradientButton {
  cursor: pointer; /* indicates the element is clickable */
  display: inline-block;
  height: 40px;
  line-height: 40px; /* vertically aligns the text */
  padding: 0px 45px; /* left and right padding around text */

OK, now for the gradient background. This is where we add the beginning and end colors of our gradient. Use your lighter (or top) color as the first hex value (i.e., #f0f0f0) and your darker (bottom) color as the second hex value (i.e., e2e2e2). This may look like a lot of complicated code, but it is really just one main rule with multiple browser fixes:

  background: linear-gradient(to bottom, #f0f0f0 5%, #e2e2e2 100%); /* lighter (top) color is first hex value (i.e. #f0f0f0), darker (bottom) color is second hex value (i.e. #e2e2e2) */
  background: -webkit-linear-gradient(top, #f0f0f0 5%, #e2e2e2 100%); /* for chrome and safari */
  background: -moz-linear-gradient(top, #f0f0f0 5%, #e2e2e2 100%); /* for firefox */
  background: -o-linear-gradient(top, #f0f0f0 5%, #e2e2e2 100%); /* for opera */
  background: -ms-linear-gradient(top, #f0f0f0 5%, #e2e2e2 100%); /* for internet explorer */
  filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f0f0f0', endColorstr='#e2e2e2',GradientType=0);

Remember to also specify your darker color as the background-color value:

  background-color: #e2e2e2; /* darker (bottom) hex value */

Phew. With the gradient inputs behind us there are only a few styles left. We should determine our button’s border, rounded corner radius, and font styles here:

  border: 2px solid #dcdcdc;
  border-radius: 5px; /* rounded corners */
  -moz-border-radius: 5px; /* for firefox */
  -webkit-border-radius: 5px; /* for chrome and safari */
  font-family: 'Open Sans', Arial, sans-serif; /* now: Arial */
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  color: #8b8b8b;

And now we come to our last property: our inverted gradient hover. To accomplish this, we use all of the same background gradient properties as before and simply reverse the colors. This means your darker color will now be the first hex value and your lighter color will be the second:

.gradientButton:hover {
  background: linear-gradient(to bottom, #e2e2e2 5%, #f0f0f0 100%); /* darker (bottom) color is first hex value (i.e. #e2e2e2), lighter (top) color is second hex value (i.e. #f0f0f0) */
  background: -webkit-linear-gradient(top, #e2e2e2 5%, #f0f0f0 100%); /* for chrome and safari */    
  background: -moz-linear-gradient(top, #e2e2e2 5%, #f0f0f0 100%); /* for firefox */
  background: -o-linear-gradient(top, #e2e2e2 5%, #f0f0f0 100%); /* opera */
  background: -ms-linear-gradient(top, #e2e2e2 5%, #f0f0f0 100%); /* for internet explorer */
  filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#e2e2e2', endColorstr='#f0f0f0',GradientType=0);

And don’t forget to replace the background-color with your lighter color:

  background-color: #f0f0f0; /* lighter (top) hex value */

There you have it. All the code you need to make some pretty significant visual changes to your default UI elements. Now that you have this knowledge, you can venture into more UI customization territories and continue to learn and experiment. If you want to play around more with this tutorial, make instant changes, or just view it in a code reader, head on over to Codepen and go crazy!

Codepen/Tutorial Links

All:  codepen.io/seskew/
Radio:  codepen.io/seskew/pen/ofilw
Checkboxes:  codepen.io/seskew/pen/uByGK
Dropdown:  codepen.io/seskew/pen/ruHFn
Button, Two-tone:  codepen.io/seskew/pen/ubyHx
Button, Gradient:  codepen.io/seskew/pen/DEbpe


Radio + Checkboxes:  hongkiat.com/blog/css3-checkbox-radio
Dropdown:  uplifted.net/programming/change-default-select-dropdown-style-just-css
Buttons:  bestcssbuttongenerator.com/#/JHga03UyEn
Open Sans Typeface:  google.com/fonts#UsePlace:use/Collection:Open+Sans


2 thoughts on “Pure CSS Custom UI Elements Part 1 – Customizing Default Form Elements

  1. This design is wicked! You most certainly
    know how to keep a reader amused. Between your wit and your videos, I
    was almost moved to start my own blog (well, almost…HaHa!) Great job.
    I really loved what you had to say, and more than that, how
    you presented it. Too cool!

  2. Hello my name is Natalie and I just wanted to send you a quick note here instead of calling you. I came to your Pure CSS Custom UI Elements Part 1 – Customizing Default Form Elements – Sarah Holley Design website and noticed you could have a lot more traffic. I have found that the key to running a successful website is making sure the visitors you are getting are interested in your website topic. There is a company that you can get keyword targeted traffic from and they let you try their service for free for 7 days. I managed to get over 300 targeted visitors to day to my website. Visit them here: http://janluetzler.de/9zw

Leave a Reply

Your email address will not be published. Required fields are marked *