# Glassblow — Animated Card for React

> Image card whose surface behaves like molten glass: a low-res viscous height field (heavy damping, ~1.5s relaxation) sinks a broad soft dent under the pointer, stretches an elongated pull on fast swipes, and blows stacking bubbles that swell for 400ms and pop into a relaxing ring. The image refracts through the slopes with one soft specular, faint caustics in the hollows and a thin chromatic split on steep slopes — slow and heavy, never watery. Idle ghost press, slump-flat intro, press/blow handle, seeded sea-glass plate. OGL, DPR-clamped, offscreen-paused, reduced-motion-safe.

- Page: https://crucibleui.com/components/cards/glassblow
- Category: [cards](https://crucibleui.com/components/cards)
- Tier: pro (one-time purchase, see https://crucibleui.com/pricing)
- Engine: ogl
- npm dependencies: ogl

## Install

```bash
npx shadcn@latest add @crucible/glassblow
```

Pro install requires adding the token-bearing `@crucible` registry to components.json (issued at https://crucibleui.com/account after purchase).

Import from `@/components/crucible/glassblow`.

## Props (`GlassblowProps`)

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `src?` | `string` | — | Image URL shown through the glass, cover-fit to the card. Loaded with `crossOrigin="anonymous"`, so cross-origin hosts must send CORS headers — if it fails to load, the built-in procedural plate stays up. Omit to show the procedural plate: a seeded sea-glass dusk over layered dunes. |
| `alt?` | `string` | — | Accessible description of the image. |
| `refraction?` | `number` | `0.5` | How strongly the glass bends the image along its slopes, 0–1. 0 shows the image undistorted (highlights only). |
| `viscosity?` | `number` | `0.6` | Thickness of the molten glass, 0–1. Higher sinks more slowly under the pointer and takes longer to relax flat (0.6 ≈ 1.5s). |
| `interactive?` | `boolean` | `true` | Pointer and keyboard input: hovering sinks a dent, fast swipes pull the glass, click / Enter / Space blows a bubble, arrow keys press. The idle ghost press keeps running either way. |
| `paused?` | `boolean` | — | Freeze the glass on its current frame. |
| `colors?` | `[string, string, string]` | `a teal sea-glass dusk` | Procedural-plate palette `[deep, sea, light]`: zenith + near dunes, horizon haze + far dunes, sun + crest light. Ignored when `src` is shown. |
| `seed?` | `number` | `11` | Seed for the procedural plate layout and the glass's resting waviness. |
| `children?` | `React.ReactNode` | — | Non-interactive overlay (captions, labels), rendered above the glass with pointer events disabled so input still reaches the surface. |
| `ref?` | `React.Ref<GlassblowHandle>` | — | React 19 ref prop → {@link GlassblowHandle}. |

Also accepts: `Omit<React.ComponentPropsWithoutRef<"div">, "children">`.

## Usage example

```tsx
import { Glassblow } from "@/components/crucible/glassblow";

export default function GlassblowDemo() {
  return (
    <div className="relative isolate flex h-full min-h-[360px] w-full items-center justify-center overflow-hidden rounded-xl bg-neutral-950 p-6">
      <div className="relative z-10 flex w-full max-w-xl flex-col items-center gap-4">
        <Glassblow
          alt="Sea-glass dusk over layered dunes"
          className="shadow-2xl shadow-black/60 ring-1 ring-white/10"
        />
        <p className="pointer-events-none text-sm text-neutral-500">
          Press into the glass — click to blow a bubble.
        </p>
      </div>
    </div>
  );
}
```

## Accessibility & performance

Honors `prefers-reduced-motion` with a designed static frame or fade (never a blank box) and passes `className` through. Pauses offscreen and on hidden tabs, clamps device pixel ratio, and tears down its GL/canvas context on unmount.
