30 CSS Text Animations28 / 30

Pure CSSMIT licensed

CSS Smoky Text Dissipate Blur Animation

Typography that exhales: a checkbox-driven toggle where letters blur, drift on their own vectors and vanish like smoke (then reform on untoggle), and an SVG-filter loop where feTurbulence displacement genuinely shreds the word into vapor before it recondenses.

Published Updated

Live Demo
Try it

The code

<div class="cat-28-group">
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap" rel="stylesheet">
<section class="cat-28a" aria-label="Toggle text into smoke">
  <div class="cat-28a__stage">
    <input class="cat-28a__check" type="checkbox" id="cat-28a-t">
    <h1 class="cat-28a__word" aria-label="VANISH">
      <span aria-hidden="true"><i style="--i:0;--x:-30px;--y:-58px;--r:-18deg">V</i><i style="--i:1;--x:14px;--y:-70px;--r:10deg">A</i><i style="--i:2;--x:-10px;--y:-52px;--r:-6deg">N</i><i style="--i:3;--x:26px;--y:-66px;--r:14deg">I</i><i style="--i:4;--x:-22px;--y:-74px;--r:-12deg">S</i><i style="--i:5;--x:18px;--y:-56px;--r:8deg">H</i></span>
    </h1>
    <label class="cat-28a__switch" for="cat-28a-t"><span class="cat-28a__knob" aria-hidden="true"></span>dissolve</label>
  </div>
</section>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&display=swap" rel="stylesheet">
<section class="cat-28b" aria-label="Text shredding into vapor">
  <div class="cat-28b__stage">
    <svg class="cat-28b__svg" viewBox="0 0 640 200">
      <defs>
        <filter id="cat-28b-f" x="-40%" y="-80%" width="180%" height="260%">
          <feTurbulence type="fractalNoise" baseFrequency="0.012 0.05" numOctaves="2" seed="7" result="n">
            <animate attributeName="baseFrequency" values="0.012 0.05;0.02 0.09;0.012 0.05" dur="7s" repeatCount="indefinite"></animate>
          </feTurbulence>
          <feDisplacementMap in="SourceGraphic" in2="n" scale="0" xChannelSelector="R" yChannelSelector="G">
            <animate attributeName="scale" values="0;0;90;0;0" keyTimes="0;.3;.55;.8;1" dur="7s" repeatCount="indefinite"></animate>
          </feDisplacementMap>
        </filter>
      </defs>
      <text x="50%" y="52%" dominant-baseline="central" text-anchor="middle" class="cat-28b__word" filter="url(#cat-28b-f)">VAPOR</text>
    </svg>
    <p class="cat-28b__sub" aria-hidden="true">feTurbulence displacement shreds the edges — blur alone can't do this</p>
  </div>
</section>
</div>
.cat-28-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  width: 100%;
}

.cat-28-group > section {
  flex: 1 1 480px;
  min-width: min(100%,360px);
}

.cat-28a,
.cat-28a *,
.cat-28a *::before,
.cat-28a *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cat-28a {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: linear-gradient(180deg,#15171c,#0d0e12);
  font-family: 'Playfair Display',Georgia,serif;
}

.cat-28a__stage {
  display: grid;
  gap: 38px;
  justify-items: center;
  text-align: center;
}

.cat-28a__check {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.cat-28a__word {
  font-size: clamp(54px,10vw,130px);
  font-weight: 700;
  letter-spacing: .1em;
  margin-right: -.1em;
  color: #e9e4d8;
}

.cat-28a__word i {
  display: inline-block;
  font-style: normal;
  transition: transform 1.1s cubic-bezier(.3,.6,.4,1),opacity 1s ease,filter 1s ease;
  transition-delay: calc(var(--i)*55ms);
}

.cat-28a__check:checked ~ .cat-28a__word i {
  opacity: 0;
  filter: blur(14px);
  transform: translate(var(--x),var(--y)) rotate(var(--r)) scale(1.35);
}

.cat-28a__switch {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-family: ui-monospace,Menlo,monospace;
  font-size: 12px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: rgba(233,228,216,.6);
  cursor: pointer;
  user-select: none;
}

.cat-28a__knob {
  width: 42px;
  height: 24px;
  border-radius: 999px;
  background: rgba(233,228,216,.14);
  border: 1px solid rgba(233,228,216,.25);
  position: relative;
  transition: background .3s;
}

.cat-28a__knob::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #e9e4d8;
  transition: transform .3s cubic-bezier(.2,.8,.3,1);
}

.cat-28a__check:checked ~ .cat-28a__switch .cat-28a__knob {
  background: rgba(233,228,216,.35);
}

.cat-28a__check:checked ~ .cat-28a__switch .cat-28a__knob::after {
  transform: translateX(18px);
}

.cat-28a__check:focus-visible ~ .cat-28a__switch .cat-28a__knob {
  outline: 2px solid #e9e4d8;
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .cat-28a__word i {
    transition: opacity .4s ease;
    transition-delay: 0s;
  }

  .cat-28a__check:checked ~ .cat-28a__word i {
    transform: none;
    filter: none;
  }
}

.cat-28b,
.cat-28b *,
.cat-28b *::before,
.cat-28b *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cat-28b {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: #0e1116;
  font-family: 'Space Grotesk','Segoe UI',system-ui,sans-serif;
}

.cat-28b__stage {
  text-align: center;
  width: min(680px,100%);
}

.cat-28b__svg {
  width: 100%;
  height: auto;
  overflow: visible;
}

.cat-28b__word {
  font-family: 'Space Grotesk','Segoe UI',system-ui,sans-serif;
  font-size: 120px;
  font-weight: 700;
  letter-spacing: 8px;
  fill: #dfe7f2;
  animation: cat-28b-haze 7s ease-in-out infinite;
}

.cat-28b__sub {
  font-family: ui-monospace,Menlo,monospace;
  font-size: 11px;
  letter-spacing: .15em;
  color: rgba(180,200,230,.4);
}

@keyframes cat-28b-haze {
  0%,
    30% {
    opacity: 1;
    filter: blur(0);
  }

  55% {
    opacity: .06;
    filter: blur(9px);
  }

  80%,
    100% {
    opacity: 1;
    filter: blur(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .cat-28b__word {
    animation: none;
  }
}
/* No JavaScript — a visually-hidden checkbox drives :checked ~ sibling selectors; each letter blurs and drifts along its own inline vector with a staggered delay, and untoggling reverses it. */

/* No JavaScript — SMIL animates feTurbulence frequency and the displacement scale (0→90→0) while a synchronized CSS keyframe handles blur and opacity; the word shreds, vanishes, recondenses. */
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Text Animation 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: CSS Smoky Text Dissipate Blur Animation
Source: https://codefronts.com/motion/css-text-animations/css-blur-reveal-text-animation/

Typography that exhales: a checkbox-driven toggle where letters blur, drift on their own vectors and vanish like smoke (then reform on untoggle), and an SVG-filter loop where feTurbulence displacement genuinely shreds the word into vapor before it recondenses.
## HTML
```html
<div class="cat-28-group">
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap" rel="stylesheet">
<section class="cat-28a" aria-label="Toggle text into smoke">
  <div class="cat-28a__stage">
    <input class="cat-28a__check" type="checkbox" id="cat-28a-t">
    <h1 class="cat-28a__word" aria-label="VANISH">
      <span aria-hidden="true"><i style="--i:0;--x:-30px;--y:-58px;--r:-18deg">V</i><i style="--i:1;--x:14px;--y:-70px;--r:10deg">A</i><i style="--i:2;--x:-10px;--y:-52px;--r:-6deg">N</i><i style="--i:3;--x:26px;--y:-66px;--r:14deg">I</i><i style="--i:4;--x:-22px;--y:-74px;--r:-12deg">S</i><i style="--i:5;--x:18px;--y:-56px;--r:8deg">H</i></span>
    </h1>
    <label class="cat-28a__switch" for="cat-28a-t"><span class="cat-28a__knob" aria-hidden="true"></span>dissolve</label>
  </div>
</section>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&display=swap" rel="stylesheet">
<section class="cat-28b" aria-label="Text shredding into vapor">
  <div class="cat-28b__stage">
    <svg class="cat-28b__svg" viewBox="0 0 640 200">
      <defs>
        <filter id="cat-28b-f" x="-40%" y="-80%" width="180%" height="260%">
          <feTurbulence type="fractalNoise" baseFrequency="0.012 0.05" numOctaves="2" seed="7" result="n">
            <animate attributeName="baseFrequency" values="0.012 0.05;0.02 0.09;0.012 0.05" dur="7s" repeatCount="indefinite"></animate>
          </feTurbulence>
          <feDisplacementMap in="SourceGraphic" in2="n" scale="0" xChannelSelector="R" yChannelSelector="G">
            <animate attributeName="scale" values="0;0;90;0;0" keyTimes="0;.3;.55;.8;1" dur="7s" repeatCount="indefinite"></animate>
          </feDisplacementMap>
        </filter>
      </defs>
      <text x="50%" y="52%" dominant-baseline="central" text-anchor="middle" class="cat-28b__word" filter="url(#cat-28b-f)">VAPOR</text>
    </svg>
    <p class="cat-28b__sub" aria-hidden="true">feTurbulence displacement shreds the edges — blur alone can't do this</p>
  </div>
</section>
</div>
```
## CSS
```css
.cat-28-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  width: 100%;
}

.cat-28-group > section {
  flex: 1 1 480px;
  min-width: min(100%,360px);
}

.cat-28a,
.cat-28a *,
.cat-28a *::before,
.cat-28a *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cat-28a {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: linear-gradient(180deg,#15171c,#0d0e12);
  font-family: 'Playfair Display',Georgia,serif;
}

.cat-28a__stage {
  display: grid;
  gap: 38px;
  justify-items: center;
  text-align: center;
}

.cat-28a__check {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.cat-28a__word {
  font-size: clamp(54px,10vw,130px);
  font-weight: 700;
  letter-spacing: .1em;
  margin-right: -.1em;
  color: #e9e4d8;
}

.cat-28a__word i {
  display: inline-block;
  font-style: normal;
  transition: transform 1.1s cubic-bezier(.3,.6,.4,1),opacity 1s ease,filter 1s ease;
  transition-delay: calc(var(--i)*55ms);
}

.cat-28a__check:checked ~ .cat-28a__word i {
  opacity: 0;
  filter: blur(14px);
  transform: translate(var(--x),var(--y)) rotate(var(--r)) scale(1.35);
}

.cat-28a__switch {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-family: ui-monospace,Menlo,monospace;
  font-size: 12px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: rgba(233,228,216,.6);
  cursor: pointer;
  user-select: none;
}

.cat-28a__knob {
  width: 42px;
  height: 24px;
  border-radius: 999px;
  background: rgba(233,228,216,.14);
  border: 1px solid rgba(233,228,216,.25);
  position: relative;
  transition: background .3s;
}

.cat-28a__knob::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #e9e4d8;
  transition: transform .3s cubic-bezier(.2,.8,.3,1);
}

.cat-28a__check:checked ~ .cat-28a__switch .cat-28a__knob {
  background: rgba(233,228,216,.35);
}

.cat-28a__check:checked ~ .cat-28a__switch .cat-28a__knob::after {
  transform: translateX(18px);
}

.cat-28a__check:focus-visible ~ .cat-28a__switch .cat-28a__knob {
  outline: 2px solid #e9e4d8;
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .cat-28a__word i {
    transition: opacity .4s ease;
    transition-delay: 0s;
  }

  .cat-28a__check:checked ~ .cat-28a__word i {
    transform: none;
    filter: none;
  }
}

.cat-28b,
.cat-28b *,
.cat-28b *::before,
.cat-28b *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cat-28b {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: #0e1116;
  font-family: 'Space Grotesk','Segoe UI',system-ui,sans-serif;
}

.cat-28b__stage {
  text-align: center;
  width: min(680px,100%);
}

.cat-28b__svg {
  width: 100%;
  height: auto;
  overflow: visible;
}

.cat-28b__word {
  font-family: 'Space Grotesk','Segoe UI',system-ui,sans-serif;
  font-size: 120px;
  font-weight: 700;
  letter-spacing: 8px;
  fill: #dfe7f2;
  animation: cat-28b-haze 7s ease-in-out infinite;
}

.cat-28b__sub {
  font-family: ui-monospace,Menlo,monospace;
  font-size: 11px;
  letter-spacing: .15em;
  color: rgba(180,200,230,.4);
}

@keyframes cat-28b-haze {
  0%,
    30% {
    opacity: 1;
    filter: blur(0);
  }

  55% {
    opacity: .06;
    filter: blur(9px);
  }

  80%,
    100% {
    opacity: 1;
    filter: blur(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .cat-28b__word {
    animation: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — a visually-hidden checkbox drives :checked ~ sibling selectors; each letter blurs and drifts along its own inline vector with a staggered delay, and untoggling reverses it. */

/* No JavaScript — SMIL animates feTurbulence frequency and the displacement scale (0→90→0) while a synchronized CSS keyframe handles blur and opacity; the word shreds, vanishes, recondenses. */
```

How this works

The toggle is state-driven CSS — a hidden checkbox, a styled switch, and letter transitions keyed off :checked. Each letter owns a drift vector and delay, so the word never dissolves uniformly:

#smoke:checked ~ .word i{
  opacity:0;filter:blur(14px);
  transform:translate(var(--x),var(--y)) scale(1.4);
  transition-delay:calc(var(--i)*45ms)}

Real smoke needs turbulence: the SVG variant filters the text through feTurbulence → feDisplacementMap, with SMIL animating baseFrequency while CSS ramps blur() and opacity in sync. Displacement tears the glyph edges organically — something blur alone can't fake.

Make it yours

  • Drift up (negative --y) for smoke, down for 'thanos dust'.
  • Dissolve duration: .8–1.4s reads elegiac; under .5s reads like an error.
  • Turbulence scale: displacement 40+ is heavy weather; 15–25 is incense.
  • Trigger from any state — :checked here, but .is-hidden via JS works identically.

Gotchas — read before shipping

  • Keep the checkbox focusable (sr-only pattern, not display:none) so keyboard users can toggle.
  • Per-letter blur is cheap; blurring the whole word rasterizes the group every frame — avoid at large sizes.
  • SVG filters run on the CPU in some engines — contain the filtered area, don't filter the page.

Browser support

ChromeSafariFirefoxEdge
26+9+16+26+

Filters, transitions and SMIL are all evergreen-universal (SMIL is un-deprecated and reliable). Reduced motion swaps dissolve for a simple fade.

Search CodeFronts

Loading…