30 CSS Text Hover Effects24 / 30
Fire / Heat Wave Glow Text
Type that catches fire the physically-plausible way: flames only burn UP, so the shadow stack builds vertically — white-yellow heat tight under the glyphs, orange above, red higher, smoke-dark at the top — and a three-frame flicker keyframe jitters those offsets so the fire licks instead of glows. A 0.5deg skew wobble on a fast alternate loop adds the heat-haze shimmer. At rest the wordmark idles as cooling embers (faint amber pulse); hover is ignition. All text-shadow + transform: no images, no canvas, one element.
Published
The code
<section class="cth-24" aria-label="Fire heat wave text demo">
<div class="cth-24__stage">
<div class="cth-24__scene">
<p class="cth-24__eyebrow">Small-batch hot sauce · Carolina Reaper edition</p>
<a class="cth-24__blaze" href="#cth24-top" id="cth24-top">SCORCH</a>
<p class="cth-24__sub">Bottled at 1.2 million Scoville. Hover the name; decide if you're serious.</p>
<div class="cth-24__row">
<a class="cth-24__btn" href="#cth24-top">Dare the sauce</a>
<span class="cth-24__note">ships with a waiver, mostly as a joke</span>
</div>
<p class="cth-24__hint">Five <code>text-shadow</code> layers stacked upward — yellow to smoke — jittered by <code>steps(3)</code>; a ±.5deg skew loop is the heat haze.</p>
</div>
</div>
</section><section class="cth-24" aria-label="Fire heat wave text demo">
<div class="cth-24__stage">
<div class="cth-24__scene">
<p class="cth-24__eyebrow">Small-batch hot sauce · Carolina Reaper edition</p>
<a class="cth-24__blaze" href="#cth24-top" id="cth24-top">SCORCH</a>
<p class="cth-24__sub">Bottled at 1.2 million Scoville. Hover the name; decide if you're serious.</p>
<div class="cth-24__row">
<a class="cth-24__btn" href="#cth24-top">Dare the sauce</a>
<span class="cth-24__note">ships with a waiver, mostly as a joke</span>
</div>
<p class="cth-24__hint">Five <code>text-shadow</code> layers stacked upward — yellow to smoke — jittered by <code>steps(3)</code>; a ±.5deg skew loop is the heat haze.</p>
</div>
</div>
</section>.cth-24,
.cth-24 *,
.cth-24 *::before,
.cth-24 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cth-24 {
--bg: oklch(0.14 0.01 30);
--ink: oklch(0.9 0.04 75);
--mut: oklch(0.58 0.03 45);
--hot: oklch(0.78 0.17 55);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
}
.cth-24__stage {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
background: radial-gradient(90% 60% at 50% 100%,oklch(0.24 0.06 35/.8),transparent 60%),radial-gradient(90% 70% at 50% 20%,oklch(0.17 0.012 30),var(--bg) 75%);
padding: clamp(20px,4vw,56px);
container: cth24/inline-size;
}
.cth-24__scene {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: clamp(16px,3cqi,26px);
}
.cth-24__eyebrow {
font-size: 12px;
font-weight: 700;
letter-spacing: .26em;
text-transform: uppercase;
color: var(--mut);
}
.cth-24__blaze {
display: inline-block;
text-decoration: none;
font-size: clamp(60px,14cqi,150px);
font-weight: 900;
letter-spacing: .01em;
line-height: 1;
padding: .35em .2em .12em;
color: oklch(0.82 0.12 65);
text-shadow: 0 -.04em .06em oklch(0.7 0.14 55/.35),0 -.12em .16em oklch(0.55 0.15 35/.25),0 -.2em .25em oklch(0.4 0.12 28/.2),0 -.3em .3em transparent,0 -.4em .35em transparent;
animation: cth24-ember 2.8s ease-in-out infinite;
}
@keyframes cth24-ember {
0%,
100% {
text-shadow: 0 -.04em .06em oklch(0.7 0.14 55/.35),0 -.12em .16em oklch(0.55 0.15 35/.25),0 -.2em .25em oklch(0.4 0.12 28/.2),0 -.3em .3em transparent,0 -.4em .35em transparent;
}
50% {
text-shadow: 0 -.05em .07em oklch(0.7 0.14 55/.5),0 -.14em .18em oklch(0.55 0.15 35/.35),0 -.22em .27em oklch(0.4 0.12 28/.28),0 -.32em .32em transparent,0 -.42em .37em transparent;
}
}
.cth-24__blaze:hover,
.cth-24__blaze:focus-visible {
color: oklch(0.95 0.08 95);
animation: cth24-flick .55s steps(3,jump-none) infinite,cth24-haze .18s ease-in-out infinite alternate;
}
.cth-24__blaze:focus-visible {
outline: 2px solid var(--hot);
outline-offset: 8px;
border-radius: 8px;
}
@keyframes cth24-flick {
0%,
100% {
text-shadow: 0 -.04em .02em oklch(0.93 0.15 95),0 -.13em .06em oklch(0.78 0.17 55),0 -.26em .12em oklch(0.62 0.21 32),0 -.42em .2em oklch(0.45 0.16 25),.02em -.6em .3em oklch(0.3 0.05 25/.8);
}
38% {
text-shadow: 0 -.05em .02em oklch(0.93 0.15 95),.01em -.16em .07em oklch(0.78 0.17 55),-.02em -.3em .13em oklch(0.62 0.21 32),.02em -.48em .22em oklch(0.45 0.16 25),-.03em -.66em .32em oklch(0.3 0.05 25/.8);
}
72% {
text-shadow: 0 -.035em .02em oklch(0.93 0.15 95),-.015em -.11em .05em oklch(0.78 0.17 55),.02em -.22em .11em oklch(0.62 0.21 32),-.02em -.38em .19em oklch(0.45 0.16 25),.03em -.55em .28em oklch(0.3 0.05 25/.8);
}
}
@keyframes cth24-haze {
from {
transform: skewX(-.5deg);
}
to {
transform: skewX(.5deg);
}
}
.cth-24__sub {
font-size: 14.5px;
line-height: 1.7;
color: var(--mut);
max-width: 44ch;
}
.cth-24__row {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
margin-top: 4px;
}
.cth-24__btn {
display: grid;
place-items: center;
min-height: 54px;
padding: 0 30px;
border-radius: 12px;
font-size: 16px;
font-weight: 850;
text-decoration: none;
color: oklch(0.98 0.01 85);
background: linear-gradient(180deg,oklch(0.62 0.21 40),oklch(0.5 0.19 30));
box-shadow: 0 0 0 transparent,0 0 0 transparent;
transition: box-shadow .4s ease,translate .3s ease;
}
.cth-24__btn:hover,
.cth-24__btn:focus-visible {
translate: 0 -2px;
box-shadow: 0 -6px 22px oklch(0.62 0.21 40/.5),0 8px 26px oklch(0.4 0.15 30/.55);
}
.cth-24__btn:focus-visible {
outline: 2px solid oklch(0.93 0.15 95);
outline-offset: 3px;
}
.cth-24__note {
font-size: 11.5px;
color: var(--mut);
font-style: italic;
}
.cth-24__hint {
font-size: 12.5px;
line-height: 1.7;
color: var(--mut);
max-width: 54ch;
}
.cth-24__hint code {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: .9em;
background: oklch(0.22 0.015 35);
padding: 1px 6px;
border-radius: 4px;
color: var(--hot);
}
@media (prefers-reduced-motion: reduce) {
.cth-24 * {
animation: none !important;
transition-duration: .01ms !important;
}
.cth-24__blaze:hover,
.cth-24__blaze:focus-visible {
text-shadow: 0 -.06em .1em oklch(0.85 0.15 75/.7),0 -.16em .22em oklch(0.65 0.18 45/.5),0 -.28em .3em oklch(0.5 0.15 30/.35),0 -.4em .34em transparent,0 -.5em .38em transparent;
}
}.cth-24,
.cth-24 *,
.cth-24 *::before,
.cth-24 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cth-24 {
--bg: oklch(0.14 0.01 30);
--ink: oklch(0.9 0.04 75);
--mut: oklch(0.58 0.03 45);
--hot: oklch(0.78 0.17 55);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
}
.cth-24__stage {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
background: radial-gradient(90% 60% at 50% 100%,oklch(0.24 0.06 35/.8),transparent 60%),radial-gradient(90% 70% at 50% 20%,oklch(0.17 0.012 30),var(--bg) 75%);
padding: clamp(20px,4vw,56px);
container: cth24/inline-size;
}
.cth-24__scene {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: clamp(16px,3cqi,26px);
}
.cth-24__eyebrow {
font-size: 12px;
font-weight: 700;
letter-spacing: .26em;
text-transform: uppercase;
color: var(--mut);
}
.cth-24__blaze {
display: inline-block;
text-decoration: none;
font-size: clamp(60px,14cqi,150px);
font-weight: 900;
letter-spacing: .01em;
line-height: 1;
padding: .35em .2em .12em;
color: oklch(0.82 0.12 65);
text-shadow: 0 -.04em .06em oklch(0.7 0.14 55/.35),0 -.12em .16em oklch(0.55 0.15 35/.25),0 -.2em .25em oklch(0.4 0.12 28/.2),0 -.3em .3em transparent,0 -.4em .35em transparent;
animation: cth24-ember 2.8s ease-in-out infinite;
}
@keyframes cth24-ember {
0%,
100% {
text-shadow: 0 -.04em .06em oklch(0.7 0.14 55/.35),0 -.12em .16em oklch(0.55 0.15 35/.25),0 -.2em .25em oklch(0.4 0.12 28/.2),0 -.3em .3em transparent,0 -.4em .35em transparent;
}
50% {
text-shadow: 0 -.05em .07em oklch(0.7 0.14 55/.5),0 -.14em .18em oklch(0.55 0.15 35/.35),0 -.22em .27em oklch(0.4 0.12 28/.28),0 -.32em .32em transparent,0 -.42em .37em transparent;
}
}
.cth-24__blaze:hover,
.cth-24__blaze:focus-visible {
color: oklch(0.95 0.08 95);
animation: cth24-flick .55s steps(3,jump-none) infinite,cth24-haze .18s ease-in-out infinite alternate;
}
.cth-24__blaze:focus-visible {
outline: 2px solid var(--hot);
outline-offset: 8px;
border-radius: 8px;
}
@keyframes cth24-flick {
0%,
100% {
text-shadow: 0 -.04em .02em oklch(0.93 0.15 95),0 -.13em .06em oklch(0.78 0.17 55),0 -.26em .12em oklch(0.62 0.21 32),0 -.42em .2em oklch(0.45 0.16 25),.02em -.6em .3em oklch(0.3 0.05 25/.8);
}
38% {
text-shadow: 0 -.05em .02em oklch(0.93 0.15 95),.01em -.16em .07em oklch(0.78 0.17 55),-.02em -.3em .13em oklch(0.62 0.21 32),.02em -.48em .22em oklch(0.45 0.16 25),-.03em -.66em .32em oklch(0.3 0.05 25/.8);
}
72% {
text-shadow: 0 -.035em .02em oklch(0.93 0.15 95),-.015em -.11em .05em oklch(0.78 0.17 55),.02em -.22em .11em oklch(0.62 0.21 32),-.02em -.38em .19em oklch(0.45 0.16 25),.03em -.55em .28em oklch(0.3 0.05 25/.8);
}
}
@keyframes cth24-haze {
from {
transform: skewX(-.5deg);
}
to {
transform: skewX(.5deg);
}
}
.cth-24__sub {
font-size: 14.5px;
line-height: 1.7;
color: var(--mut);
max-width: 44ch;
}
.cth-24__row {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
margin-top: 4px;
}
.cth-24__btn {
display: grid;
place-items: center;
min-height: 54px;
padding: 0 30px;
border-radius: 12px;
font-size: 16px;
font-weight: 850;
text-decoration: none;
color: oklch(0.98 0.01 85);
background: linear-gradient(180deg,oklch(0.62 0.21 40),oklch(0.5 0.19 30));
box-shadow: 0 0 0 transparent,0 0 0 transparent;
transition: box-shadow .4s ease,translate .3s ease;
}
.cth-24__btn:hover,
.cth-24__btn:focus-visible {
translate: 0 -2px;
box-shadow: 0 -6px 22px oklch(0.62 0.21 40/.5),0 8px 26px oklch(0.4 0.15 30/.55);
}
.cth-24__btn:focus-visible {
outline: 2px solid oklch(0.93 0.15 95);
outline-offset: 3px;
}
.cth-24__note {
font-size: 11.5px;
color: var(--mut);
font-style: italic;
}
.cth-24__hint {
font-size: 12.5px;
line-height: 1.7;
color: var(--mut);
max-width: 54ch;
}
.cth-24__hint code {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: .9em;
background: oklch(0.22 0.015 35);
padding: 1px 6px;
border-radius: 4px;
color: var(--hot);
}
@media (prefers-reduced-motion: reduce) {
.cth-24 * {
animation: none !important;
transition-duration: .01ms !important;
}
.cth-24__blaze:hover,
.cth-24__blaze:focus-visible {
text-shadow: 0 -.06em .1em oklch(0.85 0.15 75/.7),0 -.16em .22em oklch(0.65 0.18 45/.5),0 -.28em .3em oklch(0.5 0.15 30/.35),0 -.4em .34em transparent,0 -.5em .38em 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: Fire / Heat Wave Glow Text
Source: https://codefronts.com/motion/css-text-hover-effects/fire-heat-wave-glow-text/
Type that catches fire the physically-plausible way: flames only burn UP, so the shadow stack builds vertically — white-yellow heat tight under the glyphs, orange above, red higher, smoke-dark at the top — and a three-frame flicker keyframe jitters those offsets so the fire licks instead of glows. A 0.5deg skew wobble on a fast alternate loop adds the heat-haze shimmer. At rest the wordmark idles as cooling embers (faint amber pulse); hover is ignition. All text-shadow + transform: no images, no canvas, one element.
## HTML
```html
<section class="cth-24" aria-label="Fire heat wave text demo">
<div class="cth-24__stage">
<div class="cth-24__scene">
<p class="cth-24__eyebrow">Small-batch hot sauce · Carolina Reaper edition</p>
<a class="cth-24__blaze" href="#cth24-top" id="cth24-top">SCORCH</a>
<p class="cth-24__sub">Bottled at 1.2 million Scoville. Hover the name; decide if you're serious.</p>
<div class="cth-24__row">
<a class="cth-24__btn" href="#cth24-top">Dare the sauce</a>
<span class="cth-24__note">ships with a waiver, mostly as a joke</span>
</div>
<p class="cth-24__hint">Five <code>text-shadow</code> layers stacked upward — yellow to smoke — jittered by <code>steps(3)</code>; a ±.5deg skew loop is the heat haze.</p>
</div>
</div>
</section>
```
## CSS
```css
.cth-24,
.cth-24 *,
.cth-24 *::before,
.cth-24 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cth-24 {
--bg: oklch(0.14 0.01 30);
--ink: oklch(0.9 0.04 75);
--mut: oklch(0.58 0.03 45);
--hot: oklch(0.78 0.17 55);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
}
.cth-24__stage {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
background: radial-gradient(90% 60% at 50% 100%,oklch(0.24 0.06 35/.8),transparent 60%),radial-gradient(90% 70% at 50% 20%,oklch(0.17 0.012 30),var(--bg) 75%);
padding: clamp(20px,4vw,56px);
container: cth24/inline-size;
}
.cth-24__scene {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: clamp(16px,3cqi,26px);
}
.cth-24__eyebrow {
font-size: 12px;
font-weight: 700;
letter-spacing: .26em;
text-transform: uppercase;
color: var(--mut);
}
.cth-24__blaze {
display: inline-block;
text-decoration: none;
font-size: clamp(60px,14cqi,150px);
font-weight: 900;
letter-spacing: .01em;
line-height: 1;
padding: .35em .2em .12em;
color: oklch(0.82 0.12 65);
text-shadow: 0 -.04em .06em oklch(0.7 0.14 55/.35),0 -.12em .16em oklch(0.55 0.15 35/.25),0 -.2em .25em oklch(0.4 0.12 28/.2),0 -.3em .3em transparent,0 -.4em .35em transparent;
animation: cth24-ember 2.8s ease-in-out infinite;
}
@keyframes cth24-ember {
0%,
100% {
text-shadow: 0 -.04em .06em oklch(0.7 0.14 55/.35),0 -.12em .16em oklch(0.55 0.15 35/.25),0 -.2em .25em oklch(0.4 0.12 28/.2),0 -.3em .3em transparent,0 -.4em .35em transparent;
}
50% {
text-shadow: 0 -.05em .07em oklch(0.7 0.14 55/.5),0 -.14em .18em oklch(0.55 0.15 35/.35),0 -.22em .27em oklch(0.4 0.12 28/.28),0 -.32em .32em transparent,0 -.42em .37em transparent;
}
}
.cth-24__blaze:hover,
.cth-24__blaze:focus-visible {
color: oklch(0.95 0.08 95);
animation: cth24-flick .55s steps(3,jump-none) infinite,cth24-haze .18s ease-in-out infinite alternate;
}
.cth-24__blaze:focus-visible {
outline: 2px solid var(--hot);
outline-offset: 8px;
border-radius: 8px;
}
@keyframes cth24-flick {
0%,
100% {
text-shadow: 0 -.04em .02em oklch(0.93 0.15 95),0 -.13em .06em oklch(0.78 0.17 55),0 -.26em .12em oklch(0.62 0.21 32),0 -.42em .2em oklch(0.45 0.16 25),.02em -.6em .3em oklch(0.3 0.05 25/.8);
}
38% {
text-shadow: 0 -.05em .02em oklch(0.93 0.15 95),.01em -.16em .07em oklch(0.78 0.17 55),-.02em -.3em .13em oklch(0.62 0.21 32),.02em -.48em .22em oklch(0.45 0.16 25),-.03em -.66em .32em oklch(0.3 0.05 25/.8);
}
72% {
text-shadow: 0 -.035em .02em oklch(0.93 0.15 95),-.015em -.11em .05em oklch(0.78 0.17 55),.02em -.22em .11em oklch(0.62 0.21 32),-.02em -.38em .19em oklch(0.45 0.16 25),.03em -.55em .28em oklch(0.3 0.05 25/.8);
}
}
@keyframes cth24-haze {
from {
transform: skewX(-.5deg);
}
to {
transform: skewX(.5deg);
}
}
.cth-24__sub {
font-size: 14.5px;
line-height: 1.7;
color: var(--mut);
max-width: 44ch;
}
.cth-24__row {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
margin-top: 4px;
}
.cth-24__btn {
display: grid;
place-items: center;
min-height: 54px;
padding: 0 30px;
border-radius: 12px;
font-size: 16px;
font-weight: 850;
text-decoration: none;
color: oklch(0.98 0.01 85);
background: linear-gradient(180deg,oklch(0.62 0.21 40),oklch(0.5 0.19 30));
box-shadow: 0 0 0 transparent,0 0 0 transparent;
transition: box-shadow .4s ease,translate .3s ease;
}
.cth-24__btn:hover,
.cth-24__btn:focus-visible {
translate: 0 -2px;
box-shadow: 0 -6px 22px oklch(0.62 0.21 40/.5),0 8px 26px oklch(0.4 0.15 30/.55);
}
.cth-24__btn:focus-visible {
outline: 2px solid oklch(0.93 0.15 95);
outline-offset: 3px;
}
.cth-24__note {
font-size: 11.5px;
color: var(--mut);
font-style: italic;
}
.cth-24__hint {
font-size: 12.5px;
line-height: 1.7;
color: var(--mut);
max-width: 54ch;
}
.cth-24__hint code {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: .9em;
background: oklch(0.22 0.015 35);
padding: 1px 6px;
border-radius: 4px;
color: var(--hot);
}
@media (prefers-reduced-motion: reduce) {
.cth-24 * {
animation: none !important;
transition-duration: .01ms !important;
}
.cth-24__blaze:hover,
.cth-24__blaze:focus-visible {
text-shadow: 0 -.06em .1em oklch(0.85 0.15 75/.7),0 -.16em .22em oklch(0.65 0.18 45/.5),0 -.28em .3em oklch(0.5 0.15 30/.35),0 -.4em .34em transparent,0 -.5em .38em 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: Fire / Heat Wave Glow Text
Source: https://codefronts.com/motion/css-text-hover-effects/fire-heat-wave-glow-text/
Type that catches fire the physically-plausible way: flames only burn UP, so the shadow stack builds vertically — white-yellow heat tight under the glyphs, orange above, red higher, smoke-dark at the top — and a three-frame flicker keyframe jitters those offsets so the fire licks instead of glows. A 0.5deg skew wobble on a fast alternate loop adds the heat-haze shimmer. At rest the wordmark idles as cooling embers (faint amber pulse); hover is ignition. All text-shadow + transform: no images, no canvas, one element.
## HTML
```html
<section class="cth-24" aria-label="Fire heat wave text demo">
<div class="cth-24__stage">
<div class="cth-24__scene">
<p class="cth-24__eyebrow">Small-batch hot sauce · Carolina Reaper edition</p>
<a class="cth-24__blaze" href="#cth24-top" id="cth24-top">SCORCH</a>
<p class="cth-24__sub">Bottled at 1.2 million Scoville. Hover the name; decide if you're serious.</p>
<div class="cth-24__row">
<a class="cth-24__btn" href="#cth24-top">Dare the sauce</a>
<span class="cth-24__note">ships with a waiver, mostly as a joke</span>
</div>
<p class="cth-24__hint">Five <code>text-shadow</code> layers stacked upward — yellow to smoke — jittered by <code>steps(3)</code>; a ±.5deg skew loop is the heat haze.</p>
</div>
</div>
</section>
```
## CSS
```css
.cth-24,
.cth-24 *,
.cth-24 *::before,
.cth-24 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cth-24 {
--bg: oklch(0.14 0.01 30);
--ink: oklch(0.9 0.04 75);
--mut: oklch(0.58 0.03 45);
--hot: oklch(0.78 0.17 55);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
}
.cth-24__stage {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
background: radial-gradient(90% 60% at 50% 100%,oklch(0.24 0.06 35/.8),transparent 60%),radial-gradient(90% 70% at 50% 20%,oklch(0.17 0.012 30),var(--bg) 75%);
padding: clamp(20px,4vw,56px);
container: cth24/inline-size;
}
.cth-24__scene {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: clamp(16px,3cqi,26px);
}
.cth-24__eyebrow {
font-size: 12px;
font-weight: 700;
letter-spacing: .26em;
text-transform: uppercase;
color: var(--mut);
}
.cth-24__blaze {
display: inline-block;
text-decoration: none;
font-size: clamp(60px,14cqi,150px);
font-weight: 900;
letter-spacing: .01em;
line-height: 1;
padding: .35em .2em .12em;
color: oklch(0.82 0.12 65);
text-shadow: 0 -.04em .06em oklch(0.7 0.14 55/.35),0 -.12em .16em oklch(0.55 0.15 35/.25),0 -.2em .25em oklch(0.4 0.12 28/.2),0 -.3em .3em transparent,0 -.4em .35em transparent;
animation: cth24-ember 2.8s ease-in-out infinite;
}
@keyframes cth24-ember {
0%,
100% {
text-shadow: 0 -.04em .06em oklch(0.7 0.14 55/.35),0 -.12em .16em oklch(0.55 0.15 35/.25),0 -.2em .25em oklch(0.4 0.12 28/.2),0 -.3em .3em transparent,0 -.4em .35em transparent;
}
50% {
text-shadow: 0 -.05em .07em oklch(0.7 0.14 55/.5),0 -.14em .18em oklch(0.55 0.15 35/.35),0 -.22em .27em oklch(0.4 0.12 28/.28),0 -.32em .32em transparent,0 -.42em .37em transparent;
}
}
.cth-24__blaze:hover,
.cth-24__blaze:focus-visible {
color: oklch(0.95 0.08 95);
animation: cth24-flick .55s steps(3,jump-none) infinite,cth24-haze .18s ease-in-out infinite alternate;
}
.cth-24__blaze:focus-visible {
outline: 2px solid var(--hot);
outline-offset: 8px;
border-radius: 8px;
}
@keyframes cth24-flick {
0%,
100% {
text-shadow: 0 -.04em .02em oklch(0.93 0.15 95),0 -.13em .06em oklch(0.78 0.17 55),0 -.26em .12em oklch(0.62 0.21 32),0 -.42em .2em oklch(0.45 0.16 25),.02em -.6em .3em oklch(0.3 0.05 25/.8);
}
38% {
text-shadow: 0 -.05em .02em oklch(0.93 0.15 95),.01em -.16em .07em oklch(0.78 0.17 55),-.02em -.3em .13em oklch(0.62 0.21 32),.02em -.48em .22em oklch(0.45 0.16 25),-.03em -.66em .32em oklch(0.3 0.05 25/.8);
}
72% {
text-shadow: 0 -.035em .02em oklch(0.93 0.15 95),-.015em -.11em .05em oklch(0.78 0.17 55),.02em -.22em .11em oklch(0.62 0.21 32),-.02em -.38em .19em oklch(0.45 0.16 25),.03em -.55em .28em oklch(0.3 0.05 25/.8);
}
}
@keyframes cth24-haze {
from {
transform: skewX(-.5deg);
}
to {
transform: skewX(.5deg);
}
}
.cth-24__sub {
font-size: 14.5px;
line-height: 1.7;
color: var(--mut);
max-width: 44ch;
}
.cth-24__row {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
margin-top: 4px;
}
.cth-24__btn {
display: grid;
place-items: center;
min-height: 54px;
padding: 0 30px;
border-radius: 12px;
font-size: 16px;
font-weight: 850;
text-decoration: none;
color: oklch(0.98 0.01 85);
background: linear-gradient(180deg,oklch(0.62 0.21 40),oklch(0.5 0.19 30));
box-shadow: 0 0 0 transparent,0 0 0 transparent;
transition: box-shadow .4s ease,translate .3s ease;
}
.cth-24__btn:hover,
.cth-24__btn:focus-visible {
translate: 0 -2px;
box-shadow: 0 -6px 22px oklch(0.62 0.21 40/.5),0 8px 26px oklch(0.4 0.15 30/.55);
}
.cth-24__btn:focus-visible {
outline: 2px solid oklch(0.93 0.15 95);
outline-offset: 3px;
}
.cth-24__note {
font-size: 11.5px;
color: var(--mut);
font-style: italic;
}
.cth-24__hint {
font-size: 12.5px;
line-height: 1.7;
color: var(--mut);
max-width: 54ch;
}
.cth-24__hint code {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: .9em;
background: oklch(0.22 0.015 35);
padding: 1px 6px;
border-radius: 4px;
color: var(--hot);
}
@media (prefers-reduced-motion: reduce) {
.cth-24 * {
animation: none !important;
transition-duration: .01ms !important;
}
.cth-24__blaze:hover,
.cth-24__blaze:focus-visible {
text-shadow: 0 -.06em .1em oklch(0.85 0.15 75/.7),0 -.16em .22em oklch(0.65 0.18 45/.5),0 -.28em .3em oklch(0.5 0.15 30/.35),0 -.4em .34em transparent,0 -.5em .38em transparent;
}
}
```How this works
The flame is an ordered gradient of shadows along the Y axis — reading the stack top to bottom of the code is reading the fire bottom to top:
.blaze:hover{
color:oklch(0.95 0.08 95); /* white-hot metal */
animation:cth24-flick .55s steps(3,jump-none) infinite,
cth24-haze .18s ease-in-out infinite alternate; }
@keyframes cth24-flick{
0%{ text-shadow:0 -.04em .02em oklch(0.93 0.15 95),
0 -.13em .06em oklch(0.78 0.17 55),
0 -.26em .12em oklch(0.62 0.21 32),
0 -.42em .2em oklch(0.45 0.16 25),
.02em -.6em .3em oklch(0.3 0.05 25/.8); }
50%{ /* same 5 layers, offsets nudged ±.02em taller */ }
100%{ /* …and shorter, leaning left */ } }
@keyframes cth24-haze{ from{ transform:skewX(-.5deg); }
to { transform:skewX(.5deg); } }Physics notes that make it read as fire and not 'orange glow': every offset is NEGATIVE Y (heat rises — a downward flame shadow instantly breaks it); blur grows with height (the plume loses definition as it cools); hue falls with height (yellow→orange→red→smoke, a real blackbody sequence); and the flicker uses steps(3), because flames snap between shapes — an eased flicker looks like a dimmer switch. The haze wobble is sub-pixel skew at 5.5Hz, alternate direction, which your eye reads as air refracting above heat.
The rest state matters as much: embers — the same palette at 20% strength on a slow 2.8s ease pulse — so the element advertises heat before ignition. And the same five-layer/steps() grammar is reusable down-scale: the CTA button runs a box-shadow version of the identical stack, proving the technique transfers from glyphs to containers.
Make it yours
- Fuel type via hue set: 95/55/32/25 here is a wood fire; shift to 195/220/250 for butane blue; 145-centered greens read as witch-fire for games.
- Flame height: the tallest offset (-.6em) is the plume top — shorten everything proportionally for a controlled burner, stretch for a bonfire; keep blur growing with offset or it turns into an outline.
- Flicker rate: .55s/3 steps ≈ 5.4 shape-changes per second, a real candle's tempo. Faster reads as electrical fault (that's demo 07's territory), slower as breathing lava.
- Ignite on scroll instead: move the two animations to a .is-lit class and add it with IntersectionObserver — headlines catch fire as they enter the viewport.
Gotchas — read before shipping
- Animating text-shadow repaints every frame — it cannot composite. One hero word is fine; a paragraph of fire will cook laptop fans. This effect is a soloist.
- All five shadow layers must appear in every keyframe (same count, same order) — dropping one mid-loop makes the whole stack snap (same interpolation contract as demo 09).
- The skew haze must stay under ~0.7deg — past that, the text visibly wags instead of shimmering, and paired with flicker it becomes genuinely unpleasant to read.
- Respect prefers-reduced-motion hard here: flicker + wobble is exactly the combination vestibular-disorder guidance warns about. This demo swaps to a static glow — do not ship the moving version unconditionally.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 113+ | 111+ |
text-shadow keyframes and steps() are 2012-era; the floor is oklch() (hex the palette for older engines). Single element, no assets.