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.
FigmaDescription
Allows users to select and attach one or more files from their device, either by dragging files into a drop zone or by clicking to open the system file browser. Displays a list of selected files with their names, sizes, and removal options before final form submission.
Consultation submission forms (attaching supporting documents, images, or reports), stakeholder record management (uploading contact lists or organisation documents), and community engagement workflows where participants submit evidence or media alongside a response.
Government engagement processes frequently require supporting documentation — plans, PDFs, images, spreadsheets — alongside a form response. File upload gives teams a consistent, accessible pattern for collecting attachments without requiring third-party integrations or custom implementations.
Anatomy
| Part | Required? | 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
Single file
Restricts selection to one file at a time. Replacing a selection swaps the current file.
When the form expects exactly one document, such as a signed consent form or a profile image upload.
Multi-file
Allows multiple files to be selected and listed individually.
When users need to attach several supporting documents at once, such as images of a site, a plan PDF, and a covering letter for a consultation submission.
Image-only
Visually emphasises image upload with an optional thumbnail preview replacing the generic file-name row.
Community engagement submissions asking for photos of a location, event, or issue in Social Point.
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
When to use
- A form submission requires supporting documentation, such as attaching a consultation submission document alongside a structured form response.
- Stakeholder management workflows where an admin uploads a CSV of contacts to bulk-import into Open Point.
- Community engagement submissions where participants are invited to share photos or evidence in Social Point.
- Any workflow where file size and type constraints need to be enforced before submission reaches the server.
When not to use
- Do not use file upload as the sole input for a response — always pair it with other form fields so users without a file to attach can still submit. Make the attachment optional where possible.
- Do not use if the platform or storage backend cannot handle the expected file types or sizes — instead display a clear message directing users to submit files via another channel.
- Do not use for inline rich-text content embedding; use the rich-text editor's built-in media insert instead.
- Do not use when a URL link to an external document is sufficient — use a plain text Input with URL validation instead.
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
| Part | Token | Value |
|---|---|---|
| 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
- The hidden 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 . 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
| Key | Action |
|---|---|
| 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 attribute | When to use | Example |
|---|---|---|
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.