- How to Customize File Inputs
- Create HTML
- Add CSS
- Example of customizing a file input:
- Result
- Example of styling a file input:
- Styling file inputs like a boss
- Styling File Inputs
- Hiding the input
- Styling the label
- Accessibility
- Javascript Enhancements
- Styling & Customizing File Inputs the Smart Way
- Accessibility
- Keyboard Navigation
- Possible Touch Issues
- JavaScript Enhancement
- What if JavaScript is not available?
- Firefox Bug
How to Customize File Inputs
In this snippet, we’re going to show how you can customize a file input without JavaScript.
Create HTML
label class="label"> input type="file" required/> span>Select a file span> label>
Add CSS
- Use the position and top properties for the label.label input[type=»file»].
- Style the «label» class using the cursor, border, border-radius, padding, margin, and background properties, and add display.
- Add the :hover and :active pseudo-classes to the «label» class and add background.
- Add the :invalid and :valid pseudo-classes with the span to the «label» class and set color.
label.label input[type="file"] < position: absolute; top: -1000px; > .label < cursor: pointer; border: 1px solid #cccccc; border-radius: 5px; padding: 5px 15px; margin: 5px; background: #dddddd; display: inline-block; > .label:hover < background: #5cbd95; > .label:active < background: #9fa1a0; > .label:invalid+span < color: #000000; > .label:valid+span < color: #ffffff; >
Example of customizing a file input:
html> html> head> title>Title of the document title> style> label.label input[type="file"] < position: absolute; top: -1000px; > .label < cursor: pointer; border: 1px solid #cccccc; border-radius: 5px; padding: 5px 15px; margin: 5px; background: #dddddd; display: inline-block; > .label:hover < background: #5cbd95; > .label:active < background: #9fa1a0; > .label:invalid + span < color: #000000; > .label:valid + span < color: #ffffff; > style> head> body> form action="/form/sumbit" method="get"> label class="label"> input type="file" required/> span>Select a file span> label> form> body> html>
Result
In this example, first, we hide the input which does not take up space in the document layout but still exists and can be activated with the label.
The CSS possibilities of an input file are limitless, so you can use several CSS properties once you understand how this method works.
Now, let’s see one another example, where we use the opacity, position, and z-index properties on the «upload».
Example of styling a file input:
html> html> head> title>Title of the document title> style> label < cursor: pointer; background-color: lightblue; color: #ffffff; padding: 10px 20px; > #upload < opacity: 0; position: absolute; z-index: -1; > style> head> body> form action="/form/sumbit" method="get"> label for="upload">Upload a file label> input type="file" name="photo" id="upload" /> form> body> html>
Styling file inputs like a boss
HTML elements have default styles applied to them by individual browsers. These styles could vary depending on the browser, and applying your own custom styles could range from being very easy to unnecessarily complicated (sometimes impossible). In this article we would be going through how to style file inputs, which happens to be one of those difficult elements to style using CSS. If you’ve worked with file inputs in your application, you’d know that the default style doesn’t look so nice. Here’s an example of how it looks in chrome and firefox in case you’re wondering. The good news is, we can fix this. the bad news is we won’t be able to use the «conventional» method.
Styling File Inputs
class="file-input"> type="file" id="file" class="file"> for="file">Select file
Hiding the input
.file opacity: 0; width: 0.1px; height: 0.1px; position: absolute; >
You might be thinking, why not use display: none to hide the input. Well the problem with that is the input field would be removed from the layout and then become inaccessible to people using screen readers which would be very bad. Another important thing to note is the label . With file inputs, clicking on the label also opens up the file picker, so we can use that to our advantage and style the label anyway we want.
Styling the label
Now that we’ve hidden the default input, we can decide to style the label however we want. For simplicity sake, let’s just make it look like a button.
.file-input label display: block; position: relative; width: 200px; height: 50px; border-radius: 25px; background: linear-gradient(40deg, #ff6ec4, #7873f5); box-shadow: 0 4px 7px rgba(0, 0, 0, 0.4); display: flex; align-items: center; justify-content: center; color: #fff; font-weight: bold; cursor: pointer; transition: transform .2s ease-out; >
Accessibility
So we’ve been able to style the label to look like a button, but that’s not all. We need to add some sort of indications to the label for when people hover on the field, or try to focus on the file field using the keyboard. I’m just going to do something simple here and increase the size of the label when that happens.
input:hover + label, input:focus + label transform: scale(1.02); >
input:focus + label outline: 1px solid #000; outline: -webkit-focus-ring-color auto 2px; >
The -webkit-focus-ring-color is used to get the default outline look on webkit browsers like chrome or safari. For non webkit browsers, a black outline would be applied to the element.
Javascript Enhancements
We can use JavaScript to display the name and size of the file selected. This would create a slightly more natural feel to the custom field. So let’s modify our HTML and CSS a bit.
class="file-input"> type="file" id="file" class="file"> for="file"> Select file class="file-name">
.file-name position: absolute; bottom: -35px; left: 10px; font-size: 0.85rem; color: #555; >
Finally, we would add an event listener to the file input and reflect the name and size of the file below the label.
const file = document.querySelector('#file'); file.addEventListener('change', (e) => // Get the selected file const [file] = e.target.files; // Get the file name and size const name: fileName, size > = file; // Convert size in bytes to kilo bytes const fileSize = (size / 1000).toFixed(2); // Set the text content const fileNameAndSize = `$fileName> - $fileSize>KB`; document.querySelector('.file-name').textContent = fileNameAndSize; >);
Here’s how everything looks.
And that’s it for the file input. You could decide to style this however you want to get the behavior you want, it’s up to you. Happy styling!😁😁
Styling & Customizing File Inputs the Smart Way
A tutorial on how to style and customize in a semantic, accessible way using the element and some JavaScript.
There are quite a few techniques for “customizing” the element. I tried most of them, but none was good enough to have on Readerrr (for importing feeds by uploading a file). Probably the worst technique was the one where the input element is put into a container (which imitates a button), and the input follows the cursor so that when you click anywhere on the container, you actually click the input. Sounds interesting and weird at the same time, right? Anyway, it had some unacceptable drawbacks (usability, touch).
As as result, I tried googling for an unseen solution. Once it seemed that there was nothing new, my I eyes were caught by a comment on StackOverflow. It had just a few up votes and was lost somewhere in the middle of the page, but most importantly it contained a magic word – ! As you may know, pressing a label basically triggers the focus event for the bound input. Interesting thing is that, if it is a file input, it works out as a click event, resulting in opening a file browser. This is great for crafting a semantic solution.
So, pressing any of these two elements gives us the same result. That means that the most difficult part is… solved! No JavaScript, no other complex solutions like cursor position tracking, just these two lines. See for yourself:
Now let’s just style it and make this look like a normal button.
Hiding the
First off, we need to hide the ugly duckling. CSS properties such as display: none or visibility: hidden will not work out. The reasons are: the input value will not be sent to the server on form submit; the input will be excluded out of tab order (you want your website to be accessible, right?). I set up a combination of CSS properties/values for hiding the input visually but keeping it visible for the browser:
I see you are wondering why width and height are set to 0.1px instead of just 0px . Setting the property values to zero ends up throwing the element out of tab party in some browsers. And position: absolute guarantees the element does not interfere with the sibling elements.
Styling the
Since the element is visually the button, you can use all of your creative CSS juices on it. I’m sticking to something very simple for now:
.inputfile + label < font-size: 1.25em; font-weight: 700; color: white; background-color: black; display: inline-block; >.inputfile:focus + label, .inputfile + label:hover
Accessibility
How do you know that an element on the website is pressable? Firstly, the element should communicate a feeling that you can tap or click on it. Secondly, the cursor icon should change to an appropriate one when hovering the element. The former we’ve solved previously, let’s solve the latter, because labels do not trigger a cursor change by default:
Keyboard Navigation
If users are unable to navigate on your website using just a keyboard, you are doing something wrong. Hiding the input itself in a correct manner was one thing, the other is indicating when the element is focused, i.e. rendering .inputfile:focus on the label :
-webkit-focus-ring-color auto 5px is a little trick for obtaining default outline looks on Chrome, Opera and Safari. The style in the line above is for browsers that do not understand the -webkit… expression.
Possible Touch Issues
In case you’ve been using FastClick (a library for eliminating the 300ms tap-pause on touch-capable devices) and have plans to add some extra markup to the content of a label, the button won’t work as it should, unless you use pointer-events: none , respectively:
JavaScript Enhancement
Probably and hopefully the last thing missing is indicating if files were selected. The file input does usually indicate that, but in our case the input is visually hidden. Luckily, there is a way out: a tiny JavaScript enhancement. The text of a label becomes the name of the selected file. If there were multiple files selected, the text will tell us how many of them were selected.
var inputs = document.querySelectorAll( '.inputfile' ); Array.prototype.forEach.call( inputs, function( input ) < var label = input.nextElementSibling, labelVal = label.innerHTML; input.addEventListener( 'change', function( e ) < var fileName = ''; if( this.files && this.files.length >1 ) fileName = ( this.getAttribute( 'data-multiple-caption' ) || '' ).replace( '', this.files.length ); else fileName = e.target.value.split( '\' ).pop(); if( fileName ) label.querySelector( 'span' ).innerHTML = fileName; else label.innerHTML = labelVal; >); >);
There is also a jQuery version of this code presented in the source of the demo files. Make sure to check them out.
- Having the native [multiple] attribute allows users to select more than one file per upload. Whereas [data-multiple-caption] is a fictive attribute for expressing the message if multiple files were selected. Here you can set a custom message. The use of the phrase is optional and the fragment is replaced with the number of files selected. The reason I use an additional HTML attribute instead of assigning this sentence as a value for a JavaScript variable is because it’s much easier to maintain the copy when it is in one place.
- HTML attribute [multiple] is not supported in IE 9 and below and neither is the files property of JavaScript. For the latter case, we simply rely on value . Since it usually has a value of C:fakepathfilename.jpg format, the split( ‘\’ ).pop() extracts what’s actual – the name of the file.
- An interesting thing is that you can unset a value of the input by pressing the ESC button while in the file browser. This is possible only in Chrome and Opera. Therefore, we use labelVal for storing the default value of the label and bringing it back when necessary.
This is how the final result looks like:
What if JavaScript is not available?
Since there is no JavaScript-less way to indicate if any files were selected, it would be better to rely on the default looks of the file input for the sake of usability. All we need to do is to add a .no-js class name to the element and then use JavaScript and replace it with .js – that’s how we will know if JavaScript is available.
.js .inputfile < width: 0.1px; height: 0.1px; opacity: 0; overflow: hidden; position: absolute; z-index: -1; >.no-js .inputfile + label
Firefox Bug
It is quite unexpected that Firefox completely ignores the input[type=»file»]:focus expression, whereas :hover and :active work just fine! Surprisingly, Firefox allows to catch the focus event in JavaScript, so the workaround is adding a class to the file input element that let’s us control the focus style:
input.addEventListener( 'focus', function()< input.classList.add( 'has-focus' ); >); input.addEventListener( 'blur', function()< input.classList.remove( 'has-focus' ); >);
.inputfile:focus + label, .inputfile.has-focus + label
Check out the example styles in the demo to see how to style the file input element according to your needs. Make sure to take a look at the source code of the demo and feel free to use this technique in your projects. Happy uploading!