An underline draws in, then rises into a solid block behind the word while the letters flip to white in step with it, holds, and drains away. One easing on both, so the color edge tracks the block.
Also known as: underline grows into highlight, text highlight animation css, underline fill animation.
<div class="txt-underline-to-highlight">Now <span class="txt-underline-to-highlight-word">shipping</span></div>
.txt-underline-to-highlight {
--c: var(--txt-color, #3b82f6);
font-size: var(--txt-size, 32px);
font-family: system-ui, sans-serif;
font-weight: 700;
line-height: 1.2;
position: relative;
isolation: isolate;
white-space: nowrap;}
.txt-underline-to-highlight-word {
position: relative;
padding: 0 0.12em;
/* white bottom half, accent top half; sliding it raises the white */
background: linear-gradient(to top, #fff 50%, var(--c) 50%) 0 0 / 100% 200%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
-webkit-text-fill-color: transparent;
animation: txt-underline-to-highlight-ink 4s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}
.txt-underline-to-highlight-word::before {
content: "";
position: absolute;
z-index: -1;
inset: 0.06em 0 -0.06em;
border-radius: 0.08em;
background: var(--c);
transform: scale(0, 0.08);
transform-origin: 0 100%;
animation: txt-underline-to-highlight-bar 4s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}
@keyframes txt-underline-to-highlight-bar {
0% { transform: scale(0, 0.08); transform-origin: 0 100%; }
20% { transform: scale(1, 0.08); transform-origin: 0 100%; }
36%, 70% { transform: scale(1, 1); transform-origin: 0 100%; }
84% { transform: scale(1, 0.08); transform-origin: 0 100%; }
85% { transform: scale(1, 0.08); transform-origin: 100% 100%; }
97%, 100% { transform: scale(0, 0.08); transform-origin: 100% 100%; }
}
@keyframes txt-underline-to-highlight-ink {
0%, 20% { background-position: 0 0; }
36%, 70% { background-position: 0 100%; }
84%, 100% { background-position: 0 0; }
}
@media (prefers-reduced-motion: reduce) {
.txt-underline-to-highlight, .txt-underline-to-highlight *, .txt-underline-to-highlight::before, .txt-underline-to-highlight::after, .txt-underline-to-highlight *::before, .txt-underline-to-highlight *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--txt-color on the element or a parent.
Currently previewing #3b82f6.--txt-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 40px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.