A bright light in the corner throws a horizontal streak and a line of colored ghosts across the frame, and they swing through the center as the light drifts, like a camera panning past the sun.
Also known as: lens flare css animation, anamorphic flare background, cinematic light flare css.
<div class="bgx-lens-flare" aria-hidden="true"> <div class="bgx-lens-flare-rig"><span></span><span></span><span></span><span></span><span></span><span></span></div> </div>
.bgx-lens-flare {
--c: var(--bgx-color, #3b82f6);
font-size: var(--bgx-size, 16px);
position: absolute;
inset: 0;
overflow: hidden;
background: radial-gradient(ellipse 90% 90% at 20% 15%, oklch(from var(--c) 0.22 0.05 calc(h + 0)), #040509 70%);
isolation: isolate;
}
.bgx-lens-flare-rig {
position: absolute;
left: 50%;
top: 50%;
animation: bgx-lens-flare-swing 8s cubic-bezier(0.45, 0, 0.55, 1) -2s infinite alternate;
}
.bgx-lens-flare-rig span {
position: absolute;
left: var(--at);
width: var(--size);
aspect-ratio: 1;
translate: -50% -50%;
border-radius: 50%;
background: var(--paint);
mix-blend-mode: screen;
}
.bgx-lens-flare-rig span:nth-child(1) {
--at: -13em;
--size: 9em;
--paint: radial-gradient(closest-side, #fff 0 8%, oklch(from var(--c) 0.95 0.06 calc(h + 0) / 0.9) 16%, oklch(from var(--c) 0.75 0.16 calc(h + 0) / 0.35) 45%, transparent);
animation: bgx-lens-flare-shimmer 2.6s ease-in-out infinite alternate;
}
.bgx-lens-flare-rig span:nth-child(1)::after {
content: "";
position: absolute;
left: 50%;
top: 50%;
width: 46em;
height: 0.5em;
translate: -50% -50%;
border-radius: 50%;
background: radial-gradient(closest-side, #fff, oklch(from var(--c) 0.8 0.14 calc(h + 0) / 0.6) 40%, transparent);
animation: bgx-lens-flare-level 8s cubic-bezier(0.45, 0, 0.55, 1) -2s infinite alternate;
}
.bgx-lens-flare-rig span:nth-child(2) { --at: 3em; --size: 1.6em; --paint: radial-gradient(closest-side, oklch(from var(--c) 0.85 0.12 calc(h + 60) / 0.55), transparent); }
.bgx-lens-flare-rig span:nth-child(3) { --at: 6.5em; --size: 3.4em; --paint: radial-gradient(closest-side, transparent 70%, oklch(from var(--c) 0.8 0.14 calc(h - 60) / 0.5) 88%, transparent); }
.bgx-lens-flare-rig span:nth-child(4) { --at: 9em; --size: 2.2em; --paint: oklch(from var(--c) 0.85 0.12 calc(h - 30) / 0.3); border-radius: 0; clip-path: polygon(25% 5%, 75% 5%, 100% 50%, 75% 95%, 25% 95%, 0 50%); }
.bgx-lens-flare-rig span:nth-child(5) { --at: 12em; --size: 5em; --paint: radial-gradient(closest-side, oklch(from var(--c) 0.75 0.15 calc(h + 30) / 0.18) 60%, oklch(from var(--c) 0.8 0.14 calc(h + 30) / 0.35) 90%, transparent); }
.bgx-lens-flare-rig span:nth-child(6) { --at: 17em; --size: 7.5em; --paint: radial-gradient(closest-side, transparent 60%, oklch(from var(--c) 0.85 0.1 calc(h - 110) / 0.25) 85%, transparent); }
@keyframes bgx-lens-flare-swing {
from { transform: rotate(12deg); }
to { transform: rotate(30deg); }
}
@keyframes bgx-lens-flare-level {
from { transform: rotate(-12deg); }
to { transform: rotate(-30deg); }
}
@keyframes bgx-lens-flare-shimmer {
from { opacity: 0.85; transform: scale(0.94); }
to { opacity: 1; transform: scale(1.05); }
}
@media (prefers-reduced-motion: reduce) {
.bgx-lens-flare, .bgx-lens-flare *, .bgx-lens-flare::before, .bgx-lens-flare::after, .bgx-lens-flare *::before, .bgx-lens-flare *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--bgx-color on the element or a parent.
Currently previewing #3b82f6.--bgx-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 16px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.