# How to Make Popovers Origin-Aware in UI Animations

> Learn how to make popovers origin-aware in UI animations. Scale animations from the trigger element using CSS transform-origin and dynamic custom properties for smoother interactions.

- Repository: [Emil Kowalski/skills](https://github.com/emilkowalski/skills)
- Tags: how-to-guide
- Published: 2026-08-08

---

**To make popovers origin-aware in UI animations, set `transform-origin: var(--transform-origin)` in your CSS and dynamically calculate the `--transform-origin` custom property from the trigger element's bounding box coordinates, causing the animation to scale from the trigger rather than the popover's center.**

Popovers, dropdowns, menus, and tooltips should animate from the point where they are triggered to maintain clear spatial relationships with their anchor elements. The `emilkowalski/skills` repository establishes strict standards for making popovers origin-aware in UI animations, requiring developers to replace the default centered transform origin with coordinates derived from the trigger element. This technique ensures scaling animations appear to originate from the specific button or interaction point that opened the popover, creating a more natural and responsive user experience.

## Why Default Transform Origins Fail

The standard CSS default of `transform-origin: center` is **incorrect** for trigger-anchored popovers. When a popover scales in from its own center point, it visually disconnects from the trigger element that spawned it, creating a jarring spatial discontinuity.

According to [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) (line 54), this default behavior breaks the perceived relationship between the trigger and the floating component. The documentation explicitly requires origin-aware scaling for all popover-type components to preserve the hierarchical connection between elements.

## Implementing the `--transform-origin` Custom Property

The repository defines a CSS custom property `--transform-origin` that carries the trigger's coordinates to the popover component. This variable bridges the gap between JavaScript measurements and CSS animations.

### Base CSS Setup

In your global stylesheet, apply the custom property to popover components:

```css
/* Base UI – defines the origin based on the trigger element */
.popover {
  /* --transform-origin is set by the component that renders the popover,
     typically using JavaScript to copy the trigger's bounding box. */
  transform-origin: var(--transform-origin);
}

```

As specified in [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) (line 56), this declaration enables the origin-aware animation pattern throughout the component library.

### Calculating Trigger Coordinates

The `--transform-origin` value must be calculated from the trigger's bounding rectangle and applied inline or via JavaScript:

```tsx
import { useRef, useEffect } from "react";

function Popover({ triggerRef, children }) {
  const popoverRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (triggerRef.current && popoverRef.current) {
      const rect = triggerRef.current.getBoundingClientRect();
      // Set the CSS variable on the popover element
      popoverRef.current.style.setProperty(
        "--transform-origin",
        `${rect.left + rect.width / 2}px ${rect.top + rect.height / 2}px`
      );
    }
  }, [triggerRef]);

  return (
    <div className="popover" ref={popoverRef}>
      {children}
    </div>
  );
}

```

This calculation centers the transform origin on the trigger element, though you may adjust the coordinates to target specific corners or edges depending on the popover's placement.

## Animation Implementation

Once the transform origin is established, CSS transitions can leverage the variable to create origin-aware scaling:

```css
/* Simple scale-in animation that respects the origin-aware transform */
.popover[data-starting-style] {
  opacity: 0;
  transform: scale(0.8);
  transition: opacity 150ms ease, transform 150ms ease;
}

.popover[data-ending-style] {
  opacity: 1;
  transform: scale(1);
}

```

The [`skills/animate/RECIPES.md`](https://github.com/emilkowalski/skills/blob/main/skills/animate/RECIPES.md) file provides concrete CSS recipes for implementing these patterns across popovers and dropdowns, ensuring consistent timing and easing curves.

## The Modal Exception

Modals represent the only exception to the origin-aware rule. Because modals lack a specific trigger anchor and typically appear centered in the viewport, they retain the default centered transform origin.

As documented in [`skills/emil-design-eng/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/emil-design-eng/SKILL.md) (lines 236-240), modals should not use the `--transform-origin` variable:

```css
/* Modals should retain the default centered origin */
.modal {
  transform-origin: center;
}

```

This distinction ensures that modal dialogs—which interrupt the user flow rather than expanding from a specific control—maintain their centered appearance while popovers maintain their contextual connections.

## Source File References

The origin-aware animation standards are defined across several key files in the repository:

- [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) – Defines the popover origin rule and the `--transform-origin` variable convention
- [`skills/emil-design-eng/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/emil-design-eng/SKILL.md) – Explains the rationale for origin-aware popovers and documents the modal exception
- [`skills/animate/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/animate/SKILL.md) – Summarizes the `transform-origin` convention for all trigger-anchored components
- [`skills/animate/RECIPES.md`](https://github.com/emilkowalski/skills/blob/main/skills/animate/RECIPES.md) – Provides implementation recipes for dropdowns, menus, and tooltips

## Summary

- **Default centered origins are incorrect** for popovers, dropdowns, and tooltips according to [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md)
- **Use `transform-origin: var(--transform-origin)`** to enable dynamic origin positioning based on trigger coordinates
- **Calculate the custom property** from the trigger element's `getBoundingClientRect()` values in JavaScript
- **Modals remain centered** and do not use the `--transform-origin` variable since they lack specific trigger anchors
- **Reference [`skills/animate/RECIPES.md`](https://github.com/emilkowalski/skills/blob/main/skills/animate/RECIPES.md)** for concrete CSS implementation patterns

## Frequently Asked Questions

### How do I calculate the correct `--transform-origin` value for a popover?

Calculate the value using the trigger element's bounding rectangle. In JavaScript, call `getBoundingClientRect()` on the trigger element, then combine the left/top coordinates with half the width and height to target the center, or use edge coordinates to target specific corners. Set this value via `element.style.setProperty('--transform-origin', calculatedValue)` on the popover container.

### Why shouldn't modals use the origin-aware pattern?

Modals should retain `transform-origin: center` because they are not anchored to specific trigger elements. Unlike popovers that expand from buttons or menu items, modals typically center themselves in the viewport to indicate a break from the current workflow. The [`skills/emil-design-eng/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/emil-design-eng/SKILL.md) documentation explicitly exempts modals from the `--transform-origin` requirement.

### What components should follow the origin-aware animation rule?

According to the repository standards, **popovers, dropdowns, menus, and tooltips** must implement origin-aware animations. These components are triggered by specific user interactions with anchor elements, making the spatial relationship between trigger and floating component essential for usability.

### Where is the `--transform-origin` convention documented?

The convention is primarily documented in [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) (lines 54-56), which establishes that the default centered origin is incorrect for popovers. Additional context appears in [`skills/emil-design-eng/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/emil-design-eng/SKILL.md) (lines 236-240) regarding the modal exception, and implementation recipes are provided in [`skills/animate/RECIPES.md`](https://github.com/emilkowalski/skills/blob/main/skills/animate/RECIPES.md).