30 CSS Text Hover Effects30 / 30
Mask Clip Path Reveal
The geometry chapter that closes the collection: three reveal shapes on one board, all riding clip-path's native interpolation. A DIAGONAL WIPE — an accent-colored clone clipped to a 4-point polygon that sweeps corner-to-corner (the parallelogram's leading edge stays angled mid-flight). A CIRCLE BLOOM — a photograph clipped inside the glyphs (background-clip:text) revealed by circle(0%) growing to circle(75%). And BLINDS — a 12-point polygon whose three zero-width slats grow open into thirds of the word. The one law under all three: shapes interpolate only between the SAME function with the SAME point count.
Published
The code
<section class="cth-30" aria-label="Clip path reveal demo">
<div class="cth-30__stage">
<div class="cth-30__scene">
<header class="cth-30__head">
<p class="cth-30__eyebrow">Studio Meridian — title sequences & type systems</p>
<span class="cth-30__chip">same shape, same points → it tweens</span>
</header>
<div class="cth-30__rows">
<a class="cth-30__row cth-30__wipe" href="#cth30-top" id="cth30-top">
<span class="cth-30__tag">01 · diagonal wipe · polygon()</span>
<span class="cth-30__stackword"><span class="cth-30__base">SEQUENCE</span><span class="cth-30__clone cth-30__clone--wipe" aria-hidden="true">SEQUENCE</span></span>
</a>
<a class="cth-30__row cth-30__bloom" href="#cth30-top">
<span class="cth-30__tag">02 · circle bloom · circle() + photo-in-text</span>
<span class="cth-30__stackword"><span class="cth-30__base">HORIZON</span><span class="cth-30__clone cth-30__clone--bloom" aria-hidden="true" style="--photo:url('https://images.unsplash.com/photo-1506905925346-21bda4d32df4?q=80&w=1400&auto=format&fit=crop')">HORIZON</span></span>
</a>
<a class="cth-30__row cth-30__blinds" href="#cth30-top">
<span class="cth-30__tag">03 · blinds · 12-point polygon</span>
<span class="cth-30__stackword"><span class="cth-30__base">INTERVAL</span><span class="cth-30__clone cth-30__clone--blinds" aria-hidden="true">INTERVAL</span></span>
</a>
</div>
<p class="cth-30__hint">Hover each row. Three shapes, one law: clip-path only interpolates between the same function with the same point count — design the two states as siblings first.</p>
</div>
</div>
</section><section class="cth-30" aria-label="Clip path reveal demo">
<div class="cth-30__stage">
<div class="cth-30__scene">
<header class="cth-30__head">
<p class="cth-30__eyebrow">Studio Meridian — title sequences & type systems</p>
<span class="cth-30__chip">same shape, same points → it tweens</span>
</header>
<div class="cth-30__rows">
<a class="cth-30__row cth-30__wipe" href="#cth30-top" id="cth30-top">
<span class="cth-30__tag">01 · diagonal wipe · polygon()</span>
<span class="cth-30__stackword"><span class="cth-30__base">SEQUENCE</span><span class="cth-30__clone cth-30__clone--wipe" aria-hidden="true">SEQUENCE</span></span>
</a>
<a class="cth-30__row cth-30__bloom" href="#cth30-top">
<span class="cth-30__tag">02 · circle bloom · circle() + photo-in-text</span>
<span class="cth-30__stackword"><span class="cth-30__base">HORIZON</span><span class="cth-30__clone cth-30__clone--bloom" aria-hidden="true" style="--photo:url('https://images.unsplash.com/photo-1506905925346-21bda4d32df4?q=80&w=1400&auto=format&fit=crop')">HORIZON</span></span>
</a>
<a class="cth-30__row cth-30__blinds" href="#cth30-top">
<span class="cth-30__tag">03 · blinds · 12-point polygon</span>
<span class="cth-30__stackword"><span class="cth-30__base">INTERVAL</span><span class="cth-30__clone cth-30__clone--blinds" aria-hidden="true">INTERVAL</span></span>
</a>
</div>
<p class="cth-30__hint">Hover each row. Three shapes, one law: clip-path only interpolates between the same function with the same point count — design the two states as siblings first.</p>
</div>
</div>
</section>.cth-30,
.cth-30 *,
.cth-30 *::before,
.cth-30 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cth-30 {
--bg: oklch(0.2 0.012 285);
--ink: oklch(0.94 0.008 280);
--mut: oklch(0.62 0.02 285);
--acc: oklch(0.75 0.17 65);
--line: oklch(0.34 0.02 285);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
}
.cth-30__stage {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
background: radial-gradient(90% 75% at 50% 15%,oklch(0.24 0.016 290),var(--bg) 72%);
padding: clamp(20px,4vw,56px);
container: cth30/inline-size;
}
.cth-30__scene {
width: min(100%,780px);
display: flex;
flex-direction: column;
gap: clamp(16px,3cqi,26px);
}
.cth-30__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
flex-wrap: wrap;
padding-bottom: 14px;
border-bottom: 1px solid var(--line);
}
.cth-30__eyebrow {
font-size: 12px;
font-weight: 700;
letter-spacing: .22em;
text-transform: uppercase;
color: var(--mut);
}
.cth-30__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--acc);
border: 1px solid color-mix(in oklch,var(--acc) 40%,transparent);
border-radius: 99px;
padding: 5px 11px;
}
.cth-30__rows {
display: flex;
flex-direction: column;
}
.cth-30__row {
display: flex;
flex-direction: column;
gap: 6px;
padding: clamp(14px,2.6cqi,22px) 4px;
border-bottom: 1px solid var(--line);
text-decoration: none;
color: inherit;
}
.cth-30__row:focus-visible {
outline: 2px solid var(--acc);
outline-offset: -2px;
border-radius: 6px;
}
.cth-30__tag {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 10.5px;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--mut);
}
.cth-30__stackword {
position: relative;
display: inline-block;
width: fit-content;
font-size: clamp(38px,7.6cqi,74px);
font-weight: 900;
letter-spacing: .01em;
line-height: 1.08;
}
.cth-30__base {
display: block;
color: color-mix(in oklch,var(--ink) 34%,transparent);
}
.cth-30__clone {
position: absolute;
inset: 0;
display: block;
pointer-events: none;
}
.cth-30__clone--wipe {
color: var(--acc);
clip-path: polygon(-15% 0%,0% 0%,-15% 100%,-30% 100%);
transition: clip-path .65s cubic-bezier(.7,0,.2,1);
}
.cth-30__wipe:hover .cth-30__clone--wipe,
.cth-30__wipe:focus-visible .cth-30__clone--wipe {
clip-path: polygon(0% 0%,130% 0%,115% 100%,-15% 100%);
}
.cth-30__clone--bloom {
background-image: linear-gradient(oklch(0.6 0.1 260/.15),oklch(0.6 0.1 260/.15)),var(--photo);
background-size: cover;
background-position: center;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
clip-path: circle(0% at 50% 55%);
transition: clip-path .7s cubic-bezier(.3,.85,.25,1);
}
.cth-30__bloom:hover .cth-30__clone--bloom,
.cth-30__bloom:focus-visible .cth-30__clone--bloom {
clip-path: circle(75% at 50% 55%);
}
.cth-30__clone--blinds {
color: var(--acc);
clip-path: polygon(0% 100%,0% 0%,0% 0%,0% 100%,33.3% 100%,33.3% 0%,33.3% 0%,33.3% 100%,66.6% 100%,66.6% 0%,66.6% 0%,66.6% 100%);
transition: clip-path .6s cubic-bezier(.6,0,.25,1);
}
.cth-30__blinds:hover .cth-30__clone--blinds,
.cth-30__blinds:focus-visible .cth-30__clone--blinds {
clip-path: polygon(0% 100%,0% 0%,33.3% 0%,33.3% 100%,33.3% 100%,33.3% 0%,66.6% 0%,66.6% 100%,66.6% 100%,66.6% 0%,100% 0%,100% 100%);
}
.cth-30__hint {
font-size: 12.5px;
line-height: 1.7;
color: var(--mut);
max-width: 62ch;
}
@media (prefers-reduced-motion: reduce) {
.cth-30 * {
transition-duration: .01ms !important;
}
}.cth-30,
.cth-30 *,
.cth-30 *::before,
.cth-30 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cth-30 {
--bg: oklch(0.2 0.012 285);
--ink: oklch(0.94 0.008 280);
--mut: oklch(0.62 0.02 285);
--acc: oklch(0.75 0.17 65);
--line: oklch(0.34 0.02 285);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
}
.cth-30__stage {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
background: radial-gradient(90% 75% at 50% 15%,oklch(0.24 0.016 290),var(--bg) 72%);
padding: clamp(20px,4vw,56px);
container: cth30/inline-size;
}
.cth-30__scene {
width: min(100%,780px);
display: flex;
flex-direction: column;
gap: clamp(16px,3cqi,26px);
}
.cth-30__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
flex-wrap: wrap;
padding-bottom: 14px;
border-bottom: 1px solid var(--line);
}
.cth-30__eyebrow {
font-size: 12px;
font-weight: 700;
letter-spacing: .22em;
text-transform: uppercase;
color: var(--mut);
}
.cth-30__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--acc);
border: 1px solid color-mix(in oklch,var(--acc) 40%,transparent);
border-radius: 99px;
padding: 5px 11px;
}
.cth-30__rows {
display: flex;
flex-direction: column;
}
.cth-30__row {
display: flex;
flex-direction: column;
gap: 6px;
padding: clamp(14px,2.6cqi,22px) 4px;
border-bottom: 1px solid var(--line);
text-decoration: none;
color: inherit;
}
.cth-30__row:focus-visible {
outline: 2px solid var(--acc);
outline-offset: -2px;
border-radius: 6px;
}
.cth-30__tag {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 10.5px;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--mut);
}
.cth-30__stackword {
position: relative;
display: inline-block;
width: fit-content;
font-size: clamp(38px,7.6cqi,74px);
font-weight: 900;
letter-spacing: .01em;
line-height: 1.08;
}
.cth-30__base {
display: block;
color: color-mix(in oklch,var(--ink) 34%,transparent);
}
.cth-30__clone {
position: absolute;
inset: 0;
display: block;
pointer-events: none;
}
.cth-30__clone--wipe {
color: var(--acc);
clip-path: polygon(-15% 0%,0% 0%,-15% 100%,-30% 100%);
transition: clip-path .65s cubic-bezier(.7,0,.2,1);
}
.cth-30__wipe:hover .cth-30__clone--wipe,
.cth-30__wipe:focus-visible .cth-30__clone--wipe {
clip-path: polygon(0% 0%,130% 0%,115% 100%,-15% 100%);
}
.cth-30__clone--bloom {
background-image: linear-gradient(oklch(0.6 0.1 260/.15),oklch(0.6 0.1 260/.15)),var(--photo);
background-size: cover;
background-position: center;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
clip-path: circle(0% at 50% 55%);
transition: clip-path .7s cubic-bezier(.3,.85,.25,1);
}
.cth-30__bloom:hover .cth-30__clone--bloom,
.cth-30__bloom:focus-visible .cth-30__clone--bloom {
clip-path: circle(75% at 50% 55%);
}
.cth-30__clone--blinds {
color: var(--acc);
clip-path: polygon(0% 100%,0% 0%,0% 0%,0% 100%,33.3% 100%,33.3% 0%,33.3% 0%,33.3% 100%,66.6% 100%,66.6% 0%,66.6% 0%,66.6% 100%);
transition: clip-path .6s cubic-bezier(.6,0,.25,1);
}
.cth-30__blinds:hover .cth-30__clone--blinds,
.cth-30__blinds:focus-visible .cth-30__clone--blinds {
clip-path: polygon(0% 100%,0% 0%,33.3% 0%,33.3% 100%,33.3% 100%,33.3% 0%,66.6% 0%,66.6% 100%,66.6% 100%,66.6% 0%,100% 0%,100% 100%);
}
.cth-30__hint {
font-size: 12.5px;
line-height: 1.7;
color: var(--mut);
max-width: 62ch;
}
@media (prefers-reduced-motion: reduce) {
.cth-30 * {
transition-duration: .01ms !important;
}
}Here's a working CSS Text Hover Effect from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Mask Clip Path Reveal
Source: https://codefronts.com/motion/css-text-hover-effects/mask-clip-path-reveal/
The geometry chapter that closes the collection: three reveal shapes on one board, all riding clip-path's native interpolation. A DIAGONAL WIPE — an accent-colored clone clipped to a 4-point polygon that sweeps corner-to-corner (the parallelogram's leading edge stays angled mid-flight). A CIRCLE BLOOM — a photograph clipped inside the glyphs (background-clip:text) revealed by circle(0%) growing to circle(75%). And BLINDS — a 12-point polygon whose three zero-width slats grow open into thirds of the word. The one law under all three: shapes interpolate only between the SAME function with the SAME point count.
## HTML
```html
<section class="cth-30" aria-label="Clip path reveal demo">
<div class="cth-30__stage">
<div class="cth-30__scene">
<header class="cth-30__head">
<p class="cth-30__eyebrow">Studio Meridian — title sequences & type systems</p>
<span class="cth-30__chip">same shape, same points → it tweens</span>
</header>
<div class="cth-30__rows">
<a class="cth-30__row cth-30__wipe" href="#cth30-top" id="cth30-top">
<span class="cth-30__tag">01 · diagonal wipe · polygon()</span>
<span class="cth-30__stackword"><span class="cth-30__base">SEQUENCE</span><span class="cth-30__clone cth-30__clone--wipe" aria-hidden="true">SEQUENCE</span></span>
</a>
<a class="cth-30__row cth-30__bloom" href="#cth30-top">
<span class="cth-30__tag">02 · circle bloom · circle() + photo-in-text</span>
<span class="cth-30__stackword"><span class="cth-30__base">HORIZON</span><span class="cth-30__clone cth-30__clone--bloom" aria-hidden="true" style="--photo:url('https://images.unsplash.com/photo-1506905925346-21bda4d32df4?q=80&w=1400&auto=format&fit=crop')">HORIZON</span></span>
</a>
<a class="cth-30__row cth-30__blinds" href="#cth30-top">
<span class="cth-30__tag">03 · blinds · 12-point polygon</span>
<span class="cth-30__stackword"><span class="cth-30__base">INTERVAL</span><span class="cth-30__clone cth-30__clone--blinds" aria-hidden="true">INTERVAL</span></span>
</a>
</div>
<p class="cth-30__hint">Hover each row. Three shapes, one law: clip-path only interpolates between the same function with the same point count — design the two states as siblings first.</p>
</div>
</div>
</section>
```
## CSS
```css
.cth-30,
.cth-30 *,
.cth-30 *::before,
.cth-30 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cth-30 {
--bg: oklch(0.2 0.012 285);
--ink: oklch(0.94 0.008 280);
--mut: oklch(0.62 0.02 285);
--acc: oklch(0.75 0.17 65);
--line: oklch(0.34 0.02 285);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
}
.cth-30__stage {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
background: radial-gradient(90% 75% at 50% 15%,oklch(0.24 0.016 290),var(--bg) 72%);
padding: clamp(20px,4vw,56px);
container: cth30/inline-size;
}
.cth-30__scene {
width: min(100%,780px);
display: flex;
flex-direction: column;
gap: clamp(16px,3cqi,26px);
}
.cth-30__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
flex-wrap: wrap;
padding-bottom: 14px;
border-bottom: 1px solid var(--line);
}
.cth-30__eyebrow {
font-size: 12px;
font-weight: 700;
letter-spacing: .22em;
text-transform: uppercase;
color: var(--mut);
}
.cth-30__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--acc);
border: 1px solid color-mix(in oklch,var(--acc) 40%,transparent);
border-radius: 99px;
padding: 5px 11px;
}
.cth-30__rows {
display: flex;
flex-direction: column;
}
.cth-30__row {
display: flex;
flex-direction: column;
gap: 6px;
padding: clamp(14px,2.6cqi,22px) 4px;
border-bottom: 1px solid var(--line);
text-decoration: none;
color: inherit;
}
.cth-30__row:focus-visible {
outline: 2px solid var(--acc);
outline-offset: -2px;
border-radius: 6px;
}
.cth-30__tag {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 10.5px;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--mut);
}
.cth-30__stackword {
position: relative;
display: inline-block;
width: fit-content;
font-size: clamp(38px,7.6cqi,74px);
font-weight: 900;
letter-spacing: .01em;
line-height: 1.08;
}
.cth-30__base {
display: block;
color: color-mix(in oklch,var(--ink) 34%,transparent);
}
.cth-30__clone {
position: absolute;
inset: 0;
display: block;
pointer-events: none;
}
.cth-30__clone--wipe {
color: var(--acc);
clip-path: polygon(-15% 0%,0% 0%,-15% 100%,-30% 100%);
transition: clip-path .65s cubic-bezier(.7,0,.2,1);
}
.cth-30__wipe:hover .cth-30__clone--wipe,
.cth-30__wipe:focus-visible .cth-30__clone--wipe {
clip-path: polygon(0% 0%,130% 0%,115% 100%,-15% 100%);
}
.cth-30__clone--bloom {
background-image: linear-gradient(oklch(0.6 0.1 260/.15),oklch(0.6 0.1 260/.15)),var(--photo);
background-size: cover;
background-position: center;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
clip-path: circle(0% at 50% 55%);
transition: clip-path .7s cubic-bezier(.3,.85,.25,1);
}
.cth-30__bloom:hover .cth-30__clone--bloom,
.cth-30__bloom:focus-visible .cth-30__clone--bloom {
clip-path: circle(75% at 50% 55%);
}
.cth-30__clone--blinds {
color: var(--acc);
clip-path: polygon(0% 100%,0% 0%,0% 0%,0% 100%,33.3% 100%,33.3% 0%,33.3% 0%,33.3% 100%,66.6% 100%,66.6% 0%,66.6% 0%,66.6% 100%);
transition: clip-path .6s cubic-bezier(.6,0,.25,1);
}
.cth-30__blinds:hover .cth-30__clone--blinds,
.cth-30__blinds:focus-visible .cth-30__clone--blinds {
clip-path: polygon(0% 100%,0% 0%,33.3% 0%,33.3% 100%,33.3% 100%,33.3% 0%,66.6% 0%,66.6% 100%,66.6% 100%,66.6% 0%,100% 0%,100% 100%);
}
.cth-30__hint {
font-size: 12.5px;
line-height: 1.7;
color: var(--mut);
max-width: 62ch;
}
@media (prefers-reduced-motion: reduce) {
.cth-30 * {
transition-duration: .01ms !important;
}
}
```Here's a working CSS Text Hover Effect from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Mask Clip Path Reveal
Source: https://codefronts.com/motion/css-text-hover-effects/mask-clip-path-reveal/
The geometry chapter that closes the collection: three reveal shapes on one board, all riding clip-path's native interpolation. A DIAGONAL WIPE — an accent-colored clone clipped to a 4-point polygon that sweeps corner-to-corner (the parallelogram's leading edge stays angled mid-flight). A CIRCLE BLOOM — a photograph clipped inside the glyphs (background-clip:text) revealed by circle(0%) growing to circle(75%). And BLINDS — a 12-point polygon whose three zero-width slats grow open into thirds of the word. The one law under all three: shapes interpolate only between the SAME function with the SAME point count.
## HTML
```html
<section class="cth-30" aria-label="Clip path reveal demo">
<div class="cth-30__stage">
<div class="cth-30__scene">
<header class="cth-30__head">
<p class="cth-30__eyebrow">Studio Meridian — title sequences & type systems</p>
<span class="cth-30__chip">same shape, same points → it tweens</span>
</header>
<div class="cth-30__rows">
<a class="cth-30__row cth-30__wipe" href="#cth30-top" id="cth30-top">
<span class="cth-30__tag">01 · diagonal wipe · polygon()</span>
<span class="cth-30__stackword"><span class="cth-30__base">SEQUENCE</span><span class="cth-30__clone cth-30__clone--wipe" aria-hidden="true">SEQUENCE</span></span>
</a>
<a class="cth-30__row cth-30__bloom" href="#cth30-top">
<span class="cth-30__tag">02 · circle bloom · circle() + photo-in-text</span>
<span class="cth-30__stackword"><span class="cth-30__base">HORIZON</span><span class="cth-30__clone cth-30__clone--bloom" aria-hidden="true" style="--photo:url('https://images.unsplash.com/photo-1506905925346-21bda4d32df4?q=80&w=1400&auto=format&fit=crop')">HORIZON</span></span>
</a>
<a class="cth-30__row cth-30__blinds" href="#cth30-top">
<span class="cth-30__tag">03 · blinds · 12-point polygon</span>
<span class="cth-30__stackword"><span class="cth-30__base">INTERVAL</span><span class="cth-30__clone cth-30__clone--blinds" aria-hidden="true">INTERVAL</span></span>
</a>
</div>
<p class="cth-30__hint">Hover each row. Three shapes, one law: clip-path only interpolates between the same function with the same point count — design the two states as siblings first.</p>
</div>
</div>
</section>
```
## CSS
```css
.cth-30,
.cth-30 *,
.cth-30 *::before,
.cth-30 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cth-30 {
--bg: oklch(0.2 0.012 285);
--ink: oklch(0.94 0.008 280);
--mut: oklch(0.62 0.02 285);
--acc: oklch(0.75 0.17 65);
--line: oklch(0.34 0.02 285);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
}
.cth-30__stage {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
background: radial-gradient(90% 75% at 50% 15%,oklch(0.24 0.016 290),var(--bg) 72%);
padding: clamp(20px,4vw,56px);
container: cth30/inline-size;
}
.cth-30__scene {
width: min(100%,780px);
display: flex;
flex-direction: column;
gap: clamp(16px,3cqi,26px);
}
.cth-30__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
flex-wrap: wrap;
padding-bottom: 14px;
border-bottom: 1px solid var(--line);
}
.cth-30__eyebrow {
font-size: 12px;
font-weight: 700;
letter-spacing: .22em;
text-transform: uppercase;
color: var(--mut);
}
.cth-30__chip {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--acc);
border: 1px solid color-mix(in oklch,var(--acc) 40%,transparent);
border-radius: 99px;
padding: 5px 11px;
}
.cth-30__rows {
display: flex;
flex-direction: column;
}
.cth-30__row {
display: flex;
flex-direction: column;
gap: 6px;
padding: clamp(14px,2.6cqi,22px) 4px;
border-bottom: 1px solid var(--line);
text-decoration: none;
color: inherit;
}
.cth-30__row:focus-visible {
outline: 2px solid var(--acc);
outline-offset: -2px;
border-radius: 6px;
}
.cth-30__tag {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 10.5px;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--mut);
}
.cth-30__stackword {
position: relative;
display: inline-block;
width: fit-content;
font-size: clamp(38px,7.6cqi,74px);
font-weight: 900;
letter-spacing: .01em;
line-height: 1.08;
}
.cth-30__base {
display: block;
color: color-mix(in oklch,var(--ink) 34%,transparent);
}
.cth-30__clone {
position: absolute;
inset: 0;
display: block;
pointer-events: none;
}
.cth-30__clone--wipe {
color: var(--acc);
clip-path: polygon(-15% 0%,0% 0%,-15% 100%,-30% 100%);
transition: clip-path .65s cubic-bezier(.7,0,.2,1);
}
.cth-30__wipe:hover .cth-30__clone--wipe,
.cth-30__wipe:focus-visible .cth-30__clone--wipe {
clip-path: polygon(0% 0%,130% 0%,115% 100%,-15% 100%);
}
.cth-30__clone--bloom {
background-image: linear-gradient(oklch(0.6 0.1 260/.15),oklch(0.6 0.1 260/.15)),var(--photo);
background-size: cover;
background-position: center;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
clip-path: circle(0% at 50% 55%);
transition: clip-path .7s cubic-bezier(.3,.85,.25,1);
}
.cth-30__bloom:hover .cth-30__clone--bloom,
.cth-30__bloom:focus-visible .cth-30__clone--bloom {
clip-path: circle(75% at 50% 55%);
}
.cth-30__clone--blinds {
color: var(--acc);
clip-path: polygon(0% 100%,0% 0%,0% 0%,0% 100%,33.3% 100%,33.3% 0%,33.3% 0%,33.3% 100%,66.6% 100%,66.6% 0%,66.6% 0%,66.6% 100%);
transition: clip-path .6s cubic-bezier(.6,0,.25,1);
}
.cth-30__blinds:hover .cth-30__clone--blinds,
.cth-30__blinds:focus-visible .cth-30__clone--blinds {
clip-path: polygon(0% 100%,0% 0%,33.3% 0%,33.3% 100%,33.3% 100%,33.3% 0%,66.6% 0%,66.6% 100%,66.6% 100%,66.6% 0%,100% 0%,100% 100%);
}
.cth-30__hint {
font-size: 12.5px;
line-height: 1.7;
color: var(--mut);
max-width: 62ch;
}
@media (prefers-reduced-motion: reduce) {
.cth-30 * {
transition-duration: .01ms !important;
}
}
```How this works
Every variant is the two-layer grammar from demos 02/27 — base text + absolutely-stacked clone — with clip-path as the curtain. The wipe:
.wipe .clone{
clip-path:polygon(-15% 0, 0 0, -15% 100%, -30% 100%);
transition:clip-path .65s cubic-bezier(.7,0,.2,1); }
.wipe:hover .clone{
clip-path:polygon(0 0, 130% 0, 115% 100%, -15% 100%); }Both states are 4-point polygons, so the browser tweens vertex-to-vertex: the quad starts as a sliver parked off the left edge (negative coordinates are legal and load-bearing — they let the shape ENTER from outside) and stretches across; because its right edge leans -15%, the reveal front is a moving diagonal, which reads as a wipe, not a resize. The bloom is even smaller: circle(0% at 50% 55%) → circle(75% at 50% 55%) — same function, one radius interpolating — over a clone whose paint is a photo clipped INTO the letters, so the image blossoms through the typography from its center.
The interpolation law explains every clip-path animation that ever 'just snapped': polygon↔circle never tweens; a 5-point star to a 4-point quad never tweens; inset()↔inset() always does (demo 27 relies on that). Design the two states as the same shape family FIRST, then art-direct the coordinates. The blinds prove the law scales — twelve points forming three slats (four per slat, bridged along the bottom edge so the connectors enclose zero area), zero-width at rest, each sliding open in the same transition; midway the word exists as three growing stripes of itself.
Make it yours
- Wipe angle: the -15% lean on the quad's right edge is the blade angle — 0 is a hard vertical wipe (news-ticker feel), -30% is a dramatic slash (sports broadcast).
- Bloom origin: circle's 'at' position is where the reveal is born — set it from JS to the click point (two custom properties, demo 11's pattern) and taps detonate the reveal where the finger lands.
- Blinds count: more slats = more points (n slats = 4n points: each slat is its own traced rectangle, with all bridge travel collinear on one edge so it encloses nothing); keep both states' counts identical. Odd counts read industrial, many thin slats read like a scan.
- Chain reveals: transition-delay across the three rows (0 / 120ms / 240ms) when triggered by a shared parent :hover turns the board into a title sequence — every variant already shares one transition grammar.
Gotchas — read before shipping
- Same function + same point count or NO animation — the shape jumps. This is the single most-hit wall in clip-path work; both keyframe states must be structurally identical.
- clip-path clips PAINT, not layout — the hidden clone still occupies space and still captures pointer events unless you inset/disable it; the stacked-clone pattern needs pointer-events:none on the overlay.
- Coordinates beyond 0–100% are valid and essential for enter-from-outside choreography — but remember they're relative to the element's box, so generous padding keeps fast wipes from visibly 'starting inside' the glyphs.
- The photo-in-text bloom needs BOTH prefixed background-clip declarations (demo 03's rule) AND the clip-path — they compose fine, but miss the prefix and you reveal a photo rectangle, not photo letters.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 113+ | 111+ |
Animatable clip-path basic shapes are Chrome 55+/Safari 13.1+/Firefox 54+; background-clip:text needs its prefix everywhere. Floor reflects oklch() tokens. Pure CSS throughout.