21 / 30
All 30 in 30 CSS Text Hover Effects
- 01CSS Animated Underline on Hover
- 02CSS Text Fill / Liquid Fill Effect
- 03CSS Text Color Reveal / Background Clip
- 04CSS Strike-Through Hover Effect
- 05Text Swap / Flip Up Effect
- 06Glitch Text Effect
- 07Neon Glow / Flicker Text Effect
- 08Shimmer / Metallic Shine Effect
- 093D Pop / Lift Effect
- 10Split Letter / Staggered Wave Effect
- 11Mask / Spotlight Text Reveal
- 12Reveal Hidden Text / Spoiler Reveal
- 13Blur to Focus Text Effect
- 14Magnetic / Cursor-Tracking Text
- 15Letter Spacing Expansion (Tracking / Kerning)
- 16Typography Outline / Stroke Fill
- 17Typography Scramble / Matrix Effect
- 18Animated Border / Encased Text Hover
- 19Elastic / Rubber Band Bounce
- 20Variable Font Weight Shift
- 21Rainbow Gradient Flow
- 22Rotational / Perspective Tilt Text
- 23Smoke / Dissolve Particle Effect
- 24Fire / Heat Wave Glow Text
- 25Text Shadow Direction Movement
- 26Text Marquee Speed-Up on Hover
- 27Duotone / Split Color Split Text
- 28Tooltip Text Reveal
- 29Distorted Wave / Ripple Effect
- 30Mask Clip Path Reveal
Rainbow Gradient Flow
A rainbow with engineering in it: instead of hand-picking seven stops, the gradient is TWO colors and one instruction — linear-gradient(90deg in oklch longer hue, …) — telling the browser to travel the long way around the hue wheel, generating the entire spectrum at perceptually even brightness (no muddy dark cyan, no blinding yellow spike like sRGB rainbows). At rest the headline is quiet ink; hover crossfades it out (demo 03's color→transparent trick) while background-position flows the spectrum through the glyphs on a seamless 300%-wide loop.
Build UI like this from scratch: Flexbox, Grid, animations & responsive CSS
ZTM · The CSS Bootcamp · 30‑day money-back guarantee · Affiliate link
The code
<section class="cth-21" aria-label="Rainbow gradient flow demo">
<div class="cth-21__stage">
<div class="cth-21__scene">
<p class="cth-21__eyebrow">Brighton Pride — March 7, 2026</p>
<h1 class="cth-21__title"><a class="cth-21__rainbow" href="#cth21-top" id="cth21-top">EVERY COLOUR<br>OUT LOUD</a></h1>
<p class="cth-21__sub">Two oklch stops and <code>longer hue</code> generate the whole spectrum — evenly bright, endlessly looping, only when asked.</p>
<div class="cth-21__row">
<a class="cth-21__cta" href="#cth21-top">March with us</a>
<a class="cth-21__ghost" href="#cth21-top">Route map</a>
</div>
<p class="cth-21__hint">Hover the headline or the button. The loop is seamless because travel (-300%) equals the canvas width (300%).</p>
</div>
</div>
</section><section class="cth-21" aria-label="Rainbow gradient flow demo">
<div class="cth-21__stage">
<div class="cth-21__scene">
<p class="cth-21__eyebrow">Brighton Pride — March 7, 2026</p>
<h1 class="cth-21__title"><a class="cth-21__rainbow" href="#cth21-top" id="cth21-top">EVERY COLOUR<br>OUT LOUD</a></h1>
<p class="cth-21__sub">Two oklch stops and <code>longer hue</code> generate the whole spectrum — evenly bright, endlessly looping, only when asked.</p>
<div class="cth-21__row">
<a class="cth-21__cta" href="#cth21-top">March with us</a>
<a class="cth-21__ghost" href="#cth21-top">Route map</a>
</div>
<p class="cth-21__hint">Hover the headline or the button. The loop is seamless because travel (-300%) equals the canvas width (300%).</p>
</div>
</div>
</section>.cth-21,
.cth-21 *,
.cth-21 *::before,
.cth-21 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cth-21 {
--bg: oklch(0.98 0.004 90);
--ink: oklch(0.2 0.01 280);
--mut: oklch(0.5 0.015 280);
--line: oklch(0.9 0.006 280);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
}
.cth-21__stage {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
background: var(--bg);
padding: clamp(20px,4vw,56px);
container: cth21/inline-size;
}
.cth-21__scene {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: clamp(16px,3cqi,26px);
}
.cth-21__eyebrow {
font-size: 12.5px;
font-weight: 800;
letter-spacing: .24em;
text-transform: uppercase;
color: var(--mut);
}
.cth-21__title {
line-height: 1;
}
.cth-21__rainbow {
display: inline-block;
text-decoration: none;
font-size: clamp(46px,10.5cqi,108px);
font-weight: 900;
letter-spacing: -.03em;
line-height: 1.02;
padding: .06em .08em;
background-image: linear-gradient(90deg,#e40303,#ff8c00,#ffd700,#00a86b,#0057ff,#8a2be2,#e40303);
background-image: linear-gradient(90deg in oklch longer hue,oklch(0.7 0.19 0),oklch(0.7 0.19 350));
background-size: 300% 100%;
background-position: 0% 0;
-webkit-background-clip: text;
background-clip: text;
color: var(--ink);
transition: color .45s ease;
}
.cth-21__rainbow:hover,
.cth-21__rainbow:focus-visible {
color: transparent;
animation: cth21-flow 3.5s linear infinite;
}
.cth-21__rainbow:focus-visible {
outline: 3px solid oklch(0.7 0.19 300);
outline-offset: 8px;
border-radius: 8px;
}
@keyframes cth21-flow {
to {
background-position: -300% 0;
}
}
.cth-21__sub {
font-size: 15px;
line-height: 1.7;
color: var(--mut);
max-width: 52ch;
}
.cth-21__sub code {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: .9em;
background: oklch(0.93 0.005 280);
padding: 1px 6px;
border-radius: 4px;
}
.cth-21__row {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 14px;
margin-top: 6px;
}
.cth-21__cta {
display: grid;
place-items: center;
min-height: 56px;
padding: 0 32px;
border-radius: 99px;
font-size: 16px;
font-weight: 850;
text-decoration: none;
color: oklch(0.99 0.002 90);
background-image: linear-gradient(90deg,#e40303,#ff8c00,#00a86b,#0057ff,#8a2be2,#e40303);
background-image: linear-gradient(90deg in oklch longer hue,oklch(0.55 0.19 0),oklch(0.55 0.19 350));
background-size: 300% 100%;
box-shadow: 0 10px 30px oklch(0.5 0.15 320/.25);
transition: box-shadow .3s ease,translate .3s ease;
}
.cth-21__cta:hover,
.cth-21__cta:focus-visible {
animation: cth21-flow 2.6s linear infinite;
translate: 0 -2px;
box-shadow: 0 16px 40px oklch(0.5 0.15 320/.35);
}
.cth-21__cta:focus-visible {
outline: 3px solid var(--ink);
outline-offset: 3px;
}
.cth-21__ghost {
display: grid;
place-items: center;
min-height: 56px;
padding: 0 28px;
border-radius: 99px;
border: 1.5px solid color-mix(in oklch,var(--ink) 32%,transparent);
font-size: 16px;
font-weight: 750;
text-decoration: none;
color: var(--ink);
transition: border-color .3s ease;
}
.cth-21__ghost:hover,
.cth-21__ghost:focus-visible {
border-color: var(--ink);
}
.cth-21__ghost:focus-visible {
outline: 3px solid oklch(0.7 0.19 300);
outline-offset: 3px;
}
.cth-21__hint {
font-size: 12.5px;
color: var(--mut);
max-width: 54ch;
line-height: 1.7;
}
@media (prefers-reduced-motion: reduce) {
.cth-21 * {
animation: none !important;
transition-duration: .25s !important;
}
.cth-21__rainbow:hover,
.cth-21__rainbow:focus-visible {
color: transparent;
}
}.cth-21,
.cth-21 *,
.cth-21 *::before,
.cth-21 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cth-21 {
--bg: oklch(0.98 0.004 90);
--ink: oklch(0.2 0.01 280);
--mut: oklch(0.5 0.015 280);
--line: oklch(0.9 0.006 280);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
}
.cth-21__stage {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
background: var(--bg);
padding: clamp(20px,4vw,56px);
container: cth21/inline-size;
}
.cth-21__scene {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: clamp(16px,3cqi,26px);
}
.cth-21__eyebrow {
font-size: 12.5px;
font-weight: 800;
letter-spacing: .24em;
text-transform: uppercase;
color: var(--mut);
}
.cth-21__title {
line-height: 1;
}
.cth-21__rainbow {
display: inline-block;
text-decoration: none;
font-size: clamp(46px,10.5cqi,108px);
font-weight: 900;
letter-spacing: -.03em;
line-height: 1.02;
padding: .06em .08em;
background-image: linear-gradient(90deg,#e40303,#ff8c00,#ffd700,#00a86b,#0057ff,#8a2be2,#e40303);
background-image: linear-gradient(90deg in oklch longer hue,oklch(0.7 0.19 0),oklch(0.7 0.19 350));
background-size: 300% 100%;
background-position: 0% 0;
-webkit-background-clip: text;
background-clip: text;
color: var(--ink);
transition: color .45s ease;
}
.cth-21__rainbow:hover,
.cth-21__rainbow:focus-visible {
color: transparent;
animation: cth21-flow 3.5s linear infinite;
}
.cth-21__rainbow:focus-visible {
outline: 3px solid oklch(0.7 0.19 300);
outline-offset: 8px;
border-radius: 8px;
}
@keyframes cth21-flow {
to {
background-position: -300% 0;
}
}
.cth-21__sub {
font-size: 15px;
line-height: 1.7;
color: var(--mut);
max-width: 52ch;
}
.cth-21__sub code {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: .9em;
background: oklch(0.93 0.005 280);
padding: 1px 6px;
border-radius: 4px;
}
.cth-21__row {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 14px;
margin-top: 6px;
}
.cth-21__cta {
display: grid;
place-items: center;
min-height: 56px;
padding: 0 32px;
border-radius: 99px;
font-size: 16px;
font-weight: 850;
text-decoration: none;
color: oklch(0.99 0.002 90);
background-image: linear-gradient(90deg,#e40303,#ff8c00,#00a86b,#0057ff,#8a2be2,#e40303);
background-image: linear-gradient(90deg in oklch longer hue,oklch(0.55 0.19 0),oklch(0.55 0.19 350));
background-size: 300% 100%;
box-shadow: 0 10px 30px oklch(0.5 0.15 320/.25);
transition: box-shadow .3s ease,translate .3s ease;
}
.cth-21__cta:hover,
.cth-21__cta:focus-visible {
animation: cth21-flow 2.6s linear infinite;
translate: 0 -2px;
box-shadow: 0 16px 40px oklch(0.5 0.15 320/.35);
}
.cth-21__cta:focus-visible {
outline: 3px solid var(--ink);
outline-offset: 3px;
}
.cth-21__ghost {
display: grid;
place-items: center;
min-height: 56px;
padding: 0 28px;
border-radius: 99px;
border: 1.5px solid color-mix(in oklch,var(--ink) 32%,transparent);
font-size: 16px;
font-weight: 750;
text-decoration: none;
color: var(--ink);
transition: border-color .3s ease;
}
.cth-21__ghost:hover,
.cth-21__ghost:focus-visible {
border-color: var(--ink);
}
.cth-21__ghost:focus-visible {
outline: 3px solid oklch(0.7 0.19 300);
outline-offset: 3px;
}
.cth-21__hint {
font-size: 12.5px;
color: var(--mut);
max-width: 54ch;
line-height: 1.7;
}
@media (prefers-reduced-motion: reduce) {
.cth-21 * {
animation: none !important;
transition-duration: .25s !important;
}
.cth-21__rainbow:hover,
.cth-21__rainbow:focus-visible {
color: transparent;
}
}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: Rainbow Gradient Flow
Source: https://codefronts.com/motion/css-text-hover-effects/rainbow-gradient-flow/
A rainbow with engineering in it: instead of hand-picking seven stops, the gradient is TWO colors and one instruction — linear-gradient(90deg in oklch longer hue, …) — telling the browser to travel the long way around the hue wheel, generating the entire spectrum at perceptually even brightness (no muddy dark cyan, no blinding yellow spike like sRGB rainbows). At rest the headline is quiet ink; hover crossfades it out (demo 03's color→transparent trick) while background-position flows the spectrum through the glyphs on a seamless 300%-wide loop.
## HTML
```html
<section class="cth-21" aria-label="Rainbow gradient flow demo">
<div class="cth-21__stage">
<div class="cth-21__scene">
<p class="cth-21__eyebrow">Brighton Pride — March 7, 2026</p>
<h1 class="cth-21__title"><a class="cth-21__rainbow" href="#cth21-top" id="cth21-top">EVERY COLOUR<br>OUT LOUD</a></h1>
<p class="cth-21__sub">Two oklch stops and <code>longer hue</code> generate the whole spectrum — evenly bright, endlessly looping, only when asked.</p>
<div class="cth-21__row">
<a class="cth-21__cta" href="#cth21-top">March with us</a>
<a class="cth-21__ghost" href="#cth21-top">Route map</a>
</div>
<p class="cth-21__hint">Hover the headline or the button. The loop is seamless because travel (-300%) equals the canvas width (300%).</p>
</div>
</div>
</section>
```
## CSS
```css
.cth-21,
.cth-21 *,
.cth-21 *::before,
.cth-21 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cth-21 {
--bg: oklch(0.98 0.004 90);
--ink: oklch(0.2 0.01 280);
--mut: oklch(0.5 0.015 280);
--line: oklch(0.9 0.006 280);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
}
.cth-21__stage {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
background: var(--bg);
padding: clamp(20px,4vw,56px);
container: cth21/inline-size;
}
.cth-21__scene {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: clamp(16px,3cqi,26px);
}
.cth-21__eyebrow {
font-size: 12.5px;
font-weight: 800;
letter-spacing: .24em;
text-transform: uppercase;
color: var(--mut);
}
.cth-21__title {
line-height: 1;
}
.cth-21__rainbow {
display: inline-block;
text-decoration: none;
font-size: clamp(46px,10.5cqi,108px);
font-weight: 900;
letter-spacing: -.03em;
line-height: 1.02;
padding: .06em .08em;
background-image: linear-gradient(90deg,#e40303,#ff8c00,#ffd700,#00a86b,#0057ff,#8a2be2,#e40303);
background-image: linear-gradient(90deg in oklch longer hue,oklch(0.7 0.19 0),oklch(0.7 0.19 350));
background-size: 300% 100%;
background-position: 0% 0;
-webkit-background-clip: text;
background-clip: text;
color: var(--ink);
transition: color .45s ease;
}
.cth-21__rainbow:hover,
.cth-21__rainbow:focus-visible {
color: transparent;
animation: cth21-flow 3.5s linear infinite;
}
.cth-21__rainbow:focus-visible {
outline: 3px solid oklch(0.7 0.19 300);
outline-offset: 8px;
border-radius: 8px;
}
@keyframes cth21-flow {
to {
background-position: -300% 0;
}
}
.cth-21__sub {
font-size: 15px;
line-height: 1.7;
color: var(--mut);
max-width: 52ch;
}
.cth-21__sub code {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: .9em;
background: oklch(0.93 0.005 280);
padding: 1px 6px;
border-radius: 4px;
}
.cth-21__row {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 14px;
margin-top: 6px;
}
.cth-21__cta {
display: grid;
place-items: center;
min-height: 56px;
padding: 0 32px;
border-radius: 99px;
font-size: 16px;
font-weight: 850;
text-decoration: none;
color: oklch(0.99 0.002 90);
background-image: linear-gradient(90deg,#e40303,#ff8c00,#00a86b,#0057ff,#8a2be2,#e40303);
background-image: linear-gradient(90deg in oklch longer hue,oklch(0.55 0.19 0),oklch(0.55 0.19 350));
background-size: 300% 100%;
box-shadow: 0 10px 30px oklch(0.5 0.15 320/.25);
transition: box-shadow .3s ease,translate .3s ease;
}
.cth-21__cta:hover,
.cth-21__cta:focus-visible {
animation: cth21-flow 2.6s linear infinite;
translate: 0 -2px;
box-shadow: 0 16px 40px oklch(0.5 0.15 320/.35);
}
.cth-21__cta:focus-visible {
outline: 3px solid var(--ink);
outline-offset: 3px;
}
.cth-21__ghost {
display: grid;
place-items: center;
min-height: 56px;
padding: 0 28px;
border-radius: 99px;
border: 1.5px solid color-mix(in oklch,var(--ink) 32%,transparent);
font-size: 16px;
font-weight: 750;
text-decoration: none;
color: var(--ink);
transition: border-color .3s ease;
}
.cth-21__ghost:hover,
.cth-21__ghost:focus-visible {
border-color: var(--ink);
}
.cth-21__ghost:focus-visible {
outline: 3px solid oklch(0.7 0.19 300);
outline-offset: 3px;
}
.cth-21__hint {
font-size: 12.5px;
color: var(--mut);
max-width: 54ch;
line-height: 1.7;
}
@media (prefers-reduced-motion: reduce) {
.cth-21 * {
animation: none !important;
transition-duration: .25s !important;
}
.cth-21__rainbow:hover,
.cth-21__rainbow:focus-visible {
color: transparent;
}
}
```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: Rainbow Gradient Flow
Source: https://codefronts.com/motion/css-text-hover-effects/rainbow-gradient-flow/
A rainbow with engineering in it: instead of hand-picking seven stops, the gradient is TWO colors and one instruction — linear-gradient(90deg in oklch longer hue, …) — telling the browser to travel the long way around the hue wheel, generating the entire spectrum at perceptually even brightness (no muddy dark cyan, no blinding yellow spike like sRGB rainbows). At rest the headline is quiet ink; hover crossfades it out (demo 03's color→transparent trick) while background-position flows the spectrum through the glyphs on a seamless 300%-wide loop.
## HTML
```html
<section class="cth-21" aria-label="Rainbow gradient flow demo">
<div class="cth-21__stage">
<div class="cth-21__scene">
<p class="cth-21__eyebrow">Brighton Pride — March 7, 2026</p>
<h1 class="cth-21__title"><a class="cth-21__rainbow" href="#cth21-top" id="cth21-top">EVERY COLOUR<br>OUT LOUD</a></h1>
<p class="cth-21__sub">Two oklch stops and <code>longer hue</code> generate the whole spectrum — evenly bright, endlessly looping, only when asked.</p>
<div class="cth-21__row">
<a class="cth-21__cta" href="#cth21-top">March with us</a>
<a class="cth-21__ghost" href="#cth21-top">Route map</a>
</div>
<p class="cth-21__hint">Hover the headline or the button. The loop is seamless because travel (-300%) equals the canvas width (300%).</p>
</div>
</div>
</section>
```
## CSS
```css
.cth-21,
.cth-21 *,
.cth-21 *::before,
.cth-21 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cth-21 {
--bg: oklch(0.98 0.004 90);
--ink: oklch(0.2 0.01 280);
--mut: oklch(0.5 0.015 280);
--line: oklch(0.9 0.006 280);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
}
.cth-21__stage {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
background: var(--bg);
padding: clamp(20px,4vw,56px);
container: cth21/inline-size;
}
.cth-21__scene {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: clamp(16px,3cqi,26px);
}
.cth-21__eyebrow {
font-size: 12.5px;
font-weight: 800;
letter-spacing: .24em;
text-transform: uppercase;
color: var(--mut);
}
.cth-21__title {
line-height: 1;
}
.cth-21__rainbow {
display: inline-block;
text-decoration: none;
font-size: clamp(46px,10.5cqi,108px);
font-weight: 900;
letter-spacing: -.03em;
line-height: 1.02;
padding: .06em .08em;
background-image: linear-gradient(90deg,#e40303,#ff8c00,#ffd700,#00a86b,#0057ff,#8a2be2,#e40303);
background-image: linear-gradient(90deg in oklch longer hue,oklch(0.7 0.19 0),oklch(0.7 0.19 350));
background-size: 300% 100%;
background-position: 0% 0;
-webkit-background-clip: text;
background-clip: text;
color: var(--ink);
transition: color .45s ease;
}
.cth-21__rainbow:hover,
.cth-21__rainbow:focus-visible {
color: transparent;
animation: cth21-flow 3.5s linear infinite;
}
.cth-21__rainbow:focus-visible {
outline: 3px solid oklch(0.7 0.19 300);
outline-offset: 8px;
border-radius: 8px;
}
@keyframes cth21-flow {
to {
background-position: -300% 0;
}
}
.cth-21__sub {
font-size: 15px;
line-height: 1.7;
color: var(--mut);
max-width: 52ch;
}
.cth-21__sub code {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: .9em;
background: oklch(0.93 0.005 280);
padding: 1px 6px;
border-radius: 4px;
}
.cth-21__row {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 14px;
margin-top: 6px;
}
.cth-21__cta {
display: grid;
place-items: center;
min-height: 56px;
padding: 0 32px;
border-radius: 99px;
font-size: 16px;
font-weight: 850;
text-decoration: none;
color: oklch(0.99 0.002 90);
background-image: linear-gradient(90deg,#e40303,#ff8c00,#00a86b,#0057ff,#8a2be2,#e40303);
background-image: linear-gradient(90deg in oklch longer hue,oklch(0.55 0.19 0),oklch(0.55 0.19 350));
background-size: 300% 100%;
box-shadow: 0 10px 30px oklch(0.5 0.15 320/.25);
transition: box-shadow .3s ease,translate .3s ease;
}
.cth-21__cta:hover,
.cth-21__cta:focus-visible {
animation: cth21-flow 2.6s linear infinite;
translate: 0 -2px;
box-shadow: 0 16px 40px oklch(0.5 0.15 320/.35);
}
.cth-21__cta:focus-visible {
outline: 3px solid var(--ink);
outline-offset: 3px;
}
.cth-21__ghost {
display: grid;
place-items: center;
min-height: 56px;
padding: 0 28px;
border-radius: 99px;
border: 1.5px solid color-mix(in oklch,var(--ink) 32%,transparent);
font-size: 16px;
font-weight: 750;
text-decoration: none;
color: var(--ink);
transition: border-color .3s ease;
}
.cth-21__ghost:hover,
.cth-21__ghost:focus-visible {
border-color: var(--ink);
}
.cth-21__ghost:focus-visible {
outline: 3px solid oklch(0.7 0.19 300);
outline-offset: 3px;
}
.cth-21__hint {
font-size: 12.5px;
color: var(--mut);
max-width: 54ch;
line-height: 1.7;
}
@media (prefers-reduced-motion: reduce) {
.cth-21 * {
animation: none !important;
transition-duration: .25s !important;
}
.cth-21__rainbow:hover,
.cth-21__rainbow:focus-visible {
color: transparent;
}
}
```How this works
The whole spectrum from a two-stop gradient — the interpolation-space syntax does the work:
.rainbow{
background-image:linear-gradient(90deg in oklch longer hue,
oklch(0.7 0.19 0), oklch(0.7 0.19 350));
background-size:300% 100%;
-webkit-background-clip:text; background-clip:text;
color:var(--ink);
transition:color .45s ease; }
.rainbow:hover{
color:transparent;
animation:cth21-flow 3.5s linear infinite; }
@keyframes cth21-flow{ to{ background-position:-300% 0; } }Unpack the gradient line: both stops are the same lightness (0.7) and chroma (0.19) in OKLCH, differing only in hue — 0 to 350 the SHORT way is a 10° hop, but longer hue forces the 350° route through orange, yellow, green, cyan, blue, violet. Because OKLCH holds perceptual lightness constant while hue rotates, every band of the rainbow carries equal visual weight — compare any sRGB seven-stop rainbow and you'll see its dim cyan trough immediately.
The loop never stutters because the travel distance equals a whole number of pattern repeats: the canvas is 300% wide and the keyframe moves exactly -300% — frame N of cycle two is pixel-identical to frame N of cycle one. The rest state is the restraint move: ink-colored text with the rainbow painted invisibly beneath (color covers clip), so the page isn't shouting until someone asks it to. Under prefers-reduced-motion the flow stops but the reveal still works — a static rainbow is inclusive; a strobing page is not.
Make it yours
- Softer spectrum: drop chroma to 0.12 for pastel pride; raise lightness to 0.8 for sorbet. One number each — that's the point of building in oklch.
- Partial arcs: 'in oklch longer hue' between hue 20 and hue 320 skips the greens for a sunset arc; 'shorter hue' between two brand hues gives an on-palette flow with identical code.
- Tempo: 3.5s is celebratory; 8s+ becomes ambient shimmer for headers that live on screen all day; sub-1s reads as alarm.
- The CTA shows the container version: same two-stop longer-hue gradient as background (no clip), text in white, flowing only on hover — one gradient vocabulary across text and surfaces.
Gotchas — read before shipping
- Seamlessness is math: travel distance must be a multiple of background-size width (here -300% on a 300% canvas). Any other pair produces a visible reset splice once per cycle.
- Gradient interpolation-space syntax ('in oklch longer hue') needs Chrome 111+/Safari 16.2+/Firefox 113+ — older engines treat the whole background-image as invalid, so ship an ordinary multi-stop rainbow first and let this line override where supported (progressive override shown in this demo's CSS).
- Infinite background-position animation on clipped text re-rasters per frame — budget like demo 03's spin: gate it behind hover, don't idle-flow every heading.
- Hue-wheel gradients pass THROUGH low-contrast bands (yellow on white, cyan on white) — keep flowing rainbows on large bold type; at body sizes parts of every word will fail contrast somewhere in the cycle.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 113+ | 111+ |
This is the one demo where the 2023 floor is structural: hue-interpolation gradients ARE the technique (Chrome 111/Safari 16.2/Firefox 113). The demo declares a classic multi-stop rainbow first as a working fallback for older engines.