Styling file inputs like a boss

Abdulqudus Abubakre - Aug 21 '20 - - Dev Community

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.

Default file inputs in firefox and chrome

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

Let's create a simple file input in our HTML.



<div class="file-input">
  <input type="file" id="file" class="file">
  <label for="file">Select file</label>
</div>


Enter fullscreen mode Exit fullscreen mode

To style a file input, the first thing we would need to do is get rid of the default file input.

Hiding the input



.file {
  opacity: 0;
  width: 0.1px;
  height: 0.1px;
  position: absolute;
}


Enter fullscreen mode Exit fullscreen mode

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;
}


Enter fullscreen mode Exit fullscreen mode

Styled input with purple gradient

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);
}


Enter fullscreen mode Exit fullscreen mode

We can also decide to add an outline to the label on focus



input:focus + label {
  outline: 1px solid #000;
  outline: -webkit-focus-ring-color auto 2px;
}


Enter fullscreen mode Exit fullscreen mode

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.



<div class="file-input">
  <input type="file" id="file" class="file">
  <label for="file">
    Select file
    <p class="file-name"></p>
  </label>
</div>


Enter fullscreen mode Exit fullscreen mode


.file-name {
  position: absolute;
  bottom: -35px;
  left: 10px;
  font-size: 0.85rem;
  color: #555;
}


Enter fullscreen mode Exit fullscreen mode

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;
});


Enter fullscreen mode Exit fullscreen mode

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!😁😁

. . . . . . . . . . . . . . . . . .