# RevealLens

**When to use:** A two-layer reveal/magnifier effect — an `overlay` covers a `background` by default, and moving the pointer over the component uncovers the `background` in an area around the cursor. `background`/`overlay` are plain `ReactNode`s, so either can be an image, a solid colour block, or any other component (even an animated one) — swap two images on hover, reveal a crisp image over a blurred version, or magnify any custom content.

**Keywords:** image compare, before after

**Import:** `import { RevealLens } from '@ahrowe/ui'`

```tsx
import { RevealLens } from '@ahrowe/ui';

// Swap two images on hover
<RevealLens
  style={{ width: 480, height: 300 }}
  background={<img src="/photo-alt.jpg" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />}
  overlay={<img src="/photo.jpg" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />}
/>

// Crisp image revealed over a blurred version — a magnifying-glass effect
<RevealLens
  style={{ width: 480, height: 300 }}
  background={<img src="/photo.jpg" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />}
  overlay={
    <img
      src="/photo.jpg"
      style={{ width: '100%', height: '100%', objectFit: 'cover', filter: 'blur(8px)' }}
    />
  }
  radius={90}
  feather={40}
/>

// Any component as either layer
<RevealLens
  style={{ width: 480, height: 300 }}
  background={<AnimatedLogo />}
  overlay={<div style={{ background: 'var(--primary-color)' }}>Hover to reveal</div>}
/>

// Hard edge (no soft feather)
<RevealLens background={<Crisp />} overlay={<Faded />} radius={80} feather={0} />

// A fully custom CSS mask instead of the built-in circle
<RevealLens
  background={<Crisp />}
  overlay={<Faded />}
  maskImage="linear-gradient(to right, transparent, white)"
/>
```

**Sizing:** `background` and `overlay` are stacked in the same CSS grid cell rather than given a size of their own, so give the root an explicit size via `style`/`className` (e.g. `style={{ width: 480, height: 300 }}`) whenever they're sized in percentages (`width: '100%'`, as in the examples above) and need something to size against. Left unset, `RevealLens` sizes itself to fit its content, taking the larger of `background`/`overlay`'s natural size.

**Touch:** built on Pointer Events, so it works the same for mouse and touch — dragging a finger across the component moves the reveal like a magnifier. The reveal snaps back to the overlay when the pointer/touch ends or leaves.

**Key props:**

| Prop | Type | Description |
|------|------|-------------|
| `background` | `ReactNode` | Bottom layer, revealed around the pointer (required) |
| `overlay` | `ReactNode` | Top layer, covers `background` by default (required) |
| `radius` | `number` | Reveal radius in px (default `120`) |
| `feather` | `number` | Soft edge width in px; `0` gives a hard edge (default `24`) |
| `shape` | `'circle' \| 'custom'` | Documents the reveal shape being used (default `'circle'`); the actual mask is a circle unless `maskImage` is set |
| `maskImage` | `string` | Raw CSS `mask-image` value, used verbatim instead of the built-in circle gradient — the escape hatch for a non-circular reveal |
| `disabled` | `boolean` | Disables pointer tracking — `overlay` stays fully visible (default `false`) |

**Global defaults:** adopts `ConfigProvider`, e.g. `defaultProps={{ RevealLens: { radius: 150 } }}`. See [ConfigProvider.md](ConfigProvider.md).

**Slots:** `root` `background` `overlay`
