Spatial & Directional Motion
10 minSpatial & Directional Motion
Motion in a user interface isn't abstract—it's spatial. Elements exist in a two-dimensional plane, and users build a mental model of where things are based on how they move. When motion respects that model, the interface feels coherent. When it doesn't—when a dropdown fades in from nowhere, or a drawer appears without sliding from its edge—the user loses spatial context and the interface feels disjointed.
The governing principle: motion should reinforce the spatial relationships that the layout implies.
Transform-origin: the anchor point
Every scale and rotate animation pivots around a transform-origin. The default is center center—the geometric middle of the element. This is correct for modals (which expand from their own centre into the viewport) and wrong for almost everything else.
A dropdown menu anchored to a button should scale from the top—transform-origin: top center—because it's attached to something above it. A context menu that appears where the user right-clicked should scale from the click point. A sidebar drawer should scale from its anchored edge—transform-origin: left center for a left-docked drawer.
The rule: the transform-origin should match the element's spatial relationship to whatever triggered it. If the user clicked a button in the top-right corner, the popover should scale from its top-left corner (the corner nearest the trigger). If a panel slides from the bottom edge, any scale animation should originate from the bottom.
/* Dropdown below a button */
.dropdown {
transform-origin: top center;
}
/* Context menu from right-click point */
.context-menu {
transform-origin: var(--click-x) var(--click-y);
}
/* Left-docked sidebar */
.sidebar {
transform-origin: left center;
}
Getting this wrong is one of the most common animation errors on the web. A popover that scales from its centre looks like it materialised from thin air. One that scales from its trigger's location looks like it emerged from the trigger—which is exactly the spatial metaphor you want.
Directional tab indicators
Tabs are one of the clearest tests of directional motion. When the user clicks a tab, the active indicator (usually an underline or background highlight) should slide from the previous tab to the new one. Not fade. Not jump. Slide—because the tabs exist in a horizontal row, and the indicator's motion should reflect that spatial arrangement.
This means tracking direction. If the user moves from Tab 2 to Tab 4, the indicator slides right. If they move from Tab 4 to Tab 1, it slides left. The indicator's motion should match the direction the user's attention is traveling.
The implementation is a layout animation on the indicator element, positioned absolutely within the tab bar. Framer Motion's layoutId makes this straightforward—apply the same layoutId to the active indicator in each tab, and the library handles the directional interpolation automatically.
{tabs.map((tab) => (
<button key={tab.id} onClick={() => setActive(tab.id)}>
{tab.label}
{active === tab.id && (
<motion.div
layoutId="tab-indicator"
className="absolute inset-x-0 bottom-0 h-0.5 bg-primary"
transition={{ type: "spring", stiffness: 500, damping: 40 }}
/>
)}
</button>
))}
Continuity: shared elements across states
When an element is visible in both the "before" and "after" state of a transition, it should transition in place—morphing from its old size, position, and shape to its new one. This is the continuity principle. It tells the user "this is the same thing, in a new context," rather than "the old thing disappeared and a new thing appeared."
The classic example is a list-to-detail transition. The user clicks a card in a list, and the card's image expands to fill the detail view's hero area. The user understands they're looking at the same item—the spatial continuity confirms it.
The opposite—cutting to the detail view with no shared-element transition—forces the user to re-orient. "Wait, is this the same item I clicked?" The cognitive cost is small but real, and it compounds across a session.
Emerge from trigger
Overlays—modals, popovers, context menus, dropdown sheets—should animate from the element that opened them. A modal triggered by a "Settings" button in the top-right corner should scale outward from that corner, not from the centre of the viewport. A popover triggered by an avatar should emerge from the avatar.
This isn't just aesthetic. It's spatial communication. The user clicked a specific point in the interface, and the overlay's emergence from that point confirms the causal relationship: "You clicked here, and this is what appeared." When overlays fade in from nowhere, that causal link is broken.
The implementation requires passing the trigger element's bounding rect to the overlay and using it as the transform-origin. For modals, this can be simplified—animate scale from 0.95 and opacity from 0 with a transform-origin set to the trigger's position.
Scale starting values
When an element scales in (entering from a smaller size), the starting scale matters more than most developers realise. Too small (scale: 0) looks cartoonish—the element appears to grow from a point, which doesn't match any physical metaphor in a UI context. Too close to 1 (scale: 0.99) is imperceptible—the animation runs but the user doesn't register it.
The sweet spot is 0.85 to 0.95, depending on the element:
- Modals and large overlays:
scale: 0.95— subtle but perceptible - Popovers and dropdowns:
scale: 0.9— slightly more pronounced, appropriate for smaller elements - Tooltips:
scale: 0.85— visible at tooltip's small size - Buttons (press feedback):
scale: 0.97— barely visible, tactile
Pair the scale with an opacity transition (0 to 1) and the correct transform-origin, and the element appears to "grow into" its position from the point that triggered it.
Direction and user flow
Motion direction should follow the user's mental model of navigation:
- Forward navigation (drilling deeper): content enters from the right, exits to the left.
- Backward navigation (going up): content enters from the left, exits to the right.
- Vertical lists: expanded detail panels push content down, collapsed panels pull content up.
- Mobile sheets: enter from the bottom (where the thumb is), exit downward.
When direction contradicts the spatial model—a "back" action that slides content in from the right—the user's mental map breaks. They lose their sense of "where am I in the interface," even if they can't articulate why.
The direction of motion is one of the cheapest and highest-signal spatial cues available. Get it right, and the interface feels navigable. Get it wrong, and the interface feels disorienting. There's no middle ground.