> Aperture 0.1.0 — experimental, unpublished. [Package archive](/downloads/bolted-aperture-0.1.0.tgz).

# Integrate Aperture

Aperture is an experimental, MIT-licensed reveal primitive for modern web projects. It animates an SVG clip over existing HTML. It is not a dialog manager, router, page builder, or Three.js renderer. The showcase's 3D hero is separate from the library.

## Start with the project

Inspect the existing framework, dependencies, component lifecycle, and intended interaction before editing. Preserve the project's UI and conventions. Use a supplied recipe exactly; do not approximate it with a different effect or change its values without a reason.

The package is **not published to npm**. Install the matching downloaded `bolted-aperture-VERSION.tgz` from a local path, or use a starter that bundles it. Read the installed `package.json` for its version. Do not claim a registry installation succeeded or substitute a similarly named package. React and GSAP are optional peers; the core needs neither. Use the project's package manager.

## Choose an entry point

- [Vanilla / any framework](vanilla.md): `createAperture` from `@bolted/aperture`.
- [React](react.md): `useAperture` from `@bolted/aperture/react`; React 18.2+ or 19.
- [Next.js App Router](next.md): the React hook inside a client component; keep routing in the app.
- GSAP is optional: `gsapDriver` from `@bolted/aperture/gsap`, plus the `gsap` peer. No plugins required. Use the native driver unless GSAP is useful to the project.
- Portable data: `parseRecipe`, `validateRecipe`, `recipeToOptions`, `recipeSchema` from `@bolted/aperture/recipes`.
- Effect metadata: `catalog`, `effects`, `defaultRecipe`, `APERTURE_VERSION` from `@bolted/aperture/catalog`.

## Layout and lifecycle

Give the host explicit dimensions, `position: relative`, `overflow: hidden`, and zero padding. The content must be its **direct child**. Aperture positions that child to fill the host. Put persistent open/close controls outside the clipped child. Mount only after both elements exist; destroy on unmount. Module imports are safe during SSR, but controller creation needs DOM elements.

`open(origin?)`, `close()`, and `toggle()` resolve to `{status, progress}`. Status is `completed`, `interrupted`, or `destroyed`. Check completion before navigation or moving focus into the revealed content. New requests interrupt old ones. `seek(0..1)` cancels playback. `setOptions()` patches supplied values; omitted values retain their prior setting. `destroy()` is idempotent and restores owned styles and accessibility state.

Content remains inert and hidden from assistive technology until fully open. Your app owns focus, Escape, scroll locking, modal semantics, and navigation. Prefer native `<dialog>` for modal experiences and keep a reachable close control outside the clip. Study the working starter rather than treating a primitive snippet as a complete modal.

## Portable recipes

Read [the v1 schema](schema/recipe-v1.schema.json) and [effect catalog](catalog.json). Recipes contain only version, preset, duration, spread, rotation, easing, normalized origin, and two six-digit hex colors. Recipe bounds match the workbench; the imperative API's wider options are documented separately in the package README.

```ts
import { parseRecipe, recipeToOptions } from "@bolted/aperture/recipes";
const recipe = parseRecipe(jsonText, { strict: true });
const options = recipeToOptions(recipe);
// createAperture({ host, content, ...options });
// useAperture(options);
```

`version: 1` describes the recipe format, not the package version or a controller setting. Never pass it as a setting. Strict validation rejects unknown fields; default validation drops them to preserve existing imports. Both validate types, finite numbers, bounds, presets, easings, and colors; neither executes code. Text imports are limited to 32,768 characters. Keep files under 32KB as well.

Recipe share links store JSON after `#recipe=`. HTTP fetches do not receive that fragment. A copied AI brief therefore embeds the full recipe. If only a share URL is supplied, decode its fragment instead of fetching the URL and guessing.

## Compatibility and evidence

The library uses ESM/ES2022. Animation needs SVG clip-path syntax support, native inert, ResizeObserver, animation frames and matchMedia. Missing capabilities use `mode: 'instant'` with the same promises and no animation driver/SVG. This cannot rescue a browser unable to load modern JavaScript. Automatic transitions respect system Reduce Motion; `reducedMotion: 'always'` also disables animation. Manual scrubbing remains available when animation capabilities exist.

Capability detection is not a rendering certification. Physical iPhone/Safari verification is still pending. Do not claim universal browser compatibility or performance measurements without testing. Finish with the [verification checklist](verification.md), and report what was actually checked.
