Skip to content
Luna UI
Lost Fantasy
background cool animation

Lantern night

HTML
<div class="tv-ambient-particles">
  <div class="tv-ambient-particles-field" aria-hidden="true">
    <span class="tv-ambient-particles-bit" style="--x: 3.1%; --y: 24.3%; --size: 3px; --dur: 7.3s; --delay: -3.9s; --drift: 28px; --hue: 48; --rot: 259deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 8.0%; --y: 19.8%; --size: 5px; --dur: 7.3s; --delay: -0.7s; --drift: 11px; --hue: 300; --rot: 63deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 17.1%; --y: 56.9%; --size: 6px; --dur: 10.8s; --delay: -6.2s; --drift: 11px; --hue: 52; --rot: 23deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 20.5%; --y: 23.1%; --size: 5px; --dur: 7.6s; --delay: -0.9s; --drift: 27px; --hue: 330; --rot: 92deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 23.7%; --y: 52.8%; --size: 4px; --dur: 8.5s; --delay: -4.7s; --drift: -28px; --hue: 48; --rot: 316deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 29.6%; --y: 60.3%; --size: 5px; --dur: 10.1s; --delay: -4.7s; --drift: 21px; --hue: 140; --rot: 127deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 37.9%; --y: 61.5%; --size: 4px; --dur: 7.3s; --delay: -2.2s; --drift: 20px; --hue: 330; --rot: 229deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 40.8%; --y: 80.7%; --size: 3px; --dur: 9.0s; --delay: -1.5s; --drift: 14px; --hue: 160; --rot: 215deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 45.0%; --y: 59.4%; --size: 6px; --dur: 9.3s; --delay: -8.1s; --drift: 20px; --hue: 330; --rot: 179deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 53.2%; --y: 53.4%; --size: 5px; --dur: 7.3s; --delay: -0.7s; --drift: 25px; --hue: 330; --rot: 340deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 55.9%; --y: 63.7%; --size: 4px; --dur: 9.6s; --delay: -9.5s; --drift: 19px; --hue: 330; --rot: 197deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 65.5%; --y: 37.6%; --size: 5px; --dur: 8.4s; --delay: -5.1s; --drift: 11px; --hue: 52; --rot: 147deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 67.1%; --y: 30.8%; --size: 5px; --dur: 10.7s; --delay: -5.3s; --drift: -24px; --hue: 160; --rot: 281deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 73.2%; --y: 23.3%; --size: 5px; --dur: 10.5s; --delay: -2.9s; --drift: 21px; --hue: 330; --rot: 194deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 82.1%; --y: 24.3%; --size: 4px; --dur: 7.6s; --delay: -5.0s; --drift: -25px; --hue: 300; --rot: 93deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 84.0%; --y: 14.3%; --size: 5px; --dur: 9.1s; --delay: -5.5s; --drift: 14px; --hue: 330; --rot: 263deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 92.8%; --y: 58.5%; --size: 3px; --dur: 8.8s; --delay: -7.7s; --drift: 22px; --hue: 160; --rot: 201deg"></span>
    <span class="tv-ambient-particles-bit" style="--x: 94.0%; --y: 57.1%; --size: 3px; --dur: 7.8s; --delay: -7.7s; --drift: 15px; --hue: 48; --rot: 174deg"></span>
  </div>
  <p class="tv-ambient-particles-label">Lantern night</p>
</div>
CSS
/* A layer of drifting particles behind content. Each bit sets its own inline
   --x, --y, --size, --dur, --delay, --drift, --hue and --rot; variants change
   what the bits are. Longhand animation properties are used on purpose:
   the shorthand cannot take a var() for its duration or delay. */
.tv-ambient-particles {
  --tv-ambient-particles-travel: 15rem;
  position: relative;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 15rem;
  overflow: hidden;
  border-radius: 6px;
  background: radial-gradient(120% 90% at 50% 100%, #2a2140 0%, #14111f 55%, #0b0a12 100%);
  font-family: ui-sans-serif, system-ui, sans-serif;
}

/* Fades every edge so nothing looks cut off at the box. */
.tv-ambient-particles-field {
  position: absolute;
  inset: 0;
  pointer-events: none;
  -webkit-mask-image:
    linear-gradient(to right, transparent 0%, #000000 8%, #000000 92%, transparent 100%),
    linear-gradient(to bottom, transparent 0%, #000000 10%, #000000 90%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to right, transparent 0%, #000000 8%, #000000 92%, transparent 100%),
    linear-gradient(to bottom, transparent 0%, #000000 10%, #000000 90%, transparent 100%);
  mask-composite: intersect;
}

.tv-ambient-particles-label {
  position: relative;
  margin: 0;
  font-size: 1.125rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: #f5f3ff;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.6);
}

/* Motes (default): soft violet dots rising and fading. */
.tv-ambient-particles-bit {
  position: absolute;
  bottom: -10px;
  left: var(--x, 50%);
  width: var(--size, 4px);
  height: var(--size, 4px);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(196, 181, 253, 0.9) 0%, transparent 70%);
  filter: blur(0.5px);
  opacity: 0;
  animation-name: tv-ambient-particles-rise;
  animation-duration: var(--dur, 8s);
  animation-delay: var(--delay, 0s);
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

/* Fireflies: glowing points hovering in place, each in its own hue. */
.tv-ambient-particles--fireflies .tv-ambient-particles-bit {
  top: var(--y, 50%);
  bottom: auto;
  background: hsla(var(--hue, 52), 95%, 85%, 1);
  filter: none;
  box-shadow:
    0 0 8px 3px hsla(var(--hue, 52), 95%, 75%, 0.85),
    0 0 22px 8px hsla(var(--hue, 52), 95%, 62%, 0.45),
    0 0 40px 14px hsla(var(--hue, 52), 95%, 52%, 0.2);
  mix-blend-mode: screen;
  animation-name: tv-ambient-particles-hover;
  animation-timing-function: ease-in-out;
}

/* Petals: blossoms falling with a sway and a slow spin. */
.tv-ambient-particles--petals .tv-ambient-particles-bit {
  top: -20px;
  bottom: auto;
  width: calc(var(--size, 4px) * 2);
  height: calc(var(--size, 4px) * 2.8);
  border-radius: 50% 0 50% 0;
  background: radial-gradient(circle at 30% 30%, rgba(255, 240, 245, 0.95) 0%, rgba(244, 180, 200, 0.9) 55%, rgba(220, 140, 170, 0.7) 100%);
  filter: drop-shadow(0 0 2px rgba(255, 200, 220, 0.35));
  animation-name: tv-ambient-particles-fall;
  animation-duration: calc(var(--dur, 8s) * 1.5);
}

/* Embers: bright sparks rising fast off an unseen fire glowing at the bottom. */
.tv-ambient-particles--embers .tv-ambient-particles-field::after {
  content: "";
  position: absolute;
  right: 12%;
  bottom: -18%;
  left: 12%;
  height: 45%;
  background: radial-gradient(ellipse at 50% 100%, rgba(255, 140, 50, 0.32), transparent 70%);
  filter: blur(10px);
}

.tv-ambient-particles--embers .tv-ambient-particles-bit {
  bottom: 4px;
  background: radial-gradient(circle, rgba(255, 190, 90, 1) 0%, rgba(255, 140, 50, 0.9) 45%, transparent 75%);
  box-shadow: 0 0 6px 1px rgba(255, 140, 50, 0.9);
  mix-blend-mode: screen;
  animation-duration: calc(var(--dur, 8s) * 0.55);
  animation-timing-function: ease-out;
}

/* Rain: thin slanted streaks. */
.tv-ambient-particles--rain .tv-ambient-particles-bit {
  top: -40px;
  bottom: auto;
  width: 1px;
  height: calc(var(--size, 4px) * 4);
  border-radius: 0;
  background: linear-gradient(to bottom, transparent 0%, rgba(180, 200, 230, 0.85) 60%, rgba(180, 200, 230, 0.85) 100%);
  filter: blur(0.3px);
  animation-name: tv-ambient-particles-rain;
  animation-duration: calc(var(--dur, 8s) * 0.12);
}

@keyframes tv-ambient-particles-rise {
  0% { transform: translate(0, 0) scale(0.6); opacity: 0; }
  10% { opacity: 0.9; }
  50% { transform: translate(calc(var(--drift, 10px) * 0.5), calc(var(--tv-ambient-particles-travel, 15rem) * -0.5)) scale(1); }
  85% { opacity: 0.9; }
  100% { transform: translate(var(--drift, 10px), calc(var(--tv-ambient-particles-travel, 15rem) * -1)) scale(0.4); opacity: 0; }
}

@keyframes tv-ambient-particles-hover {
  0% { transform: translate(0, 0) scale(0.7); opacity: 0; }
  10% { transform: translate(-6px, -6px) scale(1); opacity: 1; }
  30% { transform: translate(6px, -10px) scale(1.1); opacity: 0.9; }
  55% { transform: translate(8px, 4px) scale(1); opacity: 1; }
  80% { transform: translate(-4px, 6px) scale(0.95); opacity: 0.8; }
  100% { transform: translate(0, 0) scale(0.7); opacity: 0; }
}

@keyframes tv-ambient-particles-fall {
  0% { transform: translate(0, 0) rotate(var(--rot, 0deg)); opacity: 0; }
  6% { opacity: 0.95; }
  25% { transform: translate(var(--drift, 10px), calc(var(--tv-ambient-particles-travel, 15rem) * 0.27)) rotate(calc(var(--rot, 0deg) + 90deg)); }
  50% { transform: translate(calc(var(--drift, 10px) * -0.85), calc(var(--tv-ambient-particles-travel, 15rem) * 0.55)) rotate(calc(var(--rot, 0deg) + 180deg)); }
  75% { transform: translate(calc(var(--drift, 10px) * 0.9), calc(var(--tv-ambient-particles-travel, 15rem) * 0.83)) rotate(calc(var(--rot, 0deg) + 270deg)); opacity: 0.85; }
  100% { transform: translate(calc(var(--drift, 10px) * -0.4), calc(var(--tv-ambient-particles-travel, 15rem) * 1.1)) rotate(calc(var(--rot, 0deg) + 360deg)); opacity: 0; }
}

@keyframes tv-ambient-particles-rain {
  0% { transform: translate(0, 0) rotate(10deg); opacity: 0; }
  8% { opacity: 0.9; }
  90% { opacity: 0.85; }
  100% { transform: translate(-3rem, calc(var(--tv-ambient-particles-travel, 15rem) * 1.25)) rotate(10deg); opacity: 0; }
}

/* Still: hold every particle at a visible resting point instead of hiding it. */
@media (prefers-reduced-motion: reduce) {
  .tv-ambient-particles-bit {
    animation: none;
    opacity: 0.7;
    bottom: auto;
    top: var(--y, 50%);
  }
}
Markdown MIT License
Usage & credits
Use when
Hero backgrounds, seasonal and festival pages, quiet atmosphere behind a headline.
Avoid when
Behind long text, dense UI, or alongside other motion competing for attention.
Requires
Tailwind v4. No additional markup requirements.