A small light swings in an arc above the word and its soft shadow slides the other way, short when the light is overhead and long when it is low. Light and shadow share one alternating clock.
Also known as: dynamic text shadow css, text shadow follows light, light source shadow animation.
<div class="txt-moving-light-shadow-text"><span class="txt-moving-light-shadow-text-light" aria-hidden="true"></span><span class="txt-moving-light-shadow-text-shadow" aria-hidden="true">SHADOW</span><span class="txt-moving-light-shadow-text-word">SHADOW</span></div>
.txt-moving-light-shadow-text {
--c: var(--txt-color, #3b82f6);
font-size: calc(var(--txt-size, 32px) * 1.6);
font-family: system-ui, sans-serif;
font-weight: 900;
line-height: 1.2;
position: relative;
display: inline-grid;
letter-spacing: 0.04em;}
.txt-moving-light-shadow-text-shadow,
.txt-moving-light-shadow-text-word {
grid-area: 1 / 1;
}
.txt-moving-light-shadow-text-word {
position: relative;
color: var(--c);
}
.txt-moving-light-shadow-text-shadow {
color: color-mix(in srgb, color-mix(in srgb, var(--c) 40%, #000) 35%, transparent);
animation: txt-moving-light-shadow-text-cast 2.8s cubic-bezier(0.45, 0, 0.55, 1) infinite alternate;
}
/* the light: a glowing dot on an arc over the word */
.txt-moving-light-shadow-text-light {
position: absolute;
left: 50%;
top: -0.7em;
width: 0.3em;
height: 0.3em;
margin-left: -0.15em;
border-radius: 50%;
background: radial-gradient(circle, color-mix(in srgb, var(--c) 55%, #fff) 0 30%, var(--c) 60%);
box-shadow: 0 0 0.3em color-mix(in srgb, var(--c) 60%, transparent);
animation: txt-moving-light-shadow-text-orbit 2.8s cubic-bezier(0.45, 0, 0.55, 1) infinite alternate;
}
@keyframes txt-moving-light-shadow-text-orbit {
0% { transform: translate(-2.6em, 0.35em); }
50% { transform: translate(0, -0.15em); }
100% { transform: translate(2.6em, 0.35em); }
}
/* the shadow falls away from the light: long and soft when it is low */
@keyframes txt-moving-light-shadow-text-cast {
0% { transform: translate(0.28em, 0.16em); filter: blur(0.045em); }
50% { transform: translate(0, 0.08em); filter: blur(0.015em); }
100% { transform: translate(-0.28em, 0.16em); filter: blur(0.045em); }
}
@media (prefers-reduced-motion: reduce) {
.txt-moving-light-shadow-text, .txt-moving-light-shadow-text *, .txt-moving-light-shadow-text::before, .txt-moving-light-shadow-text::after, .txt-moving-light-shadow-text *::before, .txt-moving-light-shadow-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.