upload area
An upload area is an alternative for the input file upload button. The upload area let the user choose one or more files from their device storage - either via the browse link (that works similar to the input file upload), or via drag and drop into the upload area. These selected files can be uploaded to a server through form submission or be manipulated using the Javascript File API.
The variant Upload area with items shows all states, a file item can have: upload, uploading, uploaded and error.
When selecting / drag and drop files, a custom event called filesAdded is dispatched to the input element.
To handle the files, you have to add an event listener to the input element for the event filesAdded.
The demonstrator Upload area demonstrator with custom event example is showing an example and logs the added files to the console.
component variations
default
<div class="m-upload-area">
<div class="m-upload-area__field">
<div class="m-upload-area__description">
<i class="a-icon boschicon-bosch-ic-upload"></i>
Drag and drop files here or
<input
id="upload-input-1"
class="m-upload-area__input"
name="upload-input"
type="file"
multiple=""
/>
<label for="upload-input-1">browse</label>
to upload.
</div>
</div>
<div class="m-upload-area__items"></div>
</div>Upload area with global error
<div class="m-upload-area">
<div class="m-upload-area__field">
<div class="m-upload-area__description">
<i class="a-icon boschicon-bosch-ic-upload"></i>
Drag and drop files here or
<input
id="upload-input-2"
class="m-upload-area__input"
name="upload-input"
type="file"
multiple=""
/>
<label for="upload-input-2">browse</label>
to upload.
</div>
</div>
<div class="a-notification a-notification--text -error" role="alert">
<i class="a-icon ui-ic-alert-error"></i>
<div id="notification-label-id-text-error" class="a-notification__content">
This is an optional global upload error notification.
</div>
</div>
<div class="m-upload-area__items"></div>
</div>Upload area with items
32.4 MB
16.8 MB
13.1 MB
3.6 MB
<div class="m-upload-area">
<div class="m-upload-area__field">
<div class="m-upload-area__description">
<i class="a-icon boschicon-bosch-ic-upload"></i>
Drag and drop files here or
<input
id="upload-input-3"
class="m-upload-area__input"
name="upload-input"
type="file"
multiple=""
/>
<label for="upload-input-3">browse</label>
to upload.
</div>
</div>
<div class="m-upload-area__items">
<div class="m-upload-area__item">
<div class="m-upload-area__item-details">
<div class="m-upload-area__item-description">
<i class="a-icon boschicon-bosch-ic-document-plain"></i>
<div>
<strong class="-size-m">ready-to-upload-file.jpg</strong>
<br />
<span class="-size-s">32.4 MB</span>
</div>
</div>
<div class="m-upload-area__item-cta">
<button type="button" class="a-button a-button--integrated">
<i class="a-icon a-button__icon boschicon-bosch-ic-upload"></i>
<span class="a-button__label">Upload</span>
</button>
<button
type="button"
class="a-button a-button--integrated -without-label"
aria-label="delete"
>
<i class="a-icon a-button__icon boschicon-bosch-ic-delete"></i>
</button>
</div>
</div>
</div>
<div class="m-upload-area__item">
<div class="m-upload-area__item-details">
<div class="m-upload-area__item-description">
<i class="a-icon boschicon-bosch-ic-document-plain"></i>
<div>
<strong class="-size-m">uploading-file.jpg</strong>
<br />
<span class="-size-s">16.8 MB</span>
</div>
</div>
<div class="m-upload-area__item-cta">
<button
type="button"
class="a-button a-button--integrated -without-label"
aria-label="delete"
>
<i class="a-icon a-button__icon boschicon-bosch-ic-delete"></i>
</button>
</div>
</div>
<div class="m-upload-area__progress">
<div class="a-progress-indicator-container">
<progress
class="a-progress-indicator -determinate"
value="25"
max="100"
></progress>
<div class="a-progress-indicator__inner-bar"></div>
</div>
<div class="-size-s m-upload-area__progress-percentage">25%</div>
</div>
</div>
<div class="m-upload-area__item">
<div class="m-upload-area__item-details">
<div class="m-upload-area__item-description">
<i class="a-icon boschicon-bosch-ic-document-plain"></i>
<div>
<strong class="-size-m">uploaded-file.jpg</strong>
<br />
<span class="-size-s">13.1 MB</span>
</div>
</div>
<div class="m-upload-area__item-cta">
<button
type="button"
class="a-button a-button--integrated -without-label"
aria-label="delete"
>
<i class="a-icon a-button__icon boschicon-bosch-ic-delete"></i>
</button>
</div>
</div>
</div>
<div class="m-upload-area__item">
<div class="m-upload-area__item-details">
<div class="m-upload-area__item-description">
<i class="a-icon boschicon-bosch-ic-document-plain"></i>
<div>
<strong class="-size-m">upload-failed-file.jpg</strong>
<br />
<span class="-size-s">3.6 MB</span>
</div>
</div>
<div class="m-upload-area__item-cta">
<button type="button" class="a-button a-button--integrated">
<i class="a-icon a-button__icon boschicon-bosch-ic-reset"></i>
<span class="a-button__label">Try again</span>
</button>
<button
type="button"
class="a-button a-button--integrated -without-label"
aria-label="delete"
>
<i class="a-icon a-button__icon boschicon-bosch-ic-delete"></i>
</button>
</div>
</div>
<div class="a-notification a-notification--text -error" role="alert">
<i class="a-icon ui-ic-alert-error"></i>
<div
id="notification-label-id-text-error"
class="a-notification__content"
>
File failed to load. Please try again!
</div>
</div>
</div>
<div class="m-upload-area__cta">
<button type="button" class="a-button a-button--secondary">
<i class="a-icon a-button__icon boschicon-bosch-ic-upload"></i>
<span class="a-button__label">Upload all</span>
</button>
<button type="button" class="a-button a-button--tertiary">
<i class="a-icon a-button__icon boschicon-bosch-ic-delete"></i>
<span class="a-button__label">Remove all</span>
</button>
</div>
</div>
</div>Upload area demonstrator with custom event example
<script>
window.addEventListener("load", (event) => {
const uploadInput = document.getElementById('upload-input-4');
uploadInput.addEventListener('filesAdded', (e) => {
console.log('files added', e.detail.files);
});
});
</script>
<div class="m-upload-area">
<div class="m-upload-area__field">
<div class="m-upload-area__description">
<i class="a-icon boschicon-bosch-ic-upload"></i>
Drag and drop files here or
<input
id="upload-input-4"
class="m-upload-area__input"
name="upload-input"
type="file"
multiple=""
/>
<label for="upload-input-4">browse</label>
to upload.
</div>
</div>
<div class="m-upload-area__items"></div>
</div>additional content
styles SCSS
.m-upload-area {
color: var(--plain__enabled__front__default);
&__field {
height: 240px;
background-color: var(--neutral__enabled__fill__default);
background-image: linear-gradient(to right, var(--neutral__enabled__front__default) 50%, transparent 50%), linear-gradient(to right, var(--neutral__enabled__front__default) 50%, transparent 50%), linear-gradient(to bottom, var(--neutral__enabled__front__default) 50%, transparent 50%), linear-gradient(to bottom, var(--neutral__enabled__front__default) 50%, transparent 50%);
background-position: left top, left bottom, left top, right top;
background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
background-size: 2rem 1px, 2rem 1px, 1px 2rem, 1px 2rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
+ .a-notification {
padding-block: .75rem;
}
}
&__description {
max-width: 200px;
text-align: center;
.a-icon {
display: block;
font-size: 3rem;
margin-bottom: 0.5rem;
}
label {
cursor: pointer;
text-decoration: underline;
font-size: 1rem;
}
}
&__input {
width: 0;
height: 0;
opacity: 0;
&:focus-visible {
& + label {
@include focus-outside;
&::after {
outline-color: var(--neutral__enabled__fill__default);
}
}
}
}
&__items {
margin-top: 1rem;
}
&__item {
border-top: 1px solid var(--small__enabled__fill__default);
padding-block: 1rem;
&:first-child {
border-top: none;
}
}
&__item-details {
display: flex;
justify-content: space-between;
+ .a-notification {
margin-top: .25rem;
}
}
&__item-description {
display: flex;
flex-grow: 1;
align-items: center;
gap: 1rem;
.a-icon {
margin: .75rem;
}
}
&__item-cta {
display: flex;
justify-content: flex-end;
align-items: center;
}
&__progress {
display: flex;
align-items: center;
.a-progress-indicator-container {
flex-grow: 1;
}
}
&__progress-percentage {
width: 3rem;
text-align: center;
}
&__cta {
margin-top: 1rem;
display: flex;
justify-content: flex-end;
gap: .75rem;
}
}