A round send button whose paper plane pulls back, launches off the top corner and glides back in from below, with a ring rolling out on each launch. Loops while a message is in flight.
Also known as: paper plane send animation, send button loading, message sending icon.
<button class="ldr-send-message-button" type="button" aria-busy="true" aria-label="Sending message"> <span class="ldr-send-message-button-plane" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M4 11.5 20 4l-6.5 16-2.6-6.9z"/><path d="M10.9 13.1 20 4"/></svg></span> </button>
.ldr-send-message-button {
--s: var(--ldr-size, 48px);
--c: var(--ldr-color, #3b82f6);
position: relative;
display: grid;
width: var(--s);
height: var(--s);
padding: 0;
border: 0;
border-radius: 50%;
background: var(--c);
cursor: progress;
animation: ldr-send-message-button-press 1.8s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}
.ldr-send-message-button::before {
content: "";
position: absolute;
inset: 0;
border-radius: 50%;
border: max(1px, calc(var(--s) / 28)) solid var(--c);
opacity: 0;
animation: ldr-send-message-button-ring 1.8s cubic-bezier(0.2, 0.6, 0.35, 1) infinite;
}
.ldr-send-message-button-plane {
display: grid;
place-items: center;
border-radius: 50%;
overflow: hidden;
}
.ldr-send-message-button-plane svg {
width: 52%;
height: 52%;
fill: none;
stroke: #fff;
stroke-width: 2;
stroke-linejoin: round;
stroke-linecap: round;
animation: ldr-send-message-button-fly 1.8s infinite;
}
@keyframes ldr-send-message-button-fly {
0%, 18% { transform: translate(0, 0) scale(1); animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1); }
30% { transform: translate(-12%, 12%) scale(0.9); animation-timing-function: cubic-bezier(0.6, 0, 0.9, 0.4); }
52% { transform: translate(150%, -150%) scale(0.6); }
52.01% { transform: translate(-150%, 150%) scale(0.6); animation-timing-function: cubic-bezier(0.2, 0.8, 0.35, 1.15); }
80%, 100% { transform: translate(0, 0) scale(1); }
}
@keyframes ldr-send-message-button-press {
0%, 20%, 44%, 100% { transform: scale(1); }
32% { transform: scale(0.92); }
}
@keyframes ldr-send-message-button-ring {
0%, 34% { transform: scale(1); opacity: 0; }
36% { transform: scale(1); opacity: 0.6; }
75%, 100% { transform: scale(1.55); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
.ldr-send-message-button, .ldr-send-message-button *, .ldr-send-message-button::before, .ldr-send-message-button::after, .ldr-send-message-button *::before, .ldr-send-message-button *::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.