# Logo Spinner

The animated Plane mark, shown while a whole screen is waiting on app boot or a page transition.

## Installation

```tsx
import { LogoSpinner } from "@makeplane/propel/components/logo-spinner";
```
## Usage

The animated Plane mark, for the moments a whole screen is waiting — app boot, a splash screen, a
full-page route transition. For pending state inside a control or a section, use `CircularProgress`
(or a control's own loading state) instead: this is brand furniture, not a status glyph.

Both themes' artwork is rendered and CSS shows the one matching the active `data-theme`, so a theme
flip costs no JS and no second request (both files load with the page — 88KB together). Under
`prefers-reduced-motion: reduce` the same CSS paints a still of the fully drawn mark instead of the
animation, since a GIF cannot be paused. `alt` is required — name what the app is waiting on; only
the visible image is in the accessibility tree, so it is announced once.

```tsx
import { LogoSpinner } from "@makeplane/propel/components/logo-spinner";

export default function BasicDemo() {
  return <LogoSpinner size="md" alt="Loading Plane" />;
}
```
## Examples

### Sizes

Mark height: `sm` 24px inline, `md` 44px full page. The width follows the artwork's aspect ratio.

```tsx
import { LogoSpinner } from "@makeplane/propel/components/logo-spinner";

export default function SizesDemo() {
  return (
    <div className="flex items-center gap-6">
      <LogoSpinner size="sm" alt="sm mark" />
      <LogoSpinner size="md" alt="md mark" />
    </div>
  );
}
```

### Responsive

`size="fluid"` is the third step and the one a splash screen wants: 24px on a phone, 44px from the
640px breakpoint up. It is the same recipe the apps' own splash CSS used. Above that breakpoint it
is by definition indistinguishable from `md` — narrow the window past 640px to see it step down.

```tsx
import { LogoSpinner } from "@makeplane/propel/components/logo-spinner";

export default function ResponsiveDemo() {
  return <LogoSpinner size="fluid" alt="Loading Plane" />;
}
```
## Assets

This is the only propel component that ships binary assets: the mark is designed frame-by-frame
artwork (65 frames per theme), not something tokens and CSS can express. The GIFs ship inside the
package next to the component, each with a PNG still of the fully drawn mark for reduced motion,
and all four are pulled in as static asset imports, so your bundler turns them into URLs at build
time — Vite, webpack 5+, Parcel, or Rollup with an asset plugin.

If you render on the server (SSR, or a prerendered SPA shell), tell your bundler not to externalize
propel, or the asset import reaches Node — which cannot load a `.gif` — and the render fails:

```ts
// vite.config.ts
export default defineConfig({
  ssr: { noExternal: ["@makeplane/propel"] },
});
```

The files are also exported raw
(`@makeplane/propel/components/logo-spinner/logo-spinner-light.gif`,
`…/logo-spinner-light-still.png`), so an app's own pre-hydration splash CSS can point at the same
artwork instead of keeping a copy.

They are exported at 158×88 / 159×88, twice the largest size they are ever displayed at (44px tall),
so they stay crisp on 2x screens while weighing 39KB and 48KB; the stills are under 1KB each.

## API Reference

### LogoSpinner

The animated Plane mark, for the moments a whole screen is waiting — app boot, a splash screen, a
full-page route transition. For pending state inside a control or a section, use
`CircularProgress` (or a control's own loading state) instead: this is brand furniture, not a
status glyph.

Both themes' artwork is rendered and CSS shows the one matching the active `data-theme`, so a
theme flip costs no JS and no second request. Under `prefers-reduced-motion: reduce` the same CSS
paints a still of the fully drawn mark instead of the animation. Only the visible image is in the
accessibility tree (the other three are `display: none`), so `alt` is announced once.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` (required) | `"sm" \| "md" \| "fluid"` | — | Mark height: `sm` 24px, `md` 44px, `fluid` 24px stepping to 44px at the 640px breakpoint. |
| `alt` (required) | `string` | — | Alternative text (required) — what the app is waiting on, e.g. "Loading Plane". |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |
