Search topics
File upload drop zones with no button to choose files
An expense form's receipts box takes files only by drag and drop, or by a click on the box itself, so nothing in it takes focus or is read as a button.
zone.addEventListener('drop', dropFiles); The problem
People who can't drag, and keyboard and screen reader users, can't attach a receipt, so the claim can't be sent.
An expense claim form asks for the amount, then the receipts. The receipts box is a dashed rectangle with an upload arrow and the words "Drag and drop receipts here". With a mouse, you drag a PDF out of a folder, let go over the box, and its name appears under it. Press Tab from the Amount field, and focus goes straight to Submit claim: nothing in the box takes focus, and there is no button to press. A screen reader reads "Drag and drop receipts here" as a line of text. On a phone, the receipt is in another app, so a drag means holding it while switching to the browser, where the phone allows that at all.
The same box often says "or click to choose files" as well, and a click anywhere on it opens the file picker. A mouse user never meets a problem. Tab still skips the box, Enter does nothing, and a screen reader reads its words as text, with no role to say that the box can be pressed.
The box is a div that a script listens to. In the first form, it listens only for a drop. Adding a file then needs a dragging movement: press on the file, carry it across, let go. WCAG 2.5.7 asks that anything done by dragging can also be done with single clicks or taps, and failure F108 is a function with no such way; here, there is none. A keyboard can't carry a file from a folder into the page either, and nothing in the box takes focus, so the receipts can't be added from the keyboard at all, which fails 2.1.1.
In the second form, the box also holds a file input hidden with the hidden attribute, and the script opens it with click() when the box is clicked. A single click now adds a file, so 2.5.7 passes, but the box that takes the click is still a div: it is not in the Tab order and ignores Enter and Space, which fails 2.1.1. It acts on a click, so it is a control, yet it exposes no role and no name; a screen reader reads its words as text and never says there is something to press, which fails 4.1.2. The file input that does the work is hidden from everyone, so it can't stand in.
An upload is often the step a claim, an application or a support request can't be sent without, so one box that only takes drops can stop the whole form. People who can drag, but slowly, meet a longer version of the same problem: the folder and the form have to be side by side on screen, and the drop has to land inside the box.
4 of 10 groups affected
- No vision (affected)
- Low vision (affected)
- Colour vision (not affected)
- No hearing (not affected)
- Hard of hearing (not affected)
- No speech (not affected)
- Motor (affected)
- Reach, strength (affected)
- Cognitive (not affected)
- Seizures (not affected)
- Limited manipulation Keyboard, switch and voice control users can't drag a file from a folder into the page, and Tab never stops on the box, so the receipt can't be attached.
- Without vision Screen reader users hear the box's words as plain text, with no button to press, and can't aim a drag at a box they can't see.
- Limited reach and strength People with tremors or weak hands lose hold of the file on the way, or let go short of the box, and have to start again from the folder.
- Limited vision With a magnifier, the folder and the box are rarely on screen together, so the file has to be carried across a view that scrolls.
A scanner sees a div, a paragraph and, in the second form, a file input hidden from everyone, which no rule asks for a label. The click and drop listeners are in the script, not the markup, so nothing says the box is a control. EaseWeb's audit sends a review lead for a box with a drop listener of its own, for the box around a file input hidden with display: none, visibility: hidden or the hidden attribute, and for a dashed or dotted box whose words ask for files to be dragged or dropped, whenever nothing in it takes focus and nothing beside it is a file input or a button to choose or upload files. Listeners that a framework puts on the root are not seen, and a button elsewhere on the page may open the same input, so a person checks each one. Asked to make the zone accessible, AI tools often add tabindex="0" to the box, as some upload libraries do. The box then takes focus, but with no role and no name a screen reader says little or nothing there, and Enter does nothing until a key handler is written too. The audit sends a review lead for such a box as well.
Try it
Attach a receipt by dragging a file onto the box. Then put the mouse aside and try again with Tab.
Broken 1: drag one of the files under the form, or a file from your computer, onto the box. Then Tab from Amount: focus jumps to Submit claim, and no click or key adds a file.
Broken 2: a click on the box now opens the file picker. Tab still passes it, and the panel never says there is anything to press.
Apply a fix. Tab stops on the box’s control, Enter or Space opens the file picker, and dropping still works.
Broken Can you fix this?
Drag and drop receipts here
Screen reader says:
(Tab through the form)
The fix
Keep the drop, and add a button that opens the file picker.
A box that takes only dropped files
<div class="field"> <span class="field-label" id="receipts-label">Receipts</span> <div class="drop-zone"> <p>Drag and drop receipts here</p> </div> <ul class="file-list"></ul></div>What is wrong3 points
- A drop is the only way in.
- Nothing in the box takes focus.
- No click or key adds a file.
A Choose files button that opens the hidden input
<div class="field"> <span class="field-label" id="receipts-label">Receipts</span> <div class="drop-zone"> <p>Drag and drop receipts here, or</p> <button type="button" class="choose" aria-describedby="receipts-label">Choose files</button> <input type="file" id="receipts" multiple hidden> </div> <ul class="file-list"></ul></div>How it is fixed3 steps
- Add a native Choose files button to the box. The browser puts it in the Tab order, activates it with Enter or Space, and gives it a role.
- Let its own text name it, and make the field's label its description, so it is read something like "Choose files, button, Receipts".
- Move the click that opens the input from the box to the button, and take
cursor: pointeroff the box. One click, tap or key press opens the file picker, and voice control users can say "Click Choose files".
This fixes both Befores and passes 2.1.1, 2.5.7 and 4.1.2. The input stays hidden with hidden: calling click() on it still opens the picker in current browsers, and the button does everything a person needs. The drop works as before.
Why this fix: Make a gesture a shortcut, never the only way
All three pass, and all three keep the drop. Use the button unless you have a reason not to: it is the only one whose words are the page's own in every browser, and whose focus ring is on the element that has focus, for one line of script. The file input itself needs no script and no hidden element, so it suits a plain form, or a page that can't run script before the form is used; but the browser writes its button's words and the "No file chosen" beside it in its own language, and that text has to be kept in step with the files that were dropped. The label over a hidden input also needs no script and looks exactly as designed, but the focus ring and the thing that takes focus are two elements tied by a CSS selector, so a change to the markup can lose the ring without anything else breaking.
Keep the drop. It is a quick way in for people who have the folder open beside the form, and the button makes it a shortcut rather than the only way. A click on the whole box can stay too, as a bigger target for the mouse, as long as the button is there; let the box's own click ignore clicks that start on the button, or the picker can be asked to open twice.
Several smaller decisions decide how the zone works once files are in it.
- Saying what was added: after a drop or after the picker closes, list the files in the zone and say so in a status region, such as "2 files added: taxi-receipt.pdf, hotel-invoice.pdf", as status messages that are shown but never announced explains. Focus returns to the button when the picker closes, so a screen reader user would otherwise have to go looking.
- Removing a file: give each listed file its own Remove button, named with the file ("Remove taxi-receipt.pdf"), and move focus to the next file or back to Choose files after a removal.
- Required uploads: the browser can't move focus to a file input hidden with
hiddento show its own message, sorequiredon it can stop the form with nothing shown. Check the files yourself, and put the message beside the button, tied to it, as form errors that don't say how to fix them shows. - Phones: the button opens the same input, so a phone still offers its own choices, such as the camera, the photo library or files.
accept="image/*,.pdf"narrows what can be picked; say the same types in the zone's words, so nobody learns them from an error. - Upload libraries: Dropzone.js makes the whole box clickable, and react-dropzone gives the box a Tab stop of its own; check what each one exposes, and add a real button if the box is all there is.
Design ideas
A place to drop files can take any shape, as long as it has a button.
Five ways to draw the place for receipts. Drag a file from your computer onto each, then put the mouse aside and use Tab: four have a button that opens the file picker, and one takes files only by a drop.
In the design file
- Draw the button that chooses files at rest, with its words, not only the words that ask for a drop.
- Draw the place at rest, and again with a file held over it.
- Draw the button with its focus ring, and the list of files once some are added, each with its own Remove button.
- Draw the phone layout, where nothing can be dragged in and the button is the only way to add a file.
Passes
- ReceiptsAttach button with file chips No box at all: a quiet outlined pill with a paperclip, and each file added becomes a chip with its own × to remove it. A file dropped anywhere on the field is taken too, so the drop stays as a shortcut and the form stays compact. The pill’s blue text is 6.09:1 on white.
Rule of thumb: Make a gesture a shortcut, never the only way
Drop receipts here or
“browse” as a button in the sentence A soft blue panel with no dashed edge, and one word in its sentence is the button: “browse”, underlined and bold in the site’s blue, 5.47:1 on the panel’s tint. Only the word takes the click and the Tab stop, so its focus ring hugs the word, and it needs a name that says what it chooses, such as “browse for receipts”.Rule of thumb: Make a gesture a shortcut, never the only way
- A drop target that appears only while a file is held At rest the card shows only its Add receipts button, and the drop area appears over the whole card only while a file is dragged onto it, with a blue edge and “Drop to attach”. The screen stays calm, the target is as large as it can be, and the button never moves.
Rule of thumb: Make a gesture a shortcut, never the only way
- Receipts
- taxi.pdf
- lunch.jpg
An Add receipt tile beside the ones added The receipts already added are small paper tiles, and the last tile is a dashed one with a plus and the words “Add receipt”. The tile is a button, so a tap or Enter opens the file picker, and a file dropped on it is taken as well. Keep the words: a plus on its own needs a name the eye can’t see.Rule of thumb: Make a gesture a shortcut, never the only way
Fails
- No receipts yetAn empty state that says “Drop receipts anywhere”WCAG 2.5.7 A friendly illustration, “No receipts yet” and “Drop receipts anywhere on this page”: the cleanest empty state of all, and the drop does work. But nothing on it can be pressed, so Tab finds nothing, a click does nothing, and on most phones there is no way to add a receipt at all.
Rule of thumb: Make a gesture a shortcut, never the only way
Verify the fix
5 checks.
Test with: Keyboard, Screen reader, Mouse, Touch, Speech input
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the upload component or its library changes.
How screen readers handle aria-describedby
Limits
This is a starting pattern, not a guarantee for every application. Moving cards and list items by dragging is covered in drag-and-drop boards that only a mouse can use, and asking what an uploaded photo shows in image uploads that never ask for alt text. A progress bar for a large upload has its own needs: see progress bars and meters that screen readers can't read. Pasting a file, or uploading a whole folder, is not covered here. The file picker belongs to the operating system, and its keys and screen reader support differ between systems: test the complete journey with your target assistive technology.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
References
- WCAG Understanding: dragging-movements (opens in a new tab)
- WCAG Technique F108 (opens in a new tab)
- WCAG Understanding: keyboard (opens in a new tab)
- WCAG Understanding: name-role-value (opens in a new tab)
- developer.mozilla.org: file (opens in a new tab)
- developer.mozilla.org: Using_files_from_web_applications (opens in a new tab)
EaseWeb service
Fixed it? Keep it from coming back.
I turn guides like this one into rules your coding agents follow and checks your CI runs. Then I retest by hand with a keyboard and screen readers, periodically.