abaabil

File

11th lightest of the twenty-three, by js weight

A real <input type="file">, styled through ::file-selector-button rather than replaced.

Example (a11y tier)

PDF or PNG, under 5 MB.
A square image works best.

Import

import File from 'abaabil/file'         // structure only
import File from 'abaabil/file/styled'  // + CSS
import File from 'abaabil/file/a11y'    // + label/description/error

Not a button with a hidden input

The usual way to build this is a styled <button> with a visually hidden <input type="file"> behind it. That pattern has to reimplement three things the real control already does: associating the label, activating from the keyboard, and announcing which file was chosen. Most implementations manage one of the three, and the missing one is usually the announcement, which is invisible to the person who built it.

It is also unnecessary, because ::file-selector-button is a real pseudo-element: the browser's own button can be styled directly. That is what this component does.

accept is a filter, not an announcement

accept narrows what the file dialog shows. It is never read out, and it does not apply at all to a file dropped onto the control. So on its own it tells the user nothing about what you will take, and the first they learn of it is a rejection.

Say it in words as well, in description, which is wired through aria-describedby so it is read with the field rather than sitting near it. The a11y tier warns in development when accept is set without one.

Props

PropTypeDefaultTierDescription
classNamestringallMerged with the base class.
labelstringa11yRendered as a real <label>.
hideLabelbooleanfalsea11y
descriptionstringa11yWired into aria-describedby. Say what you accept here.
errorstringa11ySets aria-invalid and joins aria-describedby.
...propsallaccept, multiple, onChange and the rest land on the input.