A loose marker swoosh draws itself under the link on hover and rewinds when you leave. An inline SVG path with pathLength set to 1, so the dash offset math never changes.
Also known as: svg underline animation, scribble underline css, marker stroke underline.
<a class="hfx-link-scribble-underline" href="#">Start here<svg class="hfx-link-scribble-underline-line" viewBox="0 0 200 22" aria-hidden="true"><path d="M4 9C60 4 140 3 196 6C150 9 80 12 34 18" pathLength="1"/></svg></a>
.hfx-link-scribble-underline {
--c: var(--hfx-color, #3b82f6);
position: relative;
display: inline-block;
color: var(--c);
font: 600 calc(var(--hfx-size, 16px) * 1.25)/1.3 system-ui, sans-serif;
text-decoration: none;
padding-bottom: 0.1em;}
.hfx-link-scribble-underline-line {
position: absolute;
left: -0.15em;
top: 88%;
width: calc(100% + 0.3em);
overflow: visible;
pointer-events: none;
}
.hfx-link-scribble-underline-line path {
fill: none;
stroke: var(--c);
stroke-width: 5;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 1 2;
stroke-dashoffset: 1;
transition: stroke-dashoffset 0.6s cubic-bezier(0.65, 0, 0.35, 1);
}
.hfx-link-scribble-underline:is(:hover, :focus-visible) .hfx-link-scribble-underline-line path { stroke-dashoffset: 0; }
@media (prefers-reduced-motion: reduce) {
.hfx-link-scribble-underline, .hfx-link-scribble-underline *, .hfx-link-scribble-underline::before, .hfx-link-scribble-underline::after, .hfx-link-scribble-underline *::before, .hfx-link-scribble-underline *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--hfx-color on the element or a parent.
Currently previewing #3b82f6.--hfx-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 16px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.