07蛍
ぼんやりした点16個が opacity で明滅しながら translate で小刻みに方向を変えて漂い、不規則な光の軌跡を作る。
使いどころ自然・キャンプ系ブランド背景、感性重視のポートフォリオヒーロー
opacity 명멸translate 방향전환nth-child 궤적

<main class="stage">
<div class="field" aria-hidden="true">
<span class="p" style="--i:0"></span>
<span class="p" style="--i:1"></span>
<span class="p" style="--i:2"></span>
<span class="p" style="--i:3"></span>
<span class="p" style="--i:4"></span>
<span class="p" style="--i:5"></span>
<span class="p" style="--i:6"></span>
<span class="p" style="--i:7"></span>
<span class="p" style="--i:8"></span>
<span class="p" style="--i:9"></span>
<span class="p" style="--i:10"></span>
<span class="p" style="--i:11"></span>
<span class="p" style="--i:12"></span>
<span class="p" style="--i:13"></span>
<span class="p" style="--i:14"></span>
<span class="p" style="--i:15"></span>
</div>
<p class="caption">夏の夜キャンプ</p>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────
$duration: $dur-loop * 1.75; // 3.5s 기준
$easing: ease-in-out;
$color: $subject-cream;
// ── 데모 무대 ───────────────────────────────
@include stage($stage-orange);
.field { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.caption {
position: relative; z-index: 1; margin: 0;
color: $subject-cream; font-weight: 700; font-size: clamp(16px, 5vw, 22px); line-height: 1; font-family: inherit;
}
.p {
position: absolute;
width: 4px; height: 4px;
border-radius: 50%;
background: $color;
box-shadow: 0 0 12px rgba($color, .8);
opacity: 0;
animation: drift $duration $easing infinite;
animation-delay: calc(var(--i) * -0.37s);
}
@for $i from 1 through 16 {
.p:nth-child(#{$i}) {
left: ((($i * 29) % 90)) * 1%;
top: ((($i * 61) % 78)) * 1%;
animation-duration: $duration + ($i % 4) * .4s;
}
}
// 짧게 방향을 바꾸며 떠다니는 불규칙한 궤적 (transform 만)
@keyframes drift {
0% { transform: translate(0, 0); opacity: 0; }
20% { opacity: .9; }
35% { transform: translate(14px, -10px); }
55% { transform: translate(-8px, -20px); opacity: .3; }
70% { opacity: .85; }
85% { transform: translate(10px, -6px); }
100% { transform: translate(0, 0); opacity: 0; }
}
// ── 접근성: 깜빡임·이동을 멈추고 흐린 점만 남긴다 ────────────
@media (prefers-reduced-motion: reduce) {
.p { animation: none; opacity: .45; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ff4d1f;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.field {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
}
.caption {
position: relative;
z-index: 1;
margin: 0;
color: #fff7e6;
font-weight: 700;
font-size: clamp(16px, 5vw, 22px);
line-height: 1;
font-family: inherit;
}
.p {
position: absolute;
width: 4px;
height: 4px;
border-radius: 50%;
background: #fff7e6;
box-shadow: 0 0 12px rgba(255, 247, 230, 0.8);
opacity: 0;
animation: drift 3.5s ease-in-out infinite;
animation-delay: calc(var(--i) * -0.37s);
}
.p:nth-child(1) {
left: 29%;
top: 61%;
animation-duration: 3.9s;
}
.p:nth-child(2) {
left: 58%;
top: 44%;
animation-duration: 4.3s;
}
.p:nth-child(3) {
left: 87%;
top: 27%;
animation-duration: 4.7s;
}
.p:nth-child(4) {
left: 26%;
top: 10%;
animation-duration: 3.5s;
}
.p:nth-child(5) {
left: 55%;
top: 71%;
animation-duration: 3.9s;
}
.p:nth-child(6) {
left: 84%;
top: 54%;
animation-duration: 4.3s;
}
.p:nth-child(7) {
left: 23%;
top: 37%;
animation-duration: 4.7s;
}
.p:nth-child(8) {
left: 52%;
top: 20%;
animation-duration: 3.5s;
}
.p:nth-child(9) {
left: 81%;
top: 3%;
animation-duration: 3.9s;
}
.p:nth-child(10) {
left: 20%;
top: 64%;
animation-duration: 4.3s;
}
.p:nth-child(11) {
left: 49%;
top: 47%;
animation-duration: 4.7s;
}
.p:nth-child(12) {
left: 78%;
top: 30%;
animation-duration: 3.5s;
}
.p:nth-child(13) {
left: 17%;
top: 13%;
animation-duration: 3.9s;
}
.p:nth-child(14) {
left: 46%;
top: 74%;
animation-duration: 4.3s;
}
.p:nth-child(15) {
left: 75%;
top: 57%;
animation-duration: 4.7s;
}
.p:nth-child(16) {
left: 14%;
top: 40%;
animation-duration: 3.5s;
}
@keyframes drift {
0% {
transform: translate(0, 0);
opacity: 0;
}
20% {
opacity: 0.9;
}
35% {
transform: translate(14px, -10px);
}
55% {
transform: translate(-8px, -20px);
opacity: 0.3;
}
70% {
opacity: 0.85;
}
85% {
transform: translate(10px, -6px);
}
100% {
transform: translate(0, 0);
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.p {
animation: none;
opacity: 0.45;
}
}import type { CSSProperties } from "react";
import styles from "./Fireflies.module.scss";
export type FirefliesProps = {
/** 애니메이션 기준 시간(ms) */
duration?: number;
/** CSS easing */
easing?: string;
/** 피사체 색 */
color?: string;
};
type Vars = CSSProperties & { "--i"?: number };
const COUNT = 16;
export default function Fireflies({
duration = 3500,
easing = "ease-in-out",
color = "#fff7e6",
}: FirefliesProps) {
const vars = {
"--duration": `${duration}ms`,
"--easing": easing,
"--color": color,
} as CSSProperties;
return (
<div className={styles.field} style={vars} aria-hidden="true">
{Array.from({ length: COUNT }, (_, i) => (
<span key={i} className={styles.p} style={{ "--i": i } as Vars} />
))}
</div>
);
}07 蛍 — 計測 累積面積 1.859% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · CSS 背景アニメーション 星 9選 — 一行でコピペOK
このデモが属する記事
CSS 背景アニメーション 星 9選 — 一行でコピペOKcss 背景アニメーション 星のような検索でたどり着く人が本当に探しているのは、雪や星、雨、紙吹雪、泡、蛍、花びら、霧のように小さな粒を画面全体にばらまいて動かす背景の作り方です。span 1個をnth-childで何十回も繰り返し、位置・ この記事のデモをすべて見る


