Outline Text Fill
Hollow outlined letters fill with colour from the bottom up when you hover, like liquid rising inside the type.
Create outlined text that fills on hover in pure CSS. Render the word with color: transparent and -webkit-text-stroke: 1.5px in the light ink colour. Stack an ::after layer on top with content: attr(data-text), the accent colour as a solid fill, and clip-path: inset(100% 0 0 0) so it is hidden. On :hover and :focus-visible, transition the clip-path to inset(0 0 0 0) over 500ms with cubic-bezier(0.16, 1, 0.3, 1), so the fill rises from the baseline. Use a heavy display font so the outline reads well. Keep the real text in a visually hidden span and mark the visual element aria-hidden. Under prefers-reduced-motion, swap to the filled state instantly. The word reads "Explore".
Source
'use client'
import s from './demo.module.css'
export default function OutlineFill({ labels }: { labels: Record<string, string> }) {
return (
<a href="#" className={s.link} onClick={(e) => e.preventDefault()}>
<span className={s.sr}>{labels.text}</span>
<span className={s.word} data-text={labels.text} aria-hidden="true">
{labels.text}
</span>
</a>
)
}
.link {
border-radius: 6px;
text-decoration: none;
}
.link:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 6px; }
.sr {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
.word {
position: relative;
display: inline-block;
font: 800 clamp(2.4rem, 9vw, 3.4rem) / 1.1 var(--font-display);
letter-spacing: -0.01em;
color: transparent;
-webkit-text-stroke: 1.5px var(--stage-ink);
}
.word::after {
content: attr(data-text);
position: absolute;
inset: 0;
color: var(--stage-accent);
-webkit-text-stroke: 1.5px var(--stage-accent);
clip-path: inset(100% 0 0 0);
transition: clip-path 500ms var(--ease-out);
}
.link:hover .word::after,
.link:focus-visible .word::after { clip-path: inset(0 0 0 0); }
@media (prefers-reduced-motion: reduce) {
.word::after { transition: none; }
}