File Upload

How the File Upload works with a keyboard and assistive tech, and where it stands against WCAG 2.2 AA.

AAWCAG 2.2 target
7/12contrast pairs passing
4keyboard interactions

The drop zone is a native <button> that opens a real <input type="file">, so dragging is never the only way to add a file. Each upload reports its progress as a progressbar, and every action on a file is a named button.

Keyboard#

Tab

Moves to the drop zone, then through each file's pause, resume, retry, cancel, delete and download buttons.

Enter

On the drop zone, opens the system file picker. On a file's button, runs that action.

Space

Does the same as Enter, on release.

Escape

Closes the system file picker without adding anything. This is the platform's own dialog.

Semantics#

<button>

A real button. Its text (Click to upload from device) and the size limit are its name and description.

Drop zone
<input type="file">

Hidden from the tab order and from assistive tech, because the button stands in for it. accept and multiple are passed through.

Always
aria-describedby

Links the allowed types, the size limit or the error, so they are read with the button.

Hint or error
role="progressbar"

Named for the file, with its percentage, and aria-valuetext that adds Paused or Failed.

Uploading
aria-label

Named for what they do. Name them for the file too where several are listed: Delete Q3 report.pdf.

File buttons
role="alert"

The reason a file failed is announced at once.

A failed file
aria-live="polite"

Adding and removing files is announced without interrupting.

List changes

Colour contrast#

Every variant and state has to meet WCAG 2.2 Level AA. Measure the colours as they land on screen: a translucent hover or press wash over whatever sits behind it.

4.5:1

Drop zone text · Rest, Hover, Drag over

The label in #2a2a2a and Upload in #005cf8 pass on every fill. The size-limit caption is #8f8f8f, 3.15:1 on #fcfcfc, and fails.

WCAG 1.4.3 Contrast (Minimum) ↗
3:1 if needed

Drop zone edge · Rest

The dashed #f2f2f2 edge is barely visible. The zone is a button with a readable label, so the label, not the edge, shows it can be used.

WCAG 1.4.11 Non-text Contrast ↗
3:1

Progress · Uploading

The #005cf8 bar against its track and the page. The percentage is also given as text beside it.

WCAG 1.4.11 Non-text Contrast ↗
4.5:1

Status text · Failed, Uploaded

Error text is #ec0d33 (4.49:1 on white) and drops further where it is set at 80% opacity. The green Uploaded badge is #229a14 on #cbfac2, 3.16:1. Both are under 4.5:1.

WCAG 1.4.3 Contrast (Minimum) ↗
3:1

Focus indicator · Focus visible

A 2px ring in #1a6ef8 off a white gap, on keyboard focus only. It measures 4.54:1 on white, so it clears 3:1 on every light surface the library uses.

WCAG 1.4.11 Non-text Contrast ↗WCAG 2.4.13 Focus Appearance ↗

Measured contrast#

The drop zone's text and dashed edge against its own fill in each state, from the ds.css tokens: #fcfcfc at rest, white on hover, #ebf2ff while a file is dragged over; text is held to 4.5:1 and the dashed edge to 3:1. 5 of 12 pairs fall below their minimum (4.5:1 for text, 3:1 for graphics) and are marked in amber.

Part · elementRestHoverDrag over
Drop zone · label13.99passes14.35passes12.77passes
Drop zone · Upload5.27passes5.40passes4.81passes
Drop zone · caption3.15below the minimum3.23below the minimum2.88below the minimum
Drop zone · edge1.09below the minimum1.31below the minimum4.81passes

Motion#

  • Progress bars and washes move over 180ms as the percentage changes, and the zone's fill over 150ms. All are turned off under prefers-reduced-motion.
  • The buffering spinner steps round continuously while a file starts; under reduced motion it stays still, and the progress text still says Starting.

Checklist#

State the allowed types and the size limit in the caption, not only in accept, and reject other files with a reason.

The caption (#8f8f8f, 3.15:1) and the error text (4.49:1, less at 80% opacity) fall under 4.5:1 and need darker shades.

Name file buttons for their file when several files are listed, so Delete is not read five times with nothing to tell them apart.

Don't announce every percentage. The progressbar holds the value for anyone who asks; announce the start, a failure and the finish.

After a file is deleted, move focus to the next file's row, or back to the drop zone when the list is empty.

Icon buttons are 40px and 44px, over the 24px minimum.