---
title: FlipOverlay
description: A 3D overlay that flips open from its trigger.
category: Effects
status: beta
since: 0.3.4
tags: [overlay, motion, transition]
syncStatus: reviewed
verified: true
---

## Usage

### Basic
`source` sets where the flip starts; `headerTitle` and `headerDesc` fill the built-in header.
:::TuffDemoWrapper{demo="FlipOverlayFlipOverlayDemo" code-lang="vue"}
---
code: |
  <script setup lang="ts">
  import { ref } from 'vue'

  const open = ref(false)
  const triggerRef = ref()
  </script>

  <template>
    <TxButton ref="triggerRef" @click="open = true">Open Overlay</TxButton>
    <TxFlipOverlay
      v-model="open"
      :source="triggerRef?.$el"
      header-title="Detail View"
      header-desc="Title, description, and a round close button"
    >
      <template #default="{ close }">
        <p>Content flips in from the trigger.</p>
        <TxButton size="sm" @click="close">Close</TxButton>
      </template>
    </TxFlipOverlay>
  </template>
---
:::

### Best Practices

- Pass the real trigger element or its `DOMRect` as `source`; `null` falls back to a centered card with no origin.
- Keep `duration` near the default for stacked overlays so the shared mask and card motion stay in sync.
- Put size constraints (`width`, `maxHeight`) in `cardStyle` and reusable visual variants in `cardClass`.
- Use `surface="mask"` for normal cards, `glass` / `refraction` only when the backdrop stays readable, and `pure` for fully custom cards.
- Prefer `#header-display`, `#header-actions`, or `#header-close`; `#header` replaces the close layout too.

## API Reference

### Props

| Name | Type | Default | Description |
|------|------|---------|-------------|
| `modelValue` | `boolean` | `false` | Whether the overlay is shown; bind with `v-model`. |
| `source` | `HTMLElement \| DOMRect \| null` | `null` | Animation origin. |
| `sourceRadius` | `string \| null` | `null` | Corner radius of the origin. |
| `duration` | `number` | `480` | Animation duration in ms. |
| `perspective` | `number` | `1200` | 3D perspective distance. |
| `rotateX` | `number` | `6` | X-axis rotation. |
| `rotateY` | `number` | `8` | Y-axis rotation. |
| `randomTilt` | `boolean` | `true` | Adds a slight random tilt on each open. |
| `tiltRange` | `number` | `2` | Range of the random tilt. |
| `easeOut` | `string` | `'back.out(1.25)'` | Open easing. |
| `easeIn` | `string` | `'back.in(1)'` | Close easing. |
| `maskClosable` | `boolean` | `true` | Closes on a mask click or Escape. |
| `preventAccidentalClose` | `boolean` | `false` | Blocks mask close and page exit, flashing a red warning glow. |
| `globalMask` | `boolean` | `true` | Renders the shared body-level mask. |
| `surface` | `'pure' \| 'mask' \| 'blur' \| 'glass' \| 'refraction'` | `'mask'` | Built-in card surface. |
| `surfaceColor` | `string` | `''` | Surface base color; follows the theme by default. |
| `surfaceOpacity` | `number` | `0.96` | Surface opacity in `mask` mode. |
| `speedBoost` | `number` | `1.12` | Time-scale boost after `speedBoostAt` progress. |
| `speedBoostAt` | `number` | `0.7` | Animation progress at which `speedBoost` starts. |
| `transitionName` | `string` | `'TxFlipOverlay-Mask'` | Vue transition name for the mask. |
| `header` | `boolean` | `true` | Renders the built-in header; ignored when `#header` is provided. |
| `headerTitle` | `string` | `''` | Built-in header title, wired to `aria-labelledby`. |
| `headerDesc` | `string` | `''` | Built-in header description, wired to `aria-describedby`. |
| `closable` | `boolean` | `true` | Shows the close area, including `#header-close`. |
| `closeAriaLabel` | `string` | `'Close'` | `aria-label` of the close button. |
| `maskClass` | `string` | `''` | Mask class. |
| `cardClass` | `string` | `''` | Card class. |
| `cardStyle` | `CSSProperties` | - | Inline style for the card. |
| `border` | `'solid' \| 'dashed' \| 'dash' \| 'none'` | `'solid'` | Card border; `dash` aliases `dashed`. |
| `scrollable` | `boolean` | `true` | Scrolls the body area internally. |
| `expanded` | `boolean` | - | Controlled expanded-motion state for UI that must stay in sync. |
| `animating` | `boolean` | - | Controlled animation state for UI that must stay in sync. |

### Events

| Event | Params | Description |
|------|--------|-------------|
| `update:modelValue` | `(value: boolean)` | Emits `false` when the overlay closes itself. |
| `open` | - | The open animation starts. |
| `opened` | - | The open animation ends. |
| `close` | - | The close animation starts. |
| `closed` | - | The close animation ends. |
| `update:expanded` | `(value: boolean)` | Syncs `expanded`. |
| `update:animating` | `(value: boolean)` | Syncs `animating`. |

### Slots

| Slot | Params | Description |
|------|--------|-------------|
| `default` | `{ close, expanded, animating, closable, headerTitle, headerDesc }` | Body content. |
| `header` | `{ close, expanded, animating, closable, headerTitle, headerDesc }` | Replaces the whole built-in header. |
| `header-display` | `{ close, expanded, animating, closable, headerTitle, headerDesc }` | Title and description area. |
| `header-actions` | `{ close, expanded, animating, closable, headerTitle, headerDesc }` | Actions left of the close button. |
| `header-close` | `{ close, expanded, animating, closable, headerTitle, headerDesc }` | Close area; not rendered when `closable=false`. |

### Exposed Methods

| Method | Type | Description |
|--------|------|-------------|
| `close()` | `() => void` | Runs the full close animation and emits `update:modelValue(false)`. |

## Overview

- The overlay teleports to `<body>`; non-prop attributes land on the mask.
- Closing emits `close`, `update:modelValue(false)`, then `closed`; it fully closes once the parent writes `v-model` back.
- Mask clicks and Escape obey `maskClosable` and flash the warning under `preventAccidentalClose`; the close button and `close()` bypass both.
- Header priority: `#header` overrides the built-in header; otherwise `header` decides whether it renders.
- With `globalMask`, stacked overlays share one mask and only the top one takes clicks; similar-sized neighbors offset (up to 3 layers) and deeper layers fade out.
- The card is `role="dialog"` with `aria-modal="true"`; focus moves into it on open and returns to the previously focused element on close.

## Technologies

- The flip runs on lazily loaded GSAP tweens in `flip-overlay-motion.ts`.
- Source: `packages/tuffex/packages/components/src/flip-overlay/`.

<TuffDocSourceLink />
