A small document with a highlighter sweeping across its lines one at a time, as if the model is reading it, and a sparkle badge in the corner. Two synced animations, one moving down in steps.
Also known as: ai reading document animation, document scanning loader, summarize loading.
<div class="ldr-ai-summarizing" role="status" aria-label="Summarizing"> <span class="ldr-ai-summarizing-mark" aria-hidden="true"><i></i></span> <span class="ldr-ai-summarizing-line"></span> <span class="ldr-ai-summarizing-line"></span> <span class="ldr-ai-summarizing-line"></span> <span class="ldr-ai-summarizing-line"></span> <span class="ldr-ai-summarizing-line"></span> <svg class="ldr-ai-summarizing-badge" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 0C13 7 17 11 24 12C17 13 13 17 12 24C11 17 7 13 0 12C7 11 11 7 12 0Z"/></svg> </div>
.ldr-ai-summarizing {
--s: var(--ldr-size, 48px);
--c: var(--ldr-color, #3b82f6);
--row: calc(var(--s) / 5);
position: relative;
isolation: isolate;
display: grid;
align-content: start;
width: calc(var(--s) * 1.15);
padding: calc(var(--s) / 5) calc(var(--s) / 6);
border-radius: calc(var(--s) / 9);
background: color-mix(in srgb, var(--ldr-color, #3b82f6) 6%, transparent);
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ldr-color, #3b82f6) 28%, transparent);
}
.ldr-ai-summarizing-line {
height: var(--row);
background: linear-gradient(color-mix(in srgb, var(--ldr-color, #3b82f6) 40%, transparent), color-mix(in srgb, var(--ldr-color, #3b82f6) 40%, transparent)) 0 50% / 100% calc(var(--s) / 14) no-repeat;
border-radius: calc(var(--s) / 28);
}
.ldr-ai-summarizing-line:nth-of-type(3) { width: 82%; }
.ldr-ai-summarizing-line:nth-of-type(5) { width: 90%; }
.ldr-ai-summarizing-line:nth-of-type(6) { width: 55%; }
.ldr-ai-summarizing-mark {
position: absolute;
left: calc(var(--s) / 8);
right: calc(var(--s) / 8);
top: calc(var(--s) / 5);
z-index: -1;
height: var(--row);
animation: ldr-ai-summarizing-down 3s steps(5, end) infinite;
}
.ldr-ai-summarizing-mark i {
position: absolute;
inset: 12% 0;
border-radius: calc(var(--s) / 20);
background: color-mix(in srgb, var(--ldr-color, #3b82f6) 30%, transparent);
transform-origin: left;
animation: ldr-ai-summarizing-sweep 0.6s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
.ldr-ai-summarizing-badge {
position: absolute;
right: calc(var(--s) / -7);
top: calc(var(--s) / -7);
width: calc(var(--s) / 2.6);
height: calc(var(--s) / 2.6);
fill: var(--c);
animation: ldr-ai-summarizing-twinkle 1.5s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}
@keyframes ldr-ai-summarizing-down {
from { transform: translateY(0); }
to { transform: translateY(500%); }
}
@keyframes ldr-ai-summarizing-sweep {
0% { transform: scaleX(0); opacity: 1; }
75% { transform: scaleX(1); opacity: 1; }
100% { transform: scaleX(1); opacity: 0.4; }
}
@keyframes ldr-ai-summarizing-twinkle {
0% { transform: scale(1) rotate(0deg); }
50% { transform: scale(0.72) rotate(45deg); }
100% { transform: scale(1) rotate(90deg); }
}
@media (prefers-reduced-motion: reduce) {
.ldr-ai-summarizing, .ldr-ai-summarizing *, .ldr-ai-summarizing::before, .ldr-ai-summarizing::after, .ldr-ai-summarizing *::before, .ldr-ai-summarizing *::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.