Cross-browser custom styling for file upload button

173,357

Solution 1

I'm posting this because (to my surprise) there was no other place I could find that recommended this.

There's a really easy way to do this, without restricting you to browser-defined input dimensions. Just use the <label> tag around a hidden file upload button. This allows for even more freedom in styling than the styling allowed via webkit's built-in styling[1].

The label tag was made for the exact purpose of directing any click events on it to the child inputs[2], so using that, you won't require any JavaScript to direct the click event to the input button for you anymore. You'd to use something like the following:

label.myLabel input[type="file"] {
    position:absolute;
    top: -1000px;
}

/***** Example custom styling *****/
.myLabel {
    border: 2px solid #AAA;
    border-radius: 4px;
    padding: 2px 5px;
    margin: 2px;
    background: #DDD;
    display: inline-block;
}
.myLabel:hover {
    background: #CCC;
}
.myLabel:active {
    background: #CCF;
}
.myLabel :invalid + span {
    color: #A44;
}
.myLabel :valid + span {
    color: #4A4;
}
<label class="myLabel">
    <input type="file" required/>
    <span>My Label</span>
</label>

I've used a fixed position to hide the input, to make it work even in ancient versions of Internet Explorer (emulated IE8- refused to work on a visibility:hidden or display:none file-input). I've tested in emulated IE7 and up, and it worked perfectly.


  1. You can't use <button>s inside <label> tags unfortunately, so you'll have to define the styles for the buttons yourself. To me, this is the only downside to this approach.
  2. If the for attribute is defined, its value is used to trigger the input with the same id as the for attribute on the <label>.

Solution 2

Please find below a way that works on all browsers. Basically I put the input on top the image. I make it huge using font-size so the user is always clicking the upload button.

.myFile {
  position: relative;
  overflow: hidden;
  float: left;
  clear: left;
}
.myFile input[type="file"] {
  display: block;
  position: absolute;
  top: 0;
  right: 0;
  opacity: 0;
  font-size: 100px;
  filter: alpha(opacity=0);
  cursor: pointer;
}
<label class="myFile">
  <img src="http://wscont1.apps.microsoft.com/winstore/1x/c37a9d99-6698-4339-acf3-c01daa75fb65/Icon.13385.png" alt="" />
  <input type="file" />
</label>

Solution 3

The best example is this one, No hiding, No jQuery, It's completely pure CSS

http://css-tricks.com/snippets/css/custom-file-input-styling-webkitblink/

.custom-file-input::-webkit-file-upload-button {
    visibility: hidden;
}

.custom-file-input::before {
    content: 'Select some files';
    display: inline-block;
    background: -webkit-linear-gradient(top, #f9f9f9, #e3e3e3);
    border: 1px solid #999;
    border-radius: 3px;
    padding: 5px 8px;
    outline: none;
    white-space: nowrap;
    -webkit-user-select: none;
    cursor: pointer;
    text-shadow: 1px 1px #fff;
    font-weight: 700;
    font-size: 10pt;
}

.custom-file-input:hover::before {
    border-color: black;
}

.custom-file-input:active::before {
    background: -webkit-linear-gradient(top, #e3e3e3, #f9f9f9);
}
<input type="file" class="custom-file-input">

Solution 4

This seems to take care of business pretty well. A fidde is here:

HTML

<label for="upload-file">A proper input label</label>

<div class="upload-button">

    <div class="upload-cover">
         Upload text or whatevers
    </div>

    <!-- this is later in the source so it'll be "on top" -->
    <input name="upload-file" type="file" />

</div> <!-- .upload-button -->

CSS

/* first things first - get your box-model straight*/
*, *:before, *:after {
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
}

label {
    /* just positioning */
    float: left; 
    margin-bottom: .5em;
}

.upload-button {
    /* key */
    position: relative;
    overflow: hidden;

    /* just positioning */
    float: left; 
    clear: left;
}

.upload-cover { 
    /* basically just style this however you want - the overlaying file upload should spread out and fill whatever you turn this into */
    background-color: gray;
    text-align: center;
    padding: .5em 1em;
    border-radius: 2em;
    border: 5px solid rgba(0,0,0,.1);

    cursor: pointer;
}

.upload-button input[type="file"] {
    display: block;
    position: absolute;
    top: 0; left: 0;
    margin-left: -75px; /* gets that button with no-pointer-cursor off to the left and out of the way */
    width: 200%; /* over compensates for the above - I would use calc or sass math if not here*/
    height: 100%;
    opacity: .2; /* left this here so you could see. Make it 0 */
    cursor: pointer;
    border: 1px solid red;
}

.upload-button:hover .upload-cover {
    background-color: #f06;
}

Solution 5

Any easy way to cover ALL file inputs is to just style your input[type=button] and drop this in globally to turn file inputs into buttons:

$(document).ready(function() {
    $("input[type=file]").each(function () {
        var thisInput$ = $(this);
        var newElement = $("<input type='button' value='Choose File' />");
        newElement.click(function() {
            thisInput$.click();
        });
        thisInput$.after(newElement);
        thisInput$.hide();
    });
});

Here's some sample button CSS that I got from http://cssdeck.com/labs/beautiful-flat-buttons:

input[type=button] {
  position: relative;
  vertical-align: top;
  width: 100%;
  height: 60px;
  padding: 0;
  font-size: 22px;
  color:white;
  text-align: center;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
  background: #454545;
  border: 0;
  border-bottom: 2px solid #2f2e2e;
  cursor: pointer;
  -webkit-box-shadow: inset 0 -2px #2f2e2e;
  box-shadow: inset 0 -2px #2f2e2e;
}
input[type=button]:active {
  top: 1px;
  outline: none;
  -webkit-box-shadow: none;
  box-shadow: none;
}
Share:
173,357

Related videos on Youtube

Joeytje50
Author by

Joeytje50

I am a Master student in Computer Science and hobbyist web developer, experienced in html css3 js regex and a beginner in svg php python c++. I like to think outside the box to find alternative uses for existing features, such as with my CSS3 mineturtle, or with this supposedly impossible question. I came up with CSS3 :checked-based tab menus independently in 2011 (back when IE8 was still by far the most used browser). A personal dislike of mine is jQuery UI's default styling being used so often without being customised. The visited item on your own userprofile that shows the dates you logged in, for example, just doesn't blend in with the other styles at all. &lt;/rant&gt;

Updated on April 23, 2020

Comments

  • Joeytje50
    Joeytje50 about 4 years

    I'm trying to style a file upload button to my personal preferences, but I couldn't find any really solid ways to do this without JS. I did find two other questions about this subject, but the answers there either involved JavaScript, or suggested Quirksmode's approach.

    My major issue with this Quirksmode's approach is that the file button will still have the browser-defined dimensions, so it won't automatically adjust to whatever's used as button that's placed below it. I've made some code, based on it, but it will just take up the space the file button would normally take up, so it won't at all fill the parent div like I want it to.

    HTML:

    <div class="myLabel">
        <input type="file"/>
        <span>My Label</span>
    </div>
    

    CSS:

    .myLabel {
        position: relative;
    }
    .myLabel input {
        position: absolute;
        z-index: 2;
        opacity: 0;
        width: 100%;
        height: 100%;
    }
    

    This fiddle demonstrates how this approach is quite flawed. In Chrome, clicking the !! below the second demo button will open the file dialog anyway, but also in all other browsers, the file button doesn't take up the correct areas of the button.

    Is there any more solid way to style the file upload button, without any JavaScript, and preferably using as little 'hacky' coding as possible (since hacking usually brings other problems along with it, such as the ones in the fiddle)?

    • regisbsb
      regisbsb over 9 years
      Use quirsmode but with a big font-size. see my answer.
    • Dan Dascalescu
      Dan Dascalescu almost 8 years
      Tympanus/codrops offers an excellent tutorial on styling file inputs while maintaining them accessible and navigable with the keyoard.
  • sheriffderek
    sheriffderek over 10 years
    Messed around with that a bit more here: codepen.io/sheriffderek/pen/JqlDB
  • Joeytje50
    Joeytje50 over 10 years
    Nice solution. Not really a compact solution, but still really nice. Just one tiny comment though: you should include a filter opacity for older IE versions (in which it won't look as good anyway, but then again, it's IE). Other than that, this is a really nice alternative.
  • sheriffderek
    sheriffderek over 10 years
    Good point. I don't support IE 8 or below anymore in my projects. But good to know for certain client stuff. The label solution seems to be better after all. I was using display:none; and then making the label inline-block or block with some radio buttons. Seems pretty awesome - My solution isn't super tiny, but - realistically - how many upload buttons would you have etc... I guess you could have a combo of the 2 with some conditions in a user-reset and cover all of your bases.
  • sheriffderek
    sheriffderek over 10 years
  • Joeytje50
    Joeytje50 over 10 years
    @sherriffderek oh I just love using radios for interactivity too. I've even made a whole MediaWiki extension based on just that. Sadly, on Android Browser it doesn't update that well, so you'd have to build in a fallback for that (if you wanna support the extra quarter of mobile users).
  • awe
    awe almost 10 years
    As to the issue about not having a button inside the label, I don't see this as a big issue. You can style any element to look like a button, even if it is not actually a button. The functionality when you click it is left to the input[type=file] anyway.
  • Joeytje50
    Joeytje50 almost 10 years
    Yeah, that's what I meant with the "you'll have to define the styles for the buttons yourself". You just add some styles to the <label> tag to make it look like a button.
  • Joeytje50
    Joeytje50 almost 10 years
    Also a very good solution. The only problem I can see with this is that technically, input elements don't have ::before and ::after pseudo-elements because they're void elements (they have no content). See this SO question for more info.
  • Derek Johnson
    Derek Johnson over 9 years
    This is brilliant. As for buttons inside label elements it's perfectly fine. They can contain any phrasing content (which includes input and button) as long as it's not labelled by something else or is another label. Source: html.spec.whatwg.org/multipage/forms.html#the-label-element
  • Joeytje50
    Joeytje50 over 9 years
    @DerekJohnson True, it is indeed possible and allowed to do that, but it won't have the desired effect (at least not in every browser). For example: this demo (at least in Chrome) won't focus the input when you click the button, but it will focus if you click the plain text. Since you want the label to open the file popup, you'll need to use something other than a <button>.
  • regisbsb
    regisbsb over 9 years
    it does not work on ie8 and ie7
  • Joeytje50
    Joeytje50 over 9 years
    @regisbsb That's probably because StackOverflow's snippet feature doesn't work in those browsers. I tested this code in IE7 once though, and it worked fine there. Please check jsfiddle.net/run4s/1 for a demo that does work in older IE versions.
  • regisbsb
    regisbsb over 9 years
    I tested in my solution and still does not work.
  • regisbsb
    regisbsb over 9 years
    does not work on ie8
  • Joeytje50
    Joeytje50 over 9 years
    @regisbsb I've just tested this jsfiddle in IE7&8 again, and indeed IE7 doesn't seem to work (anymore?), but IE8 works just fine. What is the code that doesn't work for you? PS: IE7 is used by only 0.08% of people, at the time of writing, so I wouldn't worry about that. Anyway, IE8 should still work just fine, so could you send me a link of a jsfiddle/jsbin/etc of the code that doesn't work in IE8?
  • Joeytje50
    Joeytje50 over 9 years
    I'm going to have to say this is not an answer to the original question, which explicitly mentions this approach by Quirksmode, which uses the same basic principle as your answer. But, if you want to use this, I'm not going to stop you of course, but then I'd recommend not using the <label> tag at all (but use a <div> instead, or something), since it doesn't add anything to this situation.
  • regisbsb
    regisbsb over 9 years
    He was complaing about that it won't resize, but now with font-size it will. Quote: "My major issue with this Quirksmode's approach is that the file button will still have the browser-defined dimensions, so it won't automatically adjust to whatever's used as button that's placed below it. I've made some code, based on it, but it will just take up the space the file button would normally take up, so it won't at all fill the parent div like I want it to."
  • sheriffderek
    sheriffderek over 9 years
    @regisbsb - It also doesn't work on Atari - or NES
  • ronkot
    ronkot over 9 years
    This amazing solution cut away lots of akward do-this-if-that-browser code from our codebase. Thanks!
  • Dylan Vester
    Dylan Vester over 9 years
    This works great for me, my only issue is that after a file is selected, it doesn't actually show that a file has been selected. Is there any way to show an indicator that a file has been chosen?
  • Joeytje50
    Joeytje50 over 9 years
    That's what's done with the :valid and :invalid code in the CSS. Alternatively you could change the text using something like this. You can't show what the selected file is exactly though, this way.
  • kaybee99
    kaybee99 over 8 years
    This causes completely different behaviour in IE and chrome. Isn't the idea is to make it browser agnostic?
  • Cullub
    Cullub over 8 years
    Also, it doesn't work in Firefox, at all, ever. Because Firefox is based on Gecko, not webkit.
  • Dan Dascalescu
    Dan Dascalescu almost 8 years
    display: none will remove the input from tab order. Using <label> (as shown by Tympanus) is semantic but needs some refinements.
  • basZero
    basZero about 7 years
    This solution with a large font has the disadvantage that if you have clickable elements near the file upload button, you can't click on them because the clickable area is covering the elements beneath... not a solution to me!
  • RP McMurphy
    RP McMurphy over 6 years
    Is there a way to show the link of the uploaded file beside as it does in the default? Or grab it using JS and showing it separately? That would make it a 100% working solution. Right now the user does not have any idea if the file has been added or not.
  • Joeytje50
    Joeytje50 over 6 years
    @RPMcMurphy yes it is possible to do this with JavaScript through reading the input's value property. Besides that, the user can know whether their file has been selected or not by the color of the input. It changes color (red->green) now once the file has been selected.
  • RP McMurphy
    RP McMurphy over 6 years
    Thanks much. Color change helps and I'll grab the value too. :)
  • Hassan Baig
    Hassan Baig about 6 years
    i) Why have both opacity:0 and filter: alpha(opacity=0)? It seems just opacity:0 is good enough to make the file element invisible. ii) Secondly, how about using font-size:1px instead of 100px? The img inside the label is de facto acting as the input element (so the clickable area would be dictated by that), so how about making the input element very small and invisible?
  • Hassan Baig
    Hassan Baig about 6 years
    This solution works with position:absolute as well. Why does that matter? It matters because position:absolute has better browser coverage than position:fixed.
  • regisbsb
    regisbsb about 6 years
    This was meant to work on IE7 and IE8
  • Naren Verma
    Naren Verma almost 6 years
    @Joeytje50, it's not displaying the file name