A pill toast with an arrow that keeps launching out of its circle, an 'Uploading 3 files' label and a mini progress bar underneath. Drop it in a corner while files go up.
Also known as: upload progress toast, file upload notification, uploading snackbar.
<div class="ldr-upload-toast" role="status" aria-label="Uploading 3 files">
<span class="ldr-upload-toast-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M12 18V6M6.5 11.5 12 6l5.5 5.5"/></svg></span>
<span class="ldr-upload-toast-body" aria-hidden="true">
<span class="ldr-upload-toast-label">Uploading 3 files</span>
<span class="ldr-upload-toast-bar"></span>
</span>
</div>.ldr-upload-toast {
--s: var(--ldr-size, 48px);
--c: var(--ldr-color, #3b82f6);
display: inline-flex;
align-items: center;
gap: 0.7em;
padding: 0.5em 1.1em 0.5em 0.5em;
border-radius: 2em;
background: color-mix(in srgb, var(--ldr-color, #3b82f6) 10%, transparent);
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ldr-color, #3b82f6) 22%, transparent);
font: 500 calc(var(--s) / 3.2) / 1 system-ui, sans-serif;
color: color-mix(in srgb, currentColor 85%, transparent);
}
.ldr-upload-toast-icon {
display: grid;
place-items: center;
width: 2.2em;
height: 2.2em;
border-radius: 50%;
background: var(--c);
overflow: hidden;
}
.ldr-upload-toast-icon svg {
width: 1.3em;
height: 1.3em;
fill: none;
stroke: #fff;
stroke-width: 2.6;
stroke-linecap: round;
stroke-linejoin: round;
animation: ldr-upload-toast-launch 1.6s infinite;
}
.ldr-upload-toast-body {
display: grid;
gap: 0.45em;
}
.ldr-upload-toast-label {
white-space: nowrap;
}
.ldr-upload-toast-bar {
position: relative;
height: 0.28em;
border-radius: 0.28em;
background: color-mix(in srgb, var(--ldr-color, #3b82f6) 18%, transparent);
overflow: hidden;
}
.ldr-upload-toast-bar::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background: var(--c);
transform-origin: left;
animation: ldr-upload-toast-fill 3.2s infinite;
}
@keyframes ldr-upload-toast-launch {
0%, 25% { transform: translateY(0); animation-timing-function: cubic-bezier(0.6, 0, 0.9, 0.5); }
50% { transform: translateY(-150%); }
50.01% { transform: translateY(150%); animation-timing-function: cubic-bezier(0.2, 0.8, 0.3, 1.2); }
80%, 100% { transform: translateY(0); }
}
@keyframes ldr-upload-toast-fill {
0% { transform: scaleX(0); opacity: 1; animation-timing-function: cubic-bezier(0.3, 0.7, 0.4, 1); }
45% { transform: scaleX(0.55); animation-timing-function: cubic-bezier(0.5, 0, 0.3, 1); }
85%, 94% { transform: scaleX(1); opacity: 1; }
100% { transform: scaleX(1); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
.ldr-upload-toast, .ldr-upload-toast *, .ldr-upload-toast::before, .ldr-upload-toast::after, .ldr-upload-toast *::before, .ldr-upload-toast *::after {
animation-duration: 3s;
}
}--ldr-color on the element or a parent.
Currently previewing #3b82f6.--ldr-size. Every dimension in the snippet is
derived from it. Currently previewing 64px.animation shorthand.
Delays on the children are relative, so they keep their rhythm.role="status" and an
aria-label so screen readers announce the wait. The reduced-motion rule
at the end slows the animation for users who asked for less motion.