Transform-Origin on Popovers vs Modals: Best Practices from Emil Kowalski's Design System
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, 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 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, distinguishing them from anchored components.
Implementation Examples
The following CSS demonstrates the distinct approaches for popovers and modals:
/* 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:
// 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: centerfor symmetric, balanced animations independent of trigger location. - Implementation uses CSS custom properties: Base UI's
--transform-originenables dynamic calculation based on trigger bounding boxes. - Documentation resides in specific files: Reference
STANDARDS.mdfor the physicality rules andSKILL.mdfor 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 within the "Physicality" section and the transform-origin table, with additional implementation guidance in SKILL.md under the review-animations and emil-design-eng sections. These files define the canonical approach for handling transform origins across the codebase.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →