A small screen full of TV static with a NO SIGNAL label that flickers in front. The noise is one inline SVG turbulence filter, drawn once and jumped between offsets with steps().
Also known as: tv static text effect, signal lost screen css, white noise background text.
<div class="txt-no-signal-static">
<svg class="txt-no-signal-static-noise" viewBox="0 0 200 100" preserveAspectRatio="none" aria-hidden="true">
<filter id="txt-no-signal-static-grain"><feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2" seed="7" /><feColorMatrix type="saturate" values="0" /></filter>
<rect width="200" height="100" filter="url(#txt-no-signal-static-grain)" />
</svg>
<span class="txt-no-signal-static-label">NO SIGNAL</span>
</div>.txt-no-signal-static {
--c: var(--txt-color, #3b82f6);
font-size: var(--txt-size, 32px);
font-family: system-ui, sans-serif;
font-weight: 700;
line-height: 1.2;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
width: 8em;
height: 3.8em;
overflow: hidden;
border-radius: 0.35em;
background: #2a2a2a;
box-shadow: inset 0 0 0.8em rgb(0 0 0 / 0.8), 0 0 0 0.12em #1d1f24;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;}
.txt-no-signal-static-noise {
position: absolute;
left: -50%;
top: -50%;
width: 200%;
height: 200%;
opacity: 0.55;
animation: txt-no-signal-static-jump 0.5s steps(1, end) infinite;
}
/* a darker band rolling down, like a drifting vertical hold */
.txt-no-signal-static::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg, transparent 40%, rgb(0 0 0 / 0.45) 50%, transparent 60%) 0 0 / 100% 200%;
animation: txt-no-signal-static-roll 2.6s linear infinite;
}
.txt-no-signal-static-label {
position: relative;
z-index: 1;
padding: 0.12em 0.5em;
background: var(--c);
color: #fff;
font-size: 0.8em;
font-weight: 700;
letter-spacing: 0.14em;
animation: txt-no-signal-static-flick 3s steps(1, end) infinite;
}
@keyframes txt-no-signal-static-jump {
0%, 100% { transform: translate(0, 0); }
20% { transform: translate(-11%, 7%); }
40% { transform: translate(13%, -9%); }
60% { transform: translate(-6%, -12%); }
80% { transform: translate(9%, 11%); }
}
@keyframes txt-no-signal-static-roll {
from { background-position: 0 100%; }
to { background-position: 0 -100%; }
}
@keyframes txt-no-signal-static-flick {
0%, 44%, 50%, 100% { opacity: 1; transform: none; }
42% { opacity: 0.3; transform: translateX(0.04em); }
48% { opacity: 0.7; transform: translateX(-0.03em); }
}
@media (prefers-reduced-motion: reduce) {
.txt-no-signal-static, .txt-no-signal-static *, .txt-no-signal-static::before, .txt-no-signal-static::after, .txt-no-signal-static *::before, .txt-no-signal-static *::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.