How to style the default upload button? Problem

I want to style the default upload buttons. How should I go about doing this?
Since this was a bit of pain to work through I thought I'd post a solution for everyone.

The base of the solution comes from this post:

http://geniuscarrier.com/how-to-style-a-html-file-upload-button-in-pure-css/

There are is another solution to CSS only styling for upload buttons, but it involves wrapping the input element in a label, which is difficult
with how Drupal handles the markup.

1. Add CSS

First we add the following two chunks to our CSS file, you can read through the explanation at the post above basically we're making the file input button transparent and styling the area behind it. 

Then when you try to click on the area behind it, you actually click on the invisible input button.

.form-type-managed-file {
  position: relative;
  overflow: hidden;
}

input.form-file {
  position: absolute;
    top: 25px;
    right: 0;
    margin: 0;
    padding: 0;
    font-size: 20px;
    cursor: pointer;
    opacity: 0;
    filter: alpha(opacity=0);
}


Now you may have noticed that we want to style the area behind it. To that we're going to need to add an HTML element to the managed file output which we can then style:

2. Change HTML output

Copy the image file function into our template file (for this example we'll be changing the image upload style) and then add two divs after &lt;div class="image-widget-data"&gt;

function MY_THEME_image_widget($variables) {
  $element = $variables['element'];
  $output = '';
  $output .= '&lt;div class="image-widget form-managed-file clearfix"&gt;';

  if (isset($element['preview'])) {
    $output .= '&lt;div class="image-preview"&gt;';
    $output .= drupal_render($element['preview']);
    $output .= '&lt;/div&gt;';
  }

  $output .= '&lt;div class="image-widget-data"&gt;&lt;div class="bold file-button"&gt;Choose File&lt;/div&gt;
  &lt;div class="file-name file-button margin_10_top_bot"&gt;No File Selected&lt;/div&gt;';
  if ($element['fid']['#value'] != 0) {
    $element['filename']['#markup'] .= ' &lt;span class="file-size"&gt;(' . format_size($element['#file']-&gt;filesize) . ')&lt;/span&gt; ';
  }
  $output .= drupal_render_children($element);
  $output .= '&lt;/div&gt;';
  $output .= '&lt;/div&gt;';

  return $output;
}


If you don't care about the filename of the uploaded file remove the second div and you're done. (You'll need to alter the CSS to make the invisible button sit above your added upload div.)

However if you want the name of the file then leave it in and we move onto a little JS.

3. Add JS

It's impossible the get the name of the file without JS so we add a quick JS script to get it to run, first we call it in the template file:

function MY_THEME_preprocess_page (&amp;$vars) {
    drupal_add_js('sites/all/themes/MY_THEME/js/file_upload.js', 'file');
}


Then we add the following script:

(function ($) {
  Drupal.behaviors.uploadfunctionAddClass = {
    attach: function (context, settings) {
      $(":file").change(function(){
        $(this).prev().text($(this).val().replace(/C:\\fakepath\\/i, ''));
      });
    }
  };
})(jQuery);


It looks for any changes to elements of type file, which in the output for a drupal managed file field is one.

Grabs the element directly before it (the second div we added in step 2) and changes the text in it to it's new value. The path will have C:\fakepath\ added to it, so we remove that as well.

The reason we use Drupal Behaviors rather than document ready, is because we need the script to apply after an AJAX call. If someone uploads a picture, removes it and adds a different one then the script won't have applied to the new elements because they've been added with AJAX. The Drupal Behaviors wrapper causes them to be called every time.

Congrats we're all done.