Styling file inputs like a boss
Tags: html, css, javascript
Published: 2020-08-21 15:24:38

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](https://res.cloudinary.com/ibnabubakre/image/upload/v1597940843/Group_3.png)

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.
```html
<div class="file-input">
  <input type="file" id="file" class="file">
  <label for="file">Select file</label>
</div>
```
To style a file input, the first thing we would need to do is get rid of the default file input.
### Hiding the input
```css
.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.

```css
.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;
}
```
![Styled input with purple gradient](https://res.cloudinary.com/ibnabubakre/image/upload/v1597953312/Rectangle_9_1.png)

### 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.

```css
input:hover + label,
input:focus + label {
  transform: scale(1.02);
}
```

We can also decide to add an outline to the label on focus
```css
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.

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

```css
.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.
```javascript
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.
{% codepen https://codepen.io/abdulqudus001/pen/rNORJYo %}

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