CircleReveal

Iris/circle-wipe transition — animates clip-path: circle(…) from a point outward (open) or from full coverage back to a point (close). Use it as a one-shot reveal on any element, or wire it into route changes with the companion PageIris wrapper for full page transitions.

mode=open · origin=50% 50%
iris open

Usage

import { CircleReveal } from '@bwo-ui/react';

// One-shot iris-open on a hero image
<CircleReveal mode="open" duration={0.9}>
  <img src="/hero.jpg" alt="" />
</CircleReveal>

// Off-center origin (15% from the left, 15% from the top)
<CircleReveal mode="open" origin={{ x: '15%', y: '15%' }}>
  <Card />
</CircleReveal>

// Close-iris dismissal — fires onComplete when finished
<CircleReveal mode="close" duration={0.5} onComplete={() => setShown(false)}>
  <Modal />
</CircleReveal>

Page transitions with PageIris

PageIris wraps your route content with a CircleReveal that replays whenever its pathname prop changes — so every navigation gets a fresh iris-open. It's framework-agnostic: pass usePathname() in Next.js, useLocation().pathname in React Router, or any string that changes per route.

// app/layout.tsx (Next.js)
'use client';
import { usePathname } from 'next/navigation';
import { PageIris } from '@bwo-ui/react';

export default function RootLayout({ children }) {
  const pathname = usePathname();
  return (
    <html lang="en">
      <body>
        <PageIris pathname={pathname ?? '/'} duration={0.8}>
          {children}
        </PageIris>
      </body>
    </html>
  );
}

CircleReveal props

PropTypeDefaultDescription
mode'open' | 'close''open'open animates the visible circle from origin outward. close shrinks the visible circle back into the origin.
origin{ x?: number | string; y?: number | string }{ x: '50%', y: '50%' }Centre of the circle. Numbers convert to percent, strings pass through (e.g. "120px", "center"). Defaults to the geometric centre of the element.
durationnumber0.7Animation duration in seconds.
easestring'expo.inOut'GSAP ease.
delaynumber0Initial delay before the tween starts.
onComplete() => voidFires when the animation finishes. Use it to unmount the element after a close iris.
primebooleantrueSynchronously set the start clip-path on mount so the first frame matches. Set to false if you need GSAP to manage the initial state itself.
asElementType'div'HTML tag or component to render the wrapping element as.

PageIris props

PropTypeDefaultDescription
pathnamestringAnything that changes on route change. Pass usePathname() / useLocation().pathname / your own key. The iris replays whenever this value changes.
durationnumber0.8Animation duration in seconds.
easestring'expo.out'GSAP ease.
origin{ x?: number | string; y?: number | string }{ x: '50%', y: '50%' }Centre of the iris. Same shape as CircleReveal#origin.
classNamestringWrapper className — useful for sizing the iris container.
styleReact.CSSPropertiesWrapper inline styles.