Skip to main content

File Upload

Enables users to attach one or more files from their device via drag-and-drop or browse. Use in forms or workflows that require document or media attachment. Don't use without specifying accepted file types and size limits in the helper text.

OverviewStyleAccessibility

Description

Anatomy

PartRequired?Notes
Drop zone container Required The bordered, interactive region users can drag files onto or click to trigger the file browser. Communicates affordance through dashed border styling and a label.
Upload icon Recommended A wa-icon inside the drop zone that reinforces the drag-and-drop affordance. Use the 'cloud-arrow-up' icon from the system icon set.
Primary label Required Short instruction text inside the drop zone, e.g. 'Drag and drop files here, or browse'. Should be concise and action-oriented.
Browse link Required An inline interactive element styled as a link inside the label that triggers the native file picker. Must be keyboard-focusable and labelled for screen readers.
Helper text Required Displayed beneath the drop zone. Must specify accepted file types and maximum file size, e.g. 'Accepted formats: PDF, DOCX, JPG. Maximum 10 MB per file.'
File list Recommended A list of attached files rendered below the drop zone after selection. Each item shows the file name, file size, upload status, and a remove button.
File list item — name Required Truncated file name shown in primary text colour. Full name available via tooltip or wrapping on smaller viewports.
File list item — size Recommended Human-readable file size (e.g. '2.4 MB') rendered in secondary text colour.
File list item — status indicator Recommended Shows upload progress (wa-progress-bar), success (wa-icon check with --op-color-status-success), or error (wa-icon with --op-color-status-error) state per file.
File list item — remove button Required An icon-only button that removes the file from the selection. Must carry an accessible label identifying the file, e.g. 'Remove quarterly-report.pdf'.
Error message Conditional Inline error message rendered when a file fails validation (wrong type, too large). Uses --op-color-status-error tokens.

Variants

States

State Behaviour
Default (empty) Drop zone displays at rest with dashed --op-color-border-default border, upload icon, and label. No files are listed.
Drag-over (active) When a file is dragged over the drop zone, the border colour transitions to --op-color-interactive-default and the background shifts to --op-color-interactive-default at approximately 8% opacity, signalling the zone is ready to accept the drop.
Focused When the drop zone or browse link receives keyboard focus, a 3px focus ring using --op-color-interactive-focus is rendered. The drop zone can be activated with Enter or Space.
Uploading Each file item in the list shows a wa-progress-bar beneath the file name. The remove button remains active so users can cancel mid-upload.
Upload success The progress bar is replaced by a success icon (--op-color-status-success) and an 'Uploaded' label. The remove button remains to allow de-selection before form submission.
File error If a file fails type or size validation, the file list item renders the file name in --op-color-status-error, an error icon, and a short inline error message. The file is not queued for upload.
Disabled The drop zone and browse link are non-interactive. Border and text use --op-color-text-disabled. Helper text should explain why the field is disabled.

Usage guidelines

Do / Don't

Do

Always state accepted file types and the maximum size limit in the helper text before the user interacts with the field.

Don't

Don't display format restrictions only after a failed upload attempt — this creates unnecessary friction and can prevent submission.

Do

Provide a per-file error message in the file list when validation fails, naming the specific file and the reason for rejection.

Don't

Don't use a single generic error for all files — users with mixed valid/invalid selections need to know which files failed and why.

Do

Keep the remove button accessible and labelled with the file name so screen reader users can identify which file they are removing.

Don't

Don't use a generic 'Remove' label for all remove buttons — this creates ambiguity for assistive technology users.

Do

Allow users to add additional files after an initial selection in multi-file mode.

Don't

Don't replace the entire file list when a user opens the file picker again in multi-file mode — append new selections instead.

Do

Confirm upload success per file with a visible status indicator pairing icon and text.

Don't

Don't silently succeed — users need confirmation that their attachment was received, especially in government submission contexts where evidence of lodgement matters.

Layout & Spacing

Drop zone container: padding: --op-space-24 (24px) vertical, --op-space-32 (32px) horizontal border: 2px dashed --op-color-border-default border-radius: --op-radius-md (8px) min-height: 120px display: flex, flex-direction: column, align-items: center, justify-content: center gap: --op-space-8 (8px) between icon, label, and helper text

Drop zone — drag-over state: border-color: --op-color-interactive-default background-color: --op-color-interactive-default at 8% opacity

File list: margin-top: --op-space-16 (16px) gap between items: --op-space-8 (8px)

File list item: padding: --op-space-12 (12px) --op-space-16 (16px) border: 1px solid --op-color-border-default border-radius: --op-radius-md (8px) display: flex, align-items: center, gap: --op-space-12 (12px) min-height: 52px

Remove button touch target: minimum 44x44px

Helper text: margin-top: --op-space-8 (8px) font-size: --op-text-sm

Tokens

PartTokenValue
Drop zone border (default) --op-color-border-default Dashed border at rest.
Drop zone border (drag-over / focus) --op-color-interactive-default Green-400; signals active drop target or keyboard focus.
Drop zone background (drag-over) --op-color-interactive-default Applied at approximately 8% opacity as a tint.
Primary label text --op-color-text-primary Main instruction text inside the drop zone.
Browse link --op-color-interactive-default Inline link colour matching the system interactive style.
Helper text --op-color-text-secondary Subdued colour for file type and size guidance.
File list item border --op-color-border-default Standard border for each listed file.
File name text --op-color-text-primary Primary text for the file name.
File size text --op-color-text-secondary Secondary text for the human-readable file size.
Success icon --op-color-status-success Confirms successful upload per file.
Error icon / error file name --op-color-status-error Signals validation failure for a specific file.
Error message text --op-color-status-error Inline error text beneath a rejected file item.
Error state border --op-color-status-error Replaces --op-color-border-default on file list items with validation errors.
Disabled state (border, text) --op-color-text-disabled Applied to drop zone and label when the field is disabled.
Focus ring --op-color-interactive-focus 3px outline on the drop zone or browse link when focused.
Upload icon --op-color-text-secondary Decorative icon inside the drop zone at rest.

Engineering notes

  • The drop zone must be a focusable element. Use a native <label> wrapping a visually-hidden <input type='file'> for the simplest accessible implementation — the label activates the input on click, Enter, and Space automatically.
  • The hidden <input type='file'> must carry the accept attribute to enforce permitted MIME types at the browser level, e.g. accept='.pdf,.docx,image/*'. Treat this as a first-line filter only — always validate file type and size on the server.
  • For multi-file support, add the multiple attribute to the <input type='file'>. When the user re-opens the picker, merge new selections into the existing file list rather than replacing it.
  • Announce upload status changes to screen readers via an aria-live='polite' region. Announce when a file is added ('quarterly-report.pdf added, uploading'), when upload completes ('quarterly-report.pdf uploaded successfully'), and on failure ('quarterly-report.pdf could not be uploaded — file exceeds 10 MB limit').
  • Each remove button must have an aria-label that includes the file name, e.g. aria-label='Remove quarterly-report.pdf'. Do not rely on icon-only buttons without accessible labels.
  • Drag-and-drop is a pointer-only enhancement. Keyboard and touch users must always be able to reach the browse trigger without dragging.
  • For determinate upload progress, bind wa-progress-bar's value attribute to upload percentage (0–100). For indeterminate state, omit the value attribute.
  • Perform client-side size validation using File.size before any network request to give immediate feedback without a round-trip.
  • Sanitise file names before rendering them in the DOM. Treat file.name as untrusted user input to prevent XSS.
  • In government submission contexts, consider generating and displaying a unique reference number or receipt token once all files are confirmed uploaded, to give users evidence of lodgement.

Keyboard interaction

KeyAction
Tab Moves focus to the drop zone or browse link. Focus ring is visible at 3px using --op-color-interactive-focus.
Enter Activates the drop zone or browse link, opening the system file picker.
Space Activates the drop zone or browse link when focused, opening the system file picker.
Tab (within file list) Moves focus through each file's remove button in document order.
Enter / Space (on remove button) Removes the associated file from the selection and announces the removal via the aria-live region.
Escape Closes the system file picker if open (browser-native behaviour).

Why it matters

Government forms are frequently completed by people using assistive technology, including screen readers and keyboard-only navigation. Submissions that require evidence — documents, images — must be fully operable without a mouse. Inaccessible file upload is a common WCAG failure point that can prevent citizens from participating in consultations or lodging required information with government agencies.

Focus

The drop zone container should have a visible focus ring (3px solid --op-color-interactive-focus) when focused via Tab. If the browse link is a separate focusable element, it should also carry its own focus ring. The focus order through the component is: drop zone or browse link, then each file list remove button in the order files were added. After a file is removed, focus should move to the next file's remove button if one exists, or back to the drop zone if the list is now empty — focus must not be lost.

ARIA

Role or attributeWhen to useExample
aria-label (drop zone) Provides a descriptive label for the drop zone when the visual label text alone is insufficient. aria-label="Attach supporting documents — drag and drop or press Enter to browse"
aria-live="polite" Applied to a visually-hidden status region that announces file additions, upload progress milestones, success, and errors without interrupting the user. <div aria-live="polite" aria-atomic="false" class="sr-only" id="upload-status"></div>
aria-label (remove button) Each remove button must name the file it acts on so screen reader users know which file will be removed. <button aria-label="Remove quarterly-report.pdf" type="button">...</button>
aria-describedby Links the drop zone to the helper text element so screen readers read type and size constraints when the field is focused. <label for="file-input" aria-describedby="upload-helper-text">...
aria-invalid Applied to the drop zone or form field wrapper when a file validation error is present. aria-invalid="true"
aria-errormessage Points to the error message element when aria-invalid is true. aria-errormessage="upload-error-msg"
accept (input attribute) Informs the browser and assistive technology which file types are permitted. Does not replace server-side validation. accept=".pdf,.docx,image/*"

Contrast

Drop zone label text (--op-color-text-primary on --op-color-bg-primary): must meet 4.5:1 for normal text. Helper text (--op-color-text-secondary on --op-color-bg-primary): must meet 4.5:1; verify at the token level — secondary text is a common contrast failure at small sizes. Browse link (--op-color-interactive-default / green-400 on --op-color-bg-primary): verify ratio meets 4.5:1. If it does not, supplement with underline decoration so colour is not the sole differentiator. Error message text (--op-color-status-error on --op-color-bg-primary): must meet 4.5:1. Success and error icons must not be the sole indicator of state — always pair icon with a visible text label or status message.

Touch targets

The drop zone is inherently large (min 120px tall). The browse link and each file list remove button must individually meet the 44x44px minimum touch target. If the remove button icon is visually 20px, pad it to 44x44px using padding or a pseudo-element without reducing the visual density of the file list.

Things to avoid

  • Do not rely solely on colour to communicate upload success or error — always pair status colours with an icon and text label.
  • Do not move focus unpredictably when files are added or removed. Focus should remain on the triggering element unless that element is removed from the DOM.
  • Do not disable the remove button during upload without a visible and screen-reader-accessible explanation — users may need to cancel an upload in progress.
  • Do not allow the aria-live region to announce on every byte of upload progress — throttle announcements to key status transitions (started, completed, failed).
  • Do not use placeholder-style text inside the drop zone that disappears on interaction without an associated persistent label.

Was this page helpful?

Updated 2 October 2026