A speaker icon with pulsing sound waves and a sentence whose highlight hops from word to word as it is read aloud. Every word shares one keyframe with a staggered delay.
Also known as: read aloud animation, tts word highlight, karaoke text highlight.
<div class="ldr-text-to-speech" role="status" aria-label="Reading aloud"> <svg class="ldr-text-to-speech-icon" viewBox="0 0 24 24" aria-hidden="true"><path class="ldr-text-to-speech-body" d="M4 9.5h3.5L12 5.5v13l-4.5-4H4z"/><path class="ldr-text-to-speech-wave" d="M15.5 9.2a4 4 0 0 1 0 5.6"/><path class="ldr-text-to-speech-wave" d="M18.3 6.6a7.8 7.8 0 0 1 0 10.8"/></svg> <span class="ldr-text-to-speech-words" aria-hidden="true"><span>Reading</span> <span>your</span> <span>notes</span> <span>aloud</span></span> </div>
.ldr-text-to-speech {
--s: var(--ldr-size, 48px);
--c: var(--ldr-color, #3b82f6);
display: inline-flex;
align-items: center;
gap: 0.5em;
font: 500 calc(var(--s) / 3) / 1.5 system-ui, sans-serif;
color: color-mix(in srgb, currentColor 72%, transparent);
white-space: nowrap;
}
.ldr-text-to-speech-icon {
width: 1.4em;
height: 1.4em;
fill: none;
stroke: var(--c);
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
.ldr-text-to-speech-body { fill: var(--c); }
.ldr-text-to-speech-wave {
animation: ldr-text-to-speech-wave 0.9s ease-in-out infinite;
}
.ldr-text-to-speech-wave + .ldr-text-to-speech-wave { animation-delay: 0.15s; }
.ldr-text-to-speech-words span {
padding: 0.1em 0.2em;
margin: 0 -0.2em;
border-radius: 0.35em;
animation: ldr-text-to-speech-read 2s infinite;
}
.ldr-text-to-speech-words span:nth-child(2) { animation-delay: 0.5s; }
.ldr-text-to-speech-words span:nth-child(3) { animation-delay: 1s; }
.ldr-text-to-speech-words span:nth-child(4) { animation-delay: 1.5s; }
@keyframes ldr-text-to-speech-read {
0%, 21% { background-color: color-mix(in srgb, var(--ldr-color, #3b82f6) 20%, transparent); color: var(--c); }
25%, 100% { background-color: transparent; color: inherit; }
}
@keyframes ldr-text-to-speech-wave {
0%, 100% { opacity: 0.25; }
50% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
.ldr-text-to-speech, .ldr-text-to-speech *, .ldr-text-to-speech::before, .ldr-text-to-speech::after, .ldr-text-to-speech *::before, .ldr-text-to-speech *::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.