If your website lets users upload files, you have probably compared the default file upload control with custom solutions. In this article I will show you can have both, using a method that uses the default input, but looks completely custom.
The default file upload
This is a default file upload:
<input type="file" id="file-upload" />
<label for="file-upload">Upload a file</label>
It is one of those HTML elements that has a lot of usability and accessibility built in.
To name a few things that you get for free when using the native <input type=file>
:
- it can show the file name of the upload file
- it can be used with just a keyboard
- on phones, it will let your user take a photo or choose to select files from specific apps
- on desktop browsers, it will let your user browse the file system
- it can be set to allow
multiple
files to be selected - it can distinguish between file types with its optional
accept
attribute - it exposes a useful
files
DOM attribute for usage in your scripts (as well as a slightly less usefulvalue
) - it integrates with your browser’s built in error mechanism
File input that matches your brand
Something you probably don’t get for free is a smooth integration with your brand’s design guidelines. They might prescribe buttons to be blue or rounded corners everywhere.
The HTML example above related the input with a corresponding label. I used corresponding for
and id
attributes, but wrapping the input
inside a label
works just as well.
In all browsers, you will find that you don’t actually have to click the input to activate it: clicking the label will do. Because of this, the trick, which I learned from Krijn, is simple: hide the <input/>
and style the <label>
.
Hide the input
You want to hide the input, but do that in a way that screenreaders don’t assume it does not exist. The method for hiding is to make it ‘visually’ hidden, but not actually hidden to assistive technologies.
This is one way:
input[type="file"] {
opacity: 0; /* make transparent */
z-index: -1; /* move under anything else */
position: absolute; /* don't let it take up space */
}
There are various different approaches to this, the important thing is that the method does not render the input unusable (as visibility: hidden
would, for example), or move it off screen (as left: -9999em
would), as that would move any native error messages off screen as well.
Style the label
Now, only the label is visible and you can style it however you like. You can make it look like a button, add background gradients, drop shadows, whatever your heart desires.
Add focus styles
It would be good to add the focus styles that your input had to the label. You can select this in CSS as follows:
input[type="file"]:focus + label {
outline: 2px solid; /* example focus style */
}
TL;DR
To make a custom file upload control, use a standard file upload and label, then visually hide the input and style the label.
Update: On Twitter, Phil pointed out that if you use the above example, users can’t see the file name of what they’ve uploaded. This is a great point; in my own implementation I’ve used some JavaScript to access the input’s files
property onchange
of the input, which I then inserted into the page if it had changed (I’ve created a Codepen to show how to do this)
Comments, likes & shares
No webmentions about this post yet! (Or I've broken my implementation)