# glimm

WebGL **sweep transitions** for the web. A colour band sweeps across
the viewport, your page swaps underneath it at the midpoint, then the band fades
out — the iOS "name drop" feel, as a page transition.

- **Framework-agnostic core** — drive the shader directly from vanilla JS.
- **React adapter** — `<GlimmProvider>` + `useGlimm()`.
- **Next.js adapter** — `<TransitionLink>`, `useTransitionRouter()`, auto link interception.
- Zero runtime dependencies. WebGL only — no CSS, no assets.
- Respects `prefers-reduced-motion` and degrades gracefully when WebGL is unavailable.

## Install

```bash
npm install glimm
```

`react` and `next` are optional peer dependencies — install whichever adapter
you use. The core entry point needs neither.

## Quick start (Next.js, App Router)

```tsx
// app/layout.tsx
import { GlimmProvider, InterceptLinks } from 'glimm/next'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <GlimmProvider palette="prism">
          <InterceptLinks />
          {children}
        </GlimmProvider>
      </body>
    </html>
  )
}
```

`<InterceptLinks />` runs every same-origin link click through a sweep. Opt a
link out with `data-glimm-skip`.

### Per-navigation control

```tsx
'use client'
import { TransitionLink, useTransitionRouter } from 'glimm/next'

// Declarative — a drop-in <Link> replacement:
<TransitionLink href="/about" sweep={{ palette: 'berry', direction: 'rtl' }}>
  About
</TransitionLink>

// Programmatic — a drop-in useRouter():
const router = useTransitionRouter()
await save(form)
router.push('/dashboard', { palette: 'ember' })
```

## React (without Next.js)

```tsx
import { GlimmProvider, useGlimm } from 'glimm/react'

function MyButton() {
  const { sweep } = useGlimm()
  return <button onClick={() => sweep(() => doNavigation())}>Go</button>
}
```

`sweep(navigate, options?)` plays the band, awaits your `navigate` callback at
the midpoint, and returns a `{ midpoint, done, cancel }` handle.

## Vanilla / framework-agnostic core

```ts
import { createShader, playSweep } from 'glimm'

const canvas = document.querySelector('canvas')!
const ctrl = createShader({ canvas, /* palette, bandTight, direction */ })

playSweep(ctrl, {
  palette: 'citrus',
  onMidpoint: () => swapPageContent(),
  onComplete: () => ctrl.destroy(),
})
```

Alternate looks: `createMeshShader` (vertex-displaced mesh) and
`createNamedropShader` (radial bulge reveal).

## Sweep options

| Option        | Default  | Notes |
| ------------- | -------- | ----- |
| `sweepMs`     | `1100`   | ms for the band to cross the viewport |
| `outroMs`     | `700`    | ms for the post-traversal fade-out |
| `midpoint`    | `0.56`   | 0–1 progress point where `onMidpoint` fires |
| `palette`     | `prism`  | preset name or a custom `Palette` |
| `direction`   | `ltr`    | `ltr` \| `rtl` \| `ttb` \| `btt` |
| `easing`      | `ease` | preset name or an `(p) => number` fn |
| `bandTight`   | —        | band sharpness |
| `peakAlpha`   | `1`      | caps the band's peak opacity |
| `brightness`  | `1`      | 0–1.5, multiplies band RGB (dim it on dark backgrounds) |
| `waveAmount`  | `0`      | 0–2, opt-in edge displacement; 0 is straight |
| `rippleAmount`| `1`      | 0–2, vertical ripple texture |
| `waveSpeed`   | `1`      | 0–3, multiplies time-based motion |
| `swellAmount` | `0.55`   | 0–1, depth swell |
| `onMidpoint`  | —        | fires when the band reaches `midpoint` — swap pages here |
| `onComplete`  | —        | fires when the outro fade finishes |

**Built-in palettes:** `prism`, `berry`, `lagoon`, `citrus`, `azure`,
`ember`. Build your own with `accentPair`,
`accentChain`, or `shuffleAccentPalette`.

**Built-in easings:** `linear`, `easeOutQuart`, `easeOutCubic`, `easeInCubic`,
`easeInOutCubic`, `easeOutExpo`, `easeInOutQuint`, `snap`, `ease`, `back`.

## Entry points

| Import          | Use for |
| --------------- | ------- |
| `glimm`         | Framework-agnostic core: shader factories, `playSweep`, palettes, easings, colour math. |
| `glimm/react`   | `<GlimmProvider>` and `useGlimm()` for any React app. |
| `glimm/next`    | Everything in `glimm/react` plus `<TransitionLink>`, `useTransitionRouter()`, and `interceptLinks` / `<InterceptLinks>`. |

Import the provider and hooks from a **single** entry point per app so they
share one React context.

## License

[MIT](./LICENSE) © Noman Ijaz. Free to use, modify, and distribute.
