How to Make Popovers Origin-Aware in UI Animations

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 (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:

/* 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 (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:

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:

/* 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 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 (lines 236-240), modals should not use the --transform-origin variable:

/* 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:

Summary

  • Default centered origins are incorrect for popovers, dropdowns, and tooltips according to 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 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 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 (lines 54-56), which establishes that the default centered origin is incorrect for popovers. Additional context appears in skills/emil-design-eng/SKILL.md (lines 236-240) regarding the modal exception, and implementation recipes are provided in skills/animate/RECIPES.md.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →