# Transform-Origin on Popovers vs Modals: Best Practices from Emil Kowalski's Design System

> Learn transform origin best practices for popovers and modals. Emil Kowalski's design system offers clear guidance for centered popovers and modals, ensuring consistency and visual balance.

- Repository: [Emil Kowalski/skills](https://github.com/emilkowalski/skills)
- Tags: best-practices
- Published: 2026-08-04

---

**Set `transform-origin` to the trigger element's center for popovers using `--transform-origin`, but keep it centered for modals to maintain spatial consistency and visual balance.**

The `emilkowalski/skills` repository establishes clear animation standards for overlay components, defining how `transform-origin` impacts user perception of spatial relationships. These guidelines differentiate between anchored elements like popovers and independent elements like modals, ensuring motion feels connected to user intent. Understanding the distinction between transform-origin on popovers vs modals is essential for creating intuitive, physically consistent interface animations.

## Why Transform-Origin Matters for Overlay Animations

The `transform-origin` property determines the pivot point for scale transformations, directly affecting how users perceive the origin of appearing UI elements. When animations scale from the wrong origin, they break the mental model of "source-to-target" interaction, creating disjointed experiences. According to the **Animation Standards Reference** in [`STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/STANDARDS.md), proper origin placement reinforces spatial relationships and reduces cognitive load during state transitions.

## Popover Transform-Origin Best Practices

### Connecting Motion to the Trigger Element

Popovers, dropdowns, and tooltips must use `var(--transform-origin)` set to the trigger's center coordinates. This approach anchors the animation to the element that invoked it, creating a visual connection between the user's action and the resulting interface. The "Physicality" section of [`STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/STANDARDS.md) emphasizes that scaling from the trigger informs users where the new UI originated, maintaining spatial consistency across the viewport.

### Implementation with CSS Custom Properties

Base UI provides the `--transform-origin` custom property to dynamically calculate the trigger's bounding box center. Avoid starting from `scale(0)`; instead, use `scale(0.95)` combined with opacity transitions for natural reveals. Ensure no other styles override this origin, as the connection to the trigger element is critical for perceived responsiveness.

## Modal Transform-Origin Best Practices

### Centered Scaling for Visual Balance

Modals and drawers must use `transform-origin: center` because they appear independently of specific trigger elements, typically centered in the viewport. This creates a symmetric, calm entrance that does not imply directional cues, appropriate for full-screen overlays that dim the background. The exemption for modals is explicitly documented in the transform-origin table within [`SKILL.md`](https://github.com/emilkowalski/skills/blob/main/SKILL.md), distinguishing them from anchored components.

## Implementation Examples

The following CSS demonstrates the distinct approaches for popovers and modals:

```css
/* Popover – scale from the trigger */
.popover {
  /* Base UI defines --transform-origin based on the trigger */
  transform-origin: var(--transform-origin);
  transition: transform 150ms ease-out, opacity 150ms ease-out;
  transform: scale(0.95);
  opacity: 0;
}

/* When the popover becomes visible */
.popover[data-open] {
  transform: scale(1);
  opacity: 1;
}

/* Modal – keep centered */
.modal {
  transform-origin: center;
  transition: transform 250ms ease-out, opacity 250ms ease-out;
  transform: scale(0.95);
  opacity: 0;
}

/* When the modal opens */
.modal[data-open] {
  transform: scale(1);
  opacity: 1;
}

```

For dynamic origin calculation in React applications:

```tsx
// React example using Base UI's CSS variable
import { useRef, useEffect } from "react";

function Popover({ open, anchorRef, children }) {
  const ref = useRef<HTMLDivElement>(null);

  // Update the CSS variable with the trigger's centre
  useEffect(() => {
    if (anchorRef.current && ref.current) {
      const rect = anchorRef.current.getBoundingClientRect();
      const originX = rect.left + rect.width / 2;
      const originY = rect.top + rect.height / 2;
      ref.current.style.setProperty("--transform-origin", `${originX}px ${originY}px`);
    }
  }, [anchorRef, open]);

  return (
    <div
      ref={ref}
      className="popover"
      data-open={open}
      role="dialog"
      aria-modal="false"
    >
      {children}
    </div>
  );
}

```

## Summary

- **Popovers require trigger-based origins**: Use `var(--transform-origin)` set to the anchor element's center to maintain spatial relationships and motion realism.
- **Modals require centered origins**: Use `transform-origin: center` for symmetric, balanced animations independent of trigger location.
- **Implementation uses CSS custom properties**: Base UI's `--transform-origin` enables dynamic calculation based on trigger bounding boxes.
- **Documentation resides in specific files**: Reference [`STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/STANDARDS.md) for the physicality rules and [`SKILL.md`](https://github.com/emilkowalski/skills/blob/main/SKILL.md) for the review checklist and design-engineering rationale.
- **Animation timing differs**: Popovers typically use 150ms transitions while modals use 250ms for appropriate perceived weight.

## Frequently Asked Questions

### What is the recommended transform-origin for popovers?

Popovers should use `var(--transform-origin)` pointing to the trigger element's center coordinates. This creates a scaling animation that appears to emerge from the button or element that opened it, reinforcing the connection between the user's action and the resulting interface.

### Why should modals use center transform-origin?

Modals use `transform-origin: center` because they are not tied to specific trigger elements and typically appear centered in the viewport. This creates a balanced, symmetric entrance that does not suggest a directional relationship with any particular UI element, appropriate for full-screen overlays that demand equal visual attention across the entire surface.

### How do I implement dynamic transform-origin for popovers?

Calculate the trigger element's bounding box center using `getBoundingClientRect()`, then set the `--transform-origin` CSS custom property on the popover element to `${originX}px ${originY}px`. Base UI provides this variable infrastructure, or you can define it manually in your design system.

### Where are these transform-origin standards documented?

The standards are documented in [`STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/STANDARDS.md) within the "Physicality" section and the transform-origin table, with additional implementation guidance in [`SKILL.md`](https://github.com/emilkowalski/skills/blob/main/SKILL.md) under the review-animations and emil-design-eng sections. These files define the canonical approach for handling transform origins across the codebase.