# BeeLoader

> A bumblebee flying figure-eights over a swaying flower, wings a blur, turning to face the way it is going. The path is two eased sways at a 1:2 ratio (a Lissajous curve), so it loops smoothly every 2.4s.

- Page: https://www.boatui.dev/loaders/bee
- Source: https://github.com/wi11s/boatui/blob/main/components/loaders/bee-loader.tsx
- Animated. Generate: ~1,005 tokens (source size, single pass). Import: ~16 tokens.

## Install

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

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

## Usage

```tsx
import { BeeLoader } from '@/components/loaders/bee-loader';

export function Saving() {
  return <BeeLoader size={48} label="Saving your changes…" />;
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `theme` | `Partial<BeeLoaderTheme>` | — | Colour overrides. Keys listed under theme tokens. |
| `size` | `number` | 64 | Width and height in pixels. |
| `label` | `string` | "Loading…" | Announced to screen readers through role="status". |
| `paused` | `boolean` | false | Freezes the animation. |
| `className` | `string` | — | Applied to the root element (an inline-flex span). |
| `style` | `CSSProperties` | — | Merged into the root element style, after theme variables. |

## Theme tokens

| Key | CSS variable | Default |
| --- | --- | --- |
| `body` | `--body` | `#f6c445` |
| `stripes` | `--stripes` | `#3d3a3a` |
| `wings` | `--wings` | `#e6f3fb` |
| `petals` | `--petals` | `#f49ab5` |
| `centre` | `--centre` | `#f2c14e` |
| `stem` | `--stem` | `#6cbf6a` |

## Source

`components/loaders/bee-loader.tsx`

```tsx
// <BeeLoader>: a bumblebee flying figure-eights over a flower, wings a blur, turning to face
// the way it's going. The figure-eight is two eased sways at a 1:2 ratio (a Lissajous curve).

import { LoaderFrame, type LoaderProps } from './loader';
import styles from './bee-loader.module.css';

export const beeLoaderTheme = {
  body: '#f6c445',
  stripes: '#3d3a3a',
  wings: '#e6f3fb',
  petals: '#f49ab5',
  centre: '#f2c14e',
  stem: '#6cbf6a',
};
export type BeeLoaderTheme = typeof beeLoaderTheme;

/** A bumblebee flying figure-eights over a flower. */
export function BeeLoader(props: LoaderProps<BeeLoaderTheme>) {
  return (
    <LoaderFrame
      {...props}
      defaultTheme={beeLoaderTheme}
      art={
        <>
          {/* Flower: sways from the base of its stem */}
          <g className={styles.flower}>
            <path d="M50 94 C50 84 48 78 50 70" fill="none" stroke="var(--stem)" strokeWidth="3" strokeLinecap="round" />
            <path d="M50 86 C44 84 40 80 40 76 C45 76 49 80 50 86 Z" fill="var(--stem)" />
            <g fill="var(--petals)">
              {[0, 72, 144, 216, 288].map(a => (
                <ellipse key={a} cx="50" cy="62" rx="4.6" ry="7" transform={`rotate(${a} 50 69)`} />
              ))}
            </g>
            <circle cx="50" cy="69" r="4.5" fill="var(--centre)" />
          </g>

          {/* Bee: x sways once per 2.4s, y twice, a quarter-beat apart, tracing a figure eight */}
          <g transform="translate(50 36)">
            <g className={styles.x}>
              <g className={styles.y}>
                <g className={styles.face}>
                  <g className={styles.bob}>
                    <ellipse className={styles.wing} cx="-2" cy="-7" rx="5" ry="7" fill="var(--wings)" opacity="0.9" />
                    <ellipse className={`${styles.wing} ${styles.wingBack}`} cx="2" cy="-6" rx="4" ry="6" fill="var(--wings)" opacity="0.7" />
                    <path d="M-12 1 L-15 2 L-12 3 Z" fill="var(--stripes)" />
                    <ellipse cx="0" cy="2" rx="12" ry="8.5" fill="var(--body)" />
                    <path d="M-5 -6 Q-7 2 -5 10 M1 -6.5 Q-1 2 1 10.5" fill="none" stroke="var(--stripes)" strokeWidth="3" />
                    <circle cx="9" cy="1" r="5" fill="var(--stripes)" />
                    <circle cx="10.5" cy="0" r="1.3" fill="#ffffff" />
                    <path d="M9 -4 Q10 -9 13 -10" fill="none" stroke="var(--stripes)" strokeWidth="1.2" strokeLinecap="round" />
                  </g>
                </g>
              </g>
            </g>
          </g>
        </>
      }
    />
  );
}
```

`components/loaders/bee-loader.module.css`

```css
/* A 2.4s figure eight. ease-in-out alternate approximates a sine, so x (1.2s each way) and
   y (0.6s each way, delayed a quarter of its cycle) trace a Lissajous curve at 1:2. */
.x { animation: x 1.2s ease-in-out infinite alternate; }
.y { animation: y 0.6s ease-in-out infinite alternate; animation-delay: -0.3s; }

/* Faces right on the way right, left on the way back; the turn happens at the ends of the sway. */
.face { animation: face 2.4s linear infinite; }

.bob { animation: bob 0.3s ease-in-out infinite alternate; }

.wing {
  transform-box: fill-box;
  transform-origin: 50% 100%;
  animation: flap 0.08s linear infinite alternate;
}
.wingBack { animation-delay: -0.04s; }

.flower {
  transform-box: fill-box;
  transform-origin: 50% 100%;
  animation: sway 2.4s ease-in-out infinite alternate;
}

@keyframes x {
  from { transform: translateX(-26px); }
  to   { transform: translateX(26px); }
}
@keyframes y {
  from { transform: translateY(-9px); }
  to   { transform: translateY(9px); }
}
@keyframes face {
  0%, 48%   { transform: scaleX(1); }
  52%, 98%  { transform: scaleX(-1); }
  100%      { transform: scaleX(1); }
}
@keyframes bob {
  from { transform: rotate(-4deg); }
  to   { transform: rotate(4deg); }
}
@keyframes flap {
  from { transform: scaleY(1); }
  to   { transform: scaleY(0.35); }
}
@keyframes sway {
  from { transform: rotate(-4deg); }
  to   { transform: rotate(4deg); }
}
```
