Each letter crumbles into grains of sand that blow away to the right, then the grains drift back and settle into type. A copy of every letter is scattered by an SVG displacement filter as it flies.
Also known as: disintegrate text effect, sand text animation, thanos snap text css, particle text dissolve.
<div class="txt-sand-disintegrate-text">
<svg class="txt-sand-disintegrate-text-defs" aria-hidden="true" focusable="false">
<filter id="txt-sand-disintegrate-text-grain" x="-30%" y="-30%" width="160%" height="160%">
<feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="1" seed="5" />
<feDisplacementMap in="SourceGraphic" scale="20" xChannelSelector="R" yChannelSelector="G" />
</filter>
</svg>
<span class="txt-sand-disintegrate-text-word"><span style="--i: 0" data-ch="D">D</span><span style="--i: 1" data-ch="u">u</span><span style="--i: 2" data-ch="n">n</span><span style="--i: 3" data-ch="e">e</span><span style="--i: 4" data-ch="s">s</span></span>
</div>.txt-sand-disintegrate-text {
--c: var(--txt-color, #3b82f6);
font-size: calc(var(--txt-size, 32px) * 1.9);
font-family: system-ui, sans-serif;
font-weight: 900;
line-height: 1.2;
--sand: color-mix(in srgb, var(--c) 18%, #c4924a);
position: relative;
display: inline-block;
letter-spacing: 0.02em;}
.txt-sand-disintegrate-text-defs {
position: absolute;
width: 0;
height: 0;
overflow: hidden;
}
.txt-sand-disintegrate-text-word span {
position: relative;
display: inline-block;
color: var(--sand);
animation: txt-sand-disintegrate-text-solid 6s ease-in-out calc(var(--i) * 0.08s) infinite;
}
.txt-sand-disintegrate-text-word span::after {
content: attr(data-ch);
position: absolute;
inset: 0;
color: color-mix(in srgb, var(--c) 14%, #e0b56a);
filter: url(#txt-sand-disintegrate-text-grain);
opacity: 0;
animation: txt-sand-disintegrate-text-dust 6s cubic-bezier(0.4, 0, 0.3, 1) calc(var(--i) * 0.08s) infinite;
}
@keyframes txt-sand-disintegrate-text-solid {
0%, 52%, 90%, 100% { color: var(--sand); }
58%, 84% { color: transparent; }
}
@keyframes txt-sand-disintegrate-text-dust {
0%, 50% { opacity: 0; transform: none; }
56% { opacity: 1; transform: translate(0.04em, -0.02em); }
70% { opacity: 0; transform: translate(0.9em, -0.45em) rotate(10deg); }
70.1%, 73% { opacity: 0; transform: translate(-0.7em, 0.3em) rotate(-8deg); }
86% { opacity: 1; transform: none; }
93%, 100% { opacity: 0; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
.txt-sand-disintegrate-text-word span, .txt-sand-disintegrate-text-word span::after { animation: none; }
}
@media (prefers-reduced-motion: reduce) {
.txt-sand-disintegrate-text, .txt-sand-disintegrate-text *, .txt-sand-disintegrate-text::before, .txt-sand-disintegrate-text::after, .txt-sand-disintegrate-text *::before, .txt-sand-disintegrate-text *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--txt-color on the element or a parent.
Currently previewing #3b82f6.--txt-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 40px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.