Dropzone and browse trigger for selecting files, reviewing selected file sizes, and removing files before submission.
Install @wyrd-company/react or the Flutter package and use it directly —
your DESIGN.md does the styling.
catalog/contracts/file-upload.ymlsource of truth · verbatim
$schema: https://thewyrdingway.dev/schemas/component-contract.schema.ymlcontractVersion: 0.1.0identity:name: FileUploadtag: wyrd-file-uploadpackage: wyrd.component.file-uploadtitle: File Uploadsummary: Dropzone and browse trigger for selecting files, reviewing selected file sizes, and removing files before submission.relationships:related-to: - behavior-file-upload - l10n-formatting-runtimeclassification:category: form-fieldcomposition: collectioninteractive: truetarget-strategy:react: requiredflutter: requiredsurface:properties: - name: selected-filestype:kind: arrayitem:kind: objectfields:name:kind: stringsize-bytes:kind: numberrequired: falsedefault: []controlled: trueserializable: truesummary: Controlled selected file metadata, represented as name plus byte size. - name: max-size-bytestype:kind: numberrequired: falsedefault: 10485760serializable: truesummary: Maximum accepted size for one file, in bytes. - name: is-drag-overtype:kind: booleanrequired: falsedefault: falsecontrolled: truereflects: trueserializable: truesummary: Controlled visual state for an active drag target. - name: is-disabledtype:kind: booleanrequired: falsedefault: falsereflects: trueserializable: truesummary: Prevents drop, browse activation, remove, and clear interactions. - name: accepttype:kind: stringrequired: falseserializable: truesummary: File input accept attribute forwarded to the browse control.slots: - name: labelrequired: falsecardinality: zero-or-oneaccepts: - textsummary: Optional visible label and accessible-name source for the upload control.events: - name: browsepayload:action:kind: action-refbubbles: truecomposed: truecancelable: truesummary: Emitted when the browse trigger or keyboard activation requests the host file picker. - name: files-addpayload:files:kind: arrayitem:kind: objectfields:name:kind: stringsize-bytes:kind: numberaction:kind: action-refbubbles: truecomposed: truecancelable: truesummary: Emitted when files are dropped or accepted from a host picker. - name: file-removepayload:name:kind: stringaction:kind: action-refbubbles: truecomposed: truecancelable: truesummary: Emitted when one selected file is removed. - name: files-clearpayload:action:kind: action-refbubbles: truecomposed: truecancelable: truesummary: Emitted when the selected-file list is cleared. - name: file-rejectpayload:name:kind: stringsize-bytes:kind: numbermax-size-bytes:kind: numberaction:kind: action-refbubbles: truecomposed: truecancelable: falsesummary: Emitted when a candidate file is rejected by the max-size rule.states: - name: idlesource: behavior-runtimesummary: No drag is active and no selected files are present. - name: drag-oversource: behavior-runtimesummary: A file drag is active over the dropzone. - name: has-filessource: behavior-runtimesummary: At least one file is selected. - name: disabledsource: propertysummary: Disabled state derived from is-disabled. - name: rejectedsource: behavior-runtimesummary: The last add attempt rejected at least one file.parts: - name: rootsummary: Field root and selected-file list owner.slots: - label - name: labelsummary: Optional label text.slots: - label - name: dropzonesummary: Focusable drag-and-drop target and browse keyboard activation surface. - name: promptsummary: Drop prompt text. - name: browse-triggersummary: Button that requests the host file picker. - name: file-listsummary: Selected-file list container. - name: file-itemsummary: Repeated selected-file row. - name: remove-triggersummary: Per-file remove affordance. - name: summarysummary: Aggregate file count and total selected size. - name: errorsummary: Rejection message container.tokens:root:gap: spacing.smlabel:color: color.on-surfacefont: typography.label-mddropzone:background: color.surfacebackground-active: color.primary-hoverforeground: color.on-surfaceforeground-active: color.on-primaryborder-color: color.outlineborder-color-active: color.primaryborder-width: 1pxradius: rounded.mdpadding: spacing.lggap: spacing.smbrowse-trigger:background: color.primaryforeground: color.on-primaryradius: rounded.smpadding-x: spacing.mdpadding-y: spacing.smfont: typography.label-mdfile-item:background: color.surface-containerforeground: color.on-surfaceradius: rounded.smpadding-x: spacing.mdpadding-y: spacing.smgap: spacing.smsummary:color: color.on-surface-variantfont: typography.body-mderror:color: color.errorfont: typography.body-mdmessages: - key: file-upload.labeldescription: Default visible label shown when the label slot is not supplied. - key: file-upload.drop-promptdescription: Instructional text shown inside the dropzone. - key: file-upload.browse-labeldescription: Browse trigger label. - key: file-upload.file-countdescription: Aggregate count of selected files.params:count: plural - key: file-upload.file-size-bytedescription: Per-file size shown in bytes.params:size: unit - key: file-upload.file-size-kilobytedescription: Per-file size shown in kilobytes.params:size: unit - key: file-upload.file-size-megabytedescription: Per-file size shown in megabytes.params:size: unit - key: file-upload.file-size-gigabytedescription: Per-file size shown in gigabytes.params:size: unit - key: file-upload.total-size-bytedescription: Aggregate selected-file size shown in bytes.params:size: unit - key: file-upload.total-size-kilobytedescription: Aggregate selected-file size shown in kilobytes.params:size: unit - key: file-upload.total-size-megabytedescription: Aggregate selected-file size shown in megabytes.params:size: unit - key: file-upload.total-size-gigabytedescription: Aggregate selected-file size shown in gigabytes.params:size: unit - key: file-upload.remove-filedescription: Accessible label for removing a selected file.params:name: string - key: file-upload.clear-labeldescription: Clear selected files trigger label. - key: file-upload.file-too-largedescription: Rejection message shown when a file exceeds the maximum allowed size.params:name: stringsize: unitmaxSize: unitobligations:accessibility:role: groupname: Label slot or default label provides the accessible name for the file upload control.description: Drop prompt, selected-file summary, and rejection message are programmatically associated with the interactive dropzone.keyboard: - Dropzone is focusable when enabled. - Enter and Space on the focused dropzone activate the browse request. - Remove triggers are reachable by Tab and expose the filename in their accessible label. - Disabled upload prevents browse, drop, remove, and clear activation.semantics: - Dropzone exposes disabled state when the upload is disabled. - Selected files are exposed as a list with one item per file. - Rejection text is exposed as an alert or status update.behavior:model: behavior-runtimeruntimeBlueprint: behavior/file-uploadinteractions: - Drag enter moves the machine from idle to drag-over unless disabled. - Drop or accepted browse result with at least one file moves to has-files. - Removing the final file returns to idle. - Clear returns from has-files or rejected to idle.hostEffects: - Activate the host file picker when browse is requested. - Dispatch files-add, file-remove, files-clear, and file-reject events. - Validate dropped or browsed file sizes before committing them to selected-files.conditions: - rtl - locale - reduced-motion - cvd-deuteranopia - density-compact - text-expansion - keyboard-insetsdui:status: partialserializableProperties: - selected-files - max-size-bytes - is-drag-over - is-disabled - acceptserializableSlots: - labeleventBindings: - browse - files-add - file-remove - files-clear - file-rejectnotes: File metadata and control state serialize; host file handles, drag data transfer, and native picker activation remain target-local.examples: - name: idlestate: idleproperties:selected-files: []accept: image/png,image/jpeg,image/webpslots:label: Upload design assets - name: drag-overstate: drag-overproperties:is-drag-over: trueselected-files: []slots:label: Drop validation evidence - name: disabledstate: disabledproperties:is-disabled: trueselected-files: []slots:label: Upload release attachments
01
Examples
Generated from the contract's examples block.
Previews are the published @wyrd-company/react component, themed by the
brand's role tokens — flip the site's light/dark toggle and they follow.
Controlled selected file metadata, represented as name plus byte size.
max-size-bytes
number
10485760
Maximum accepted size for one file, in bytes.
is-drag-over
boolean
false
Controlled visual state for an active drag target.
is-disabled
boolean
false
Prevents drop, browse activation, remove, and clear interactions.
accept
string
undefined
File input accept attribute forwarded to the browse control.
03
Slots & events
Slot
Cardinality
Accepts
Summary
label
zero-or-one
text
Optional visible label and accessible-name source for the upload control.
Event
Payload
Summary
browse
action
Emitted when the browse trigger or keyboard activation requests the host file picker.
files-add
files, action
Emitted when files are dropped or accepted from a host picker.
file-remove
name, action
Emitted when one selected file is removed.
files-clear
action
Emitted when the selected-file list is cleared.
file-reject
name, size-bytes, max-size-bytes, action
Emitted when a candidate file is rejected by the max-size rule.
04
Tokens
The contract's token bindings. Open the shared contextual sidebar from any example;
primary swatches reflect the color you pick there. Error swatches show the active
preset's values; editing any knob switches to custom and resets error to TWW · Ink.
Binding
Token
root.gap
spacing.sm
label.color
color.on-surface
label.font
typography.label-md
dropzone.background
color.surface
dropzone.background-active
color.primary-hover
dropzone.foreground
color.on-surface
dropzone.foreground-active
color.on-primary
dropzone.border-color
color.outline
dropzone.border-color-active
color.primary
dropzone.border-width
1px
dropzone.radius
rounded.md
dropzone.padding
spacing.lg
dropzone.gap
spacing.sm
browse-trigger.background
color.primary
browse-trigger.foreground
color.on-primary
browse-trigger.radius
rounded.sm
browse-trigger.padding-x
spacing.md
browse-trigger.padding-y
spacing.sm
browse-trigger.font
typography.label-md
file-item.background
color.surface-container
file-item.foreground
color.on-surface
file-item.radius
rounded.sm
file-item.padding-x
spacing.md
file-item.padding-y
spacing.sm
file-item.gap
spacing.sm
summary.color
color.on-surface-variant
summary.font
typography.body-md
error.color
color.error
error.font
typography.body-md
05
States
State
Source
Summary
idle
behavior-runtime
No drag is active and no selected files are present.
drag-over
behavior-runtime
A file drag is active over the dropzone.
has-files
behavior-runtime
At least one file is selected.
disabled
property
Disabled state derived from is-disabled.
rejected
behavior-runtime
The last add attempt rejected at least one file.
06
Accessibility
From the contract's accessibility obligations. Role group; Label slot or default label provides the accessible name for the file upload control.
Dropzone is focusable when enabled.
Enter and Space on the focused dropzone activate the browse request.
Remove triggers are reachable by Tab and expose the filename in their accessible label.
Disabled upload prevents browse, drop, remove, and clear activation.
Dropzone exposes disabled state when the upload is disabled.
Selected files are exposed as a list with one item per file.
Rejection text is exposed as an alert or status update.