A solid block with the word punched clean through it. On hover the block drops into an underline and the letters fill back in. An SVG mask cuts the holes, so any background shows through.
Also known as: cutout text hover, text mask block, punched out text.
<svg class="hfx-text-knockout" viewBox="0 0 220 90" role="img" aria-label="Bold">
<defs>
<mask id="hfx-text-knockout-holes">
<rect width="220" height="90" fill="#fff"/>
<text x="110" y="67" text-anchor="middle" font-size="62" fill="#000">BOLD</text>
</mask>
</defs>
<g mask="url(#hfx-text-knockout-holes)">
<rect class="hfx-text-knockout-block" width="220" height="90" rx="8"/>
</g>
<text class="hfx-text-knockout-word" x="110" y="67" text-anchor="middle" font-size="62">BOLD</text>
</svg>.hfx-text-knockout {
--c: var(--hfx-color, #3b82f6);
font-size: var(--hfx-size, 16px);
width: 10em;
overflow: visible;
font-family: system-ui, sans-serif;
font-weight: 900;
cursor: default;
}
.hfx-text-knockout-block {
fill: var(--c);
transform-box: fill-box;
transform-origin: 50% 100%;
transition: transform 0.5s cubic-bezier(0.7, 0, 0.2, 1);
}
.hfx-text-knockout-word {
fill: currentColor;
opacity: 0;
transition: opacity 0.3s ease;
}
.hfx-text-knockout:hover .hfx-text-knockout-block { transform: scaleY(0.07); }
.hfx-text-knockout:hover .hfx-text-knockout-word {
opacity: 1;
transition-delay: 0.15s;
}
@media (prefers-reduced-motion: reduce) {
.hfx-text-knockout, .hfx-text-knockout *, .hfx-text-knockout::before, .hfx-text-knockout::after, .hfx-text-knockout *::before, .hfx-text-knockout *::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.