File Input
formUSWDS-derivedinteractive
File upload control with drag-and-drop, preview, and file type validation.
Reference: USWDS documentation ↗
Variants
Default File Input
<flex-file-input>
<label class="flex-label" for="file-upload">Upload a file</label>
<div class="flex-file-input__target">
<div class="flex-file-input__instructions" aria-hidden="true">Drag file here or <span class="flex-file-input__choose">choose from folder</span></div>
<input class="flex-file-input__input" id="file-upload" name="file-upload" type="file"/>
</div>
<div class="flex-file-input__preview-area">
</div>
</flex-file-input>Image Only
<flex-file-input>
<label class="flex-label" for="image-upload">Upload an image</label>
<span class="flex-file-input__hint" id="image-upload-hint">Select any image file (JPG, PNG, GIF)</span>
<div class="flex-file-input__target">
<div class="flex-file-input__instructions" aria-hidden="true">Drag file here or <span class="flex-file-input__choose">choose from folder</span></div>
<input class="flex-file-input__input" id="image-upload" name="image-upload" type="file" accept="image/*" aria-describedby="image-upload-hint"/>
</div>
<div class="flex-file-input__preview-area">
</div>
</flex-file-input>Multiple Files
<flex-file-input>
<label class="flex-label" for="multi-upload">Upload documents</label>
<span class="flex-file-input__hint" id="multi-upload-hint">Select one or more PDF or Word documents</span>
<div class="flex-file-input__target">
<div class="flex-file-input__instructions" aria-hidden="true">Drag file here or <span class="flex-file-input__choose">choose from folder</span></div>
<input class="flex-file-input__input" id="multi-upload" name="multi-upload" type="file" accept=".pdf,.doc,.docx" multiple="" aria-describedby="multi-upload-hint"/>
</div>
<div class="flex-file-input__preview-area">
</div>
</flex-file-input>Contract
Class mapping
| USWDS | Flex | Notes |
|---|---|---|
usa-file-input | <flex-file-input> (custom element) | Container element |
usa-file-input__target | .flex-file-input__target | Drop zone / click target |
usa-file-input__instructions | .flex-file-input__instructions | Instructional text ("Drag file here or choose from folder") |
usa-file-input__choose | .flex-file-input__choose | "choose from folder" styled link text |
usa-file-input__input | .flex-file-input__input | Hidden native file input positioned over target |
usa-file-input__preview | .flex-file-input__preview | Individual file preview card |
usa-file-input__target (drag active) | .flex-file-input__target[data-drag-active] | Drag highlight state via data attribute |
has-invalid-file | .flex-file-input__target--error | Error state when file type is invalid |
Verified properties
displaymax-widthwidthborder-stylefont-sizetext-alignpositionBehavior promises
- ✓ Drag highlight appears on dragenter/dragover
- ✓ Drag highlight removed on dragleave/drop
- ✓ File selection displays file name and size in preview
- ✓ Image files show thumbnail preview
- ✓ Remove button removes file from preview
- ✓ Invalid file type shows error state
Source CSS
/* flex-file-input — USWDS File Input conformance
File upload with drag-and-drop, preview, and validation */
flex-file-input {
display: block;
max-inline-size: var(--flex-control-max-width);
inline-size: 100%;
}
.flex-file-input__target {
border: 1px dashed var(--flex-gray-cool-30);
display: block;
font-size: 0.93rem;
margin-block-start: 0.5rem;
position: relative;
text-align: center;
inline-size: 100%;
transition: border-color 0.15s ease, background-color 0.15s ease;
&:hover {
border-color: var(--flex-gray-60);
}
&[data-drag-active] {
border-color: var(--flex-blue-vivid-60);
border-style: solid;
background-color: var(--flex-blue-vivid-5);
}
}
.flex-file-input__target--error {
border-color: var(--flex-color-error);
}
.flex-file-input__instructions {
padding-block: 2rem;
padding-inline: 1rem;
pointer-events: none;
position: relative;
z-index: 3;
color: var(--flex-color-text);
}
.flex-file-input__choose {
color: var(--flex-blue-vivid-60);
text-decoration: underline;
font-weight: 400;
}
.flex-file-input__input {
cursor: pointer;
block-size: 100%;
inset-inline-start: 0;
margin: 0;
max-inline-size: none;
position: absolute;
padding: 0.5rem;
text-indent: -999em;
inset-block-start: 0;
inline-size: 100%;
z-index: 1;
opacity: 0;
border: 0;
&:focus + .flex-file-input__instructions {
outline: 0.25rem solid var(--flex-blue-vivid-40);
outline-offset: 0;
}
}
/* --- Preview area --- */
.flex-file-input__preview-area {
margin-block-start: 0.5rem;
}
.flex-file-input__preview {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.5rem;
border: 1px solid var(--flex-gray-10);
border-radius: 0.25rem;
background-color: var(--flex-color-surface);
margin-block-end: 0.25rem;
}
.flex-file-input__preview--error {
border-color: var(--flex-color-error);
background-color: var(--flex-color-error-lighter);
& .flex-file-input__file-name {
color: var(--flex-color-error);
font-weight: 400;
}
}
.flex-file-input__thumbnail {
inline-size: 3rem;
block-size: 3rem;
object-fit: cover;
border-radius: 0.125rem;
flex-shrink: 0;
}
.flex-file-input__file-icon {
display: inline-block;
inline-size: 2rem;
block-size: 2.5rem;
flex-shrink: 0;
background-color: var(--flex-gray-30);
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z'/%3E%3C/svg%3E");
mask-size: contain;
mask-repeat: no-repeat;
}
.flex-file-input__file-info {
display: flex;
flex-direction: column;
gap: 0.125rem;
flex: 1;
min-inline-size: 0;
}
.flex-file-input__file-name {
font-size: 0.93rem;
font-weight: 700;
color: var(--flex-color-text);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.flex-file-input__file-size {
font-size: 0.8rem;
color: var(--flex-color-text-muted);
}
.flex-file-input__remove {
display: flex;
align-items: center;
justify-content: center;
inline-size: 1.5rem;
block-size: 1.5rem;
flex-shrink: 0;
border: 0;
border-radius: 50%;
background-color: transparent;
color: var(--flex-gray-60);
font-size: 1.25rem;
line-height: 1;
cursor: pointer;
padding: 0;
&:hover {
background-color: var(--flex-gray-10);
color: var(--flex-color-text);
}
&:focus {
outline: 0.25rem solid var(--flex-blue-vivid-40);
outline-offset: 0;
}
}
/* --- Screen reader status --- */
.flex-file-input__status {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
A digital services project by Flexion