I guess you are out of luck to do this using one gradient but here is an idea that rely on mask where you will need at least 3 gradient to create the holes. The good thing is that the gradient is the same so using CSS variables we can see it as one gradient.
body {
background-color: #222;
}
a {
color: white;
font-size: 30px;
text-decoration: none;
position: relative;
display: inline-block;
overflow: hidden;
}
a:before {
content: "";
position: absolute;
bottom: 0;
left: 0;
right: -150%;
height: 2px;
background: currentcolor;
--grad: linear-gradient(to right, white calc(50% - 5px), transparent calc(50% - 5px) calc(50% + 5px), white 0);
-webkit-mask: var(--grad), var(--grad), var(--grad);
-webkit-mask-size: 230% 100%, 280% 100%, 350% 100%;
-webkit-mask-position: 100% 0;
-webkit-mask-composite: destination-in;
mask: var(--grad), var(--grad), var(--grad);
mask-size: 230% 100%, 280% 100%, 350% 100%;
mask-position: 100% 0;
mask-composite: intersect;
animation: move 4s infinite ease-out;
}
@keyframes move {
100% {
-webkit-mask-position: 54% 0;
mask-position: 54% 0;
}
}
<a href="#">Animated link underline</a>
The mask part isn't difficult. All the trick rely on the gradient and position animation.
here is a better illustration to understand the trick. The green square are the holes in the previous code:
body {
background-color: #222;
}
a {
color: white;
font-size: 30px;
text-decoration: none;
position: relative;
display: inline-block;
overflow:hidden;
}
a:before {
content: "";
position: absolute;
bottom: 0;
left: 0;
right: -150%;
height: 8px;
--grad: linear-gradient(to right, transparent calc(50% - 5px), green calc(50% - 5px) calc(50% + 5px), transparent 0);
background: var(--grad), var(--grad), var(--grad);
background-size: 230% 100%, 280% 100%, 350% 100%;
background-position: 100% 0;
animation: move 4s infinite ease-out;
}
@keyframes move {
100% {
background-position: 54% 0;
}
}
<a href="#">Animated link underline</a>
Related question to understand the calculation: Using percentage values with background-position on a linear-gradient
Another idea using only background:
body {
background-color: #222;
}
a {
color: white;
font-size: 30px;
text-decoration: none;
padding-bottom:5px;
background:
linear-gradient(to right,
currentColor 0 80%,
transparent 0 calc(80% + 10px),
currentColor 0 85%,
transparent 0 calc(85% + 10px),
currentColor 0 90%,
transparent 0 calc(90% + 10px),
currentColor 0) bottom right/1000% 2px no-repeat;
animation:move 2s linear infinite;
}
.alt {
-webkit-box-decoration-break: clone;
}
@keyframes move {
100% {
background-size:calc(100% + 60px) 2px;
background-position:bottom 0 right -60px;
}
}
<a href="#">Animated link underline</a>
<br>
<br>
<a href="#" style="color:red;">Animated multil line<br> underline</a>
<br>
<br>
<a href="#" class="alt" style="color:pink;">Animated multil line<br> underline</a>