Updated
File Upload
File Upload is where a person adds a photo or a document. After that it reports progress, success and failure.
Updated
Preview
A drop zone for photos and documents: Default invites the first tap and offers camera, photo library and files as sources, and Active marks the press or drag-over. Press it to run the whole cycle.
The cycle runs Default, Active, Uploading, Upload in that order, and a press during the run starts over.
The set draws two shapes: the drop zone stays until a file is chosen, then the component shrinks to a file row, so a finished upload takes less room than the invitation to add one. The progress bar, the file name and a round action, Cancel, Remove or Retry, live in that row.
The supporting text and the meta line are fg/soft-500, #8E8E93, which measures 3.26 to 1 on white and 2.99 to 1 on the bg/weak-50 zone, under the 4.5 to 1 that WCAG asks of small text.
In code, make the whole zone one button with a visible focus ring that opens the system picker. The zone is 361 by 212, far above the minimum touch target, while the source pills and the round actions are 36 and need a larger hit area.
Source: the File Upload set of the Appetite UI Figma file, read on October 2, 2026.
Which states does File Upload have?
Six states in two shapes: Default, Active and Disabled are the drop zone, and Uploading, Upload and Error are the file row. The rows differ only in what the second line says and which action sits at the end.
Default
Active
Disabled
Uploading
Upload
Error
The three zone states share one shape and change the paint. Active fills the zone with brand at 10 percent alpha, draws the dashed edge in brand and turns the featured circle brand with a white icon.
Disabled keeps the dashed edge on border/soft-100 and moves every ink to fg/disabled-400.
The row states share a second shape. Uploading shows the file name, a meta line with the amount sent and a 4 high progress bar.
Upload swaps the bar for a green check and the word Uploaded, and Remove replaces Cancel. Error moves the stroke, the file tile and the meta line to the error colors, keeps the name, and offers Retry.
Which properties does File Upload have?
One variant axis and three text properties. The zone reads Label and Supporting Text, the row reads File Name.
| Property | Type | Default |
|---|---|---|
State | Default, Active, Uploading, Error, Disabled, Upload | Default |
Label | Text, on the drop zone | Upload a file |
Supporting Text | Text, on the drop zone | PNG, JPG or PDF, up to 10 MB |
File Name | Text, on the file row | brief.pdf |
| State | Shape | Fill | Edge | Leading and meta |
|---|---|---|---|---|
| Default | Zone 361 x 212 | bg/weak-50 | dashed border/sub-200 | cloud-upload in brand/base on a white circle, three source pills |
| Active | Zone 361 x 212 | brand/alpha-10 | dashed brand/base | the same, on a brand circle with a white icon |
| Disabled | Zone 361 x 212 | bg/weak-50 | dashed border/soft-100 | every ink fg/disabled-400 |
| Uploading | Row 361 x 72 | bg/white-0 | solid border/soft-100 | file tile, name, amount sent, progress bar, Cancel |
| Upload | Row 361 x 72 | bg/white-0 | solid border/soft-100 | file tile, name, green check and Uploaded, Remove |
| Error | Row 361 x 72 | bg/white-0 | solid state/error/light-200 | error tile, name, the reason in state/error/base-500, Retry |
The meta line, the source pills and the actions are layers inside the set and carry no property; the pills are Button instances at Neutral, Outline, sm. Change the meta text on the instance, and show or hide a source by editing the Sources row.
Tokens
Every value the component reads, resolved through its aliases in Light and Dark.
| Token | Light | Dark | Applied to |
|---|---|---|---|
bg/weak-50 | #F4F5F5 | #242427 | Zone fill and the file tile |
bg/white-0 | #FFFFFF | #0E0E0F | Row fill, the featured circle at rest and the source pills |
border/sub-200 | #D1D1D6 | #48484A | Dashed edge at rest |
border/soft-100 | #E9E9EA | #2F2F31 | Row stroke, pill stroke and the Disabled dashed edge |
brand/base | #155DFC | #2B7FFF | Active edge and circle, the icon at rest, the progress fill |
brand/alpha-10 | #2B7FFF at 10% | #2B7FFF at 10% | Active fill |
bg/soft-100 | #E9E9EA | #48484A | Progress track and the Disabled circle |
fg/strong-950 | #0E0E0F | #FFFFFF | Label and file name |
fg/soft-500 | #8E8E93 | #AEAEB2 | Supporting text and meta |
text/sub-700 | #48484A | #C7C7CC | Pill labels |
fg/sub-700 | #48484A | #C7C7CC | Pill icons, tile icon, action icon |
text/on-color/strong | #FFFFFF | #FFFFFF | Icon on the Active circle |
fg/disabled-400 | #AEAEB2 | #8E8E93 | Every ink on Disabled |
state/success/base-500 | #22C55E | #16A34A | Check beside Uploaded |
state/error/light-200 | #FFC9C9 | #FB2C36 at 24% | Error stroke |
state/error/lighter-50 | #FEF2F2 | #FB2C36 at 16% | Error tile |
state/error/base-500 | #FB2C36 | #FB2C36 | Error icon and reason |
dropShadow/xs | effect | effect | Featured circle at rest |
The edge is 1 wide in every state and drawn inside the frame, so it never changes the size.
Source: the Color Tokens collection of the Appetite UI Figma file, read on October 2, 2026.
How does File Upload differ on iOS and Material 3?
File Upload has no counterpart in the iOS or Material 3 pages of the file, because both platforms hand file selection to a system picker. Build the surface here and let it open the picker.
| Appetite UI | iOS counterpart | Material 3 counterpart | What differs |
|---|---|---|---|
| File Upload | None in the kit. SwiftUI offers PhotosPicker and fileImporter | None in the kit. Android offers the Photo Picker and GetContent | The system pickers draw the chooser. File Upload draws the surface that opens it and the progress after it, and the three sources map onto the pickers. |
The source pills and the round actions are 36 high, under the minimum touch target of 44 pt on iOS and 48 dp on Android set by platform/min-touch-target, so add hit area in code. The whole drop zone is the larger target for the first tap.
See Platform Bridge, iOS and Material 3 for how the same component adopts each platform.
Guidelines
Do
Put the reason on the meta line. The state has a place for the message, so use it instead of a toast that leaves.
Don't
Do not leave the supporting line empty. What you accept and how big it can be is the one thing the user needs before they choose.
Uploading, Upload and Error are answers from the server, so wire State to the real request. A row that says Uploading while nothing is in flight is worse than no state at all.
Tell assistive technology what happened. Announce Uploading and Upload in a polite live region and Error in an assertive one with the reason, and keep the progress amount as text, which the meta line already is.