# FirefliesBackground

> Thirty fireflies wandering four-point loops and blinking on their own rhythms over a dusk gradient, with a crescent moon and a meadow of swaying grass blades placed by percentage so it spans any width. CSS keyframes only.

- Page: https://www.boatui.dev/backgrounds/fireflies
- Source: https://github.com/wi11s/boatui/blob/main/components/backgrounds/fireflies-background.tsx
- Animated. Generate: ~1,081 tokens (source size, single pass). Import: ~22 tokens.

## Install

All backgrounds: `npx degit wi11s/boatui/components/backgrounds components/backgrounds`

Only this one: copy the shared files (`background.tsx`, `background.module.css`) and `fireflies-background.tsx` and `fireflies-background.module.css` into `components/backgrounds/`. Shared files are in https://www.boatui.dev/llms-full.txt.

## Usage

```tsx
import { FirefliesBackground } from '@/components/backgrounds/fireflies-background';

export default function Layout({ children }: { children: React.ReactNode }) {
  return <FirefliesBackground style={{ minHeight: '100vh' }}>{children}</FirefliesBackground>;
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `theme` | `Partial<FirefliesTheme>` | — | Colour overrides. Keys listed under theme tokens. |
| `paused` | `boolean` | false | Freezes the animation. |
| `className` | `string` | — | Applied to the root element. Size it like any block element. |
| `style` | `CSSProperties` | — | Merged into the root element style, after theme variables. |
| `children` | `ReactNode` | — | Your content. The background paints behind it. |

## Theme tokens

| Key | CSS variable | Default |
| --- | --- | --- |
| `skyTop` | `--sky-top` | `#0e1a30` |
| `skyBottom` | `--sky-bottom` | `#33284d` |
| `firefly` | `--firefly` | `#fbf8c4` |
| `glow` | `--glow` | `#d6f36a` |
| `grass` | `--grass` | `#0a1120` |
| `moon` | `--moon` | `#f3ecd2` |

## Source

`components/backgrounds/fireflies-background.tsx`

```tsx
// <FirefliesBackground>: fireflies wandering and blinking over a dusky meadow,
// with a crescent moon and swaying grass. Animated.

import type { CSSProperties } from 'react';
import { BackgroundFrame, r2, seeded, type BackgroundProps } from './background';
import styles from './fireflies-background.module.css';

export const firefliesTheme = {
  skyTop: '#0e1a30',
  skyBottom: '#33284d',
  firefly: '#fbf8c4',
  glow: '#d6f36a',
  grass: '#0a1120',
  moon: '#f3ecd2',
};
export type FirefliesTheme = typeof firefliesTheme;

const rand = seeded(73);
const offset = (range: number) => `${r2((rand() - 0.5) * range)}px`;

const FIREFLIES = Array.from({ length: 30 }, () => ({
  x: r2(3 + rand() * 94),
  y: r2(18 + rand() * 72),
  wander: r2(10 + rand() * 10),
  blink: r2(2.8 + rand() * 3.5),
  delay: r2(rand() * 10),
  path: { '--ax': offset(80), '--ay': offset(50), '--bx': offset(80), '--by': offset(50), '--cx': offset(80), '--cy': offset(50) },
  scale: r2(0.7 + rand() * 0.7),
}));

// Blades are placed at percentage positions so the meadow spans any width without stretching.
const BLADES = Array.from({ length: 70 }, () => ({
  x: r2(rand() * 101),
  h: r2(28 + rand() * 52),
  lean: r2((rand() - 0.5) * 16),
  w: r2(2.5 + rand() * 2.5),
  sway: r2(2.5 + rand() * 2.5),
  delay: r2(rand() * 4),
}));

/** Fireflies wandering and blinking over a dusky meadow. */
export function FirefliesBackground(props: BackgroundProps<FirefliesTheme>) {
  return (
    <BackgroundFrame
      {...props}
      defaultTheme={firefliesTheme}
      layer={
        <>
          <div className={styles.sky} />
          <svg className={styles.moon} viewBox="0 0 54 54">
            <defs>
              <mask id="qs-fireflies-moon">
                <rect width="54" height="54" fill="#fff" />
                <circle cx="36" cy="20" r="21" fill="#000" />
              </mask>
            </defs>
            <circle cx="27" cy="27" r="22" fill="var(--moon)" mask="url(#qs-fireflies-moon)" />
          </svg>

          {FIREFLIES.map((f, i) => (
            <div
              key={i}
              className={styles.firefly}
              style={{ left: `${f.x}%`, top: `${f.y}%`, animationDuration: `${f.wander}s`, animationDelay: `${-f.delay}s`, scale: f.scale, ...f.path } as CSSProperties}
            >
              <span className={styles.light} style={{ animationDuration: `${f.blink}s`, animationDelay: `${-f.delay}s` }} />
            </div>
          ))}

          <svg className={styles.grass}>
            {BLADES.map((b, i) => (
              <svg key={i} x={`${b.x}%`} y="100%" overflow="visible">
                <path
                  className={styles.blade}
                  style={{ animationDuration: `${b.sway}s`, animationDelay: `${-b.delay}s` }}
                  d={`M${-b.w} 0 Q${r2(b.lean * 0.4)} ${r2(-b.h * 0.6)} ${b.lean} ${-b.h} Q${r2(b.lean * 0.4 + 1)} ${r2(-b.h * 0.5)} ${b.w} 0 Z`}
                  fill="var(--grass)"
                />
              </svg>
            ))}
          </svg>
        </>
      }
    />
  );
}
```

`components/backgrounds/fireflies-background.module.css`

```css
.sky {
  position: absolute;
  inset: 0;
  background: linear-gradient(var(--sky-top), var(--sky-bottom));
}

.moon {
  position: absolute;
  top: 28px;
  right: 36px;
  width: 54px;
  height: 54px;
  filter: drop-shadow(0 0 14px rgb(255 245 210 / 0.35));
}

/* Each firefly wanders a four-point loop and blinks on its own rhythm. */
.firefly {
  position: absolute;
  animation: wander ease-in-out infinite;
}

.light {
  display: block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--firefly);
  box-shadow: 0 0 6px 2px var(--glow), 0 0 18px 6px color-mix(in srgb, var(--glow) 45%, transparent);
  animation: blink ease-in-out infinite;
}

.grass {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 90px;
  overflow: visible;
}

.blade {
  animation: sway ease-in-out infinite alternate;
}

@keyframes wander {
  0%, 100% { transform: translate(0, 0); }
  25% { transform: translate(var(--ax), var(--ay)); }
  50% { transform: translate(var(--bx), var(--by)); }
  75% { transform: translate(var(--cx), var(--cy)); }
}
@keyframes blink {
  0%, 35%, 100% { opacity: 0.12; }
  50% { opacity: 1; }
  65% { opacity: 0.3; }
}
@keyframes sway {
  from { transform: rotate(-4deg); }
  to   { transform: rotate(4deg); }
}
```
