Last Updated: January 20, 2024
·
294.8K
· barneycarroll

Total input[type=file] style control with pure CSS

If you search for a solution to the common problem of how to get full appearance control over an <input type="file"/>, the result will probably fit into one of 3 categories:

  • Requires Javascript.
  • Doesn't work in a major browser.
  • Doesn't actually provide complete stylistic control.

That bill certainly fit every single answer I found on StackOverflow. But you can do this with pure CSS. It requires a single wrapping element to hook the styles onto (the input itself is hidden, since its styles are so limited/limiting). Semantics nazis will probably want to make this a <label/> — and by the way, there's nothing wrong with having multiple labels per element. Look at the spec. Now look at me. Yeah. Anyway:

CSS

.fileContainer {
    overflow: hidden;
    position: relative;
}

.fileContainer [type=file] {
    cursor: inherit;
    display: block;
    font-size: 999px;
    filter: alpha(opacity=0);
    min-height: 100%;
    min-width: 100%;
    opacity: 0;
    position: absolute;
    right: 0;
    text-align: right;
    top: 0;
}

HTML

<label class="fileContainer">
    Click here to trigger the file uploader!
    <input type="file"/>
</label>

TL;DR: Check out my fiddle for a demo and star my gist for future reference.

Is this buggy? Is there an even simpler way to do it? Leave a comment.

7 Responses
Add your response

This is great, I guess the only downside to this approach is there is no confirmation of the file the user has selected! Is there a way to achieve this as well? Thanks

over 1 year ago ·

Great solution! Thanks!

over 1 year ago ·

@jaydeesigns sorryfor leaving this reply late — writing this here for posterity in case anyone else faces the same problem.

Yup, one of the tradeoffs is you have to obscure whatever useful parts of the input are there too. It's worth noting that feedback on the selected file is not at all standardized, but you can extract whatever the browser will allow using the following Javascript (where fileInput is a reference to the file input):

fileInput.value;

If I'd selected the file at D:\some\thing\foo.bar the output would be:

Firefox: 'foo.bar',
Chrome:  'C:\fakepath\foo.bar',
IE:      'D:\some\thing\foo.bar'

If you wanted to standardise that, you can extract the only reliable bit (the file name) by applying a regular expression which strips out any sequence of characters followed by a slash:

fileInput.value.replace(/([^\\]*\\)*/,'');

In such a way, you could update the label text (for example), by using jQuery:

$( '.fileContainer [type=file]' ).on( 'click', function updateFileName( event ){
    var $input = $( this );

    setTimeout( function delayResolution(){
        $input.parent().text( $input.val().replace(/([^\\]*\\)*/,'') )
    }, 0 )
} );

Note I didn't use the change event above because IE will only fire that once the user has navigated (tabbed or clicked) away from the input after using it. Instead we listen for the click event (which will trigger on keyboard activation too), and use a setTimeout to make the function resolve as soon as possible (zero milliseconds): in this way, the user clicks, the dialogue has time to open, and Javascript only executes when the user has finished inputting.

over 1 year ago ·

Hi Barney,
This is the best solution I ever read.
However, only one problem left, the info of the file that you selected that usually appear on the right side of the input button. Using your method, I found that it would replaced since the original ones was hidden.

Regarding that case I would like to know how to solve it.

Many thanks,
Agung

over 1 year ago ·

.fileContainer {
position: relative;
}

.fileContainer [type=file] {
cursor: inherit;
display: block;
font-size: 16px;
min-height: 100%;
min-width: 100%;
position: absolute;
left: 0px;
text-align: right;
top: 7px;
z-index: -1;
outline: none;
}

.fileContainer [type=file]:focus {
outline: none;
}
/* Example stylistic flourishes */

.fileContainer {
background: lightgray;
border-radius: .5em;
float: left;
padding: .5em;
}

.fileContainer {
cursor: pointer;
}

over 1 year ago ·

instead of label how can we use another button like browse or upload

over 1 year ago ·

Nice!

11 months ago ·