Flip in
Entrance animation that flips elements from rotateY(-90deg) into view using CSS perspective for a convincing 3D reveal.
Quick implementation
.flip-container {
perspective: 800px;
}
.flip-in {
animation: flip-in 0.6s ease-out both;
backface-visibility: hidden;
}
@keyframes flip-in {
from {
transform: rotateY(-90deg);
opacity: 0;
}
to {
transform: rotateY(0deg);
opacity: 1;
}
}
@media (prefers-reduced-motion: reduce) {
.flip-in {
animation: none;
opacity: 1;
transform: none;
}
}
Prompt this to your LLM
Includes role, constraints, two framework variants, and edge cases to handle.
You are a senior frontend engineer building entrance animations for a UI.
Goal: A flip-in entrance animation that rotates elements from rotateY(-90deg)
into their final position at rotateY(0), combined with an opacity fade-in —
no JavaScript. The effect should look like a card flipping open from the side.
Technical constraints:
- Set perspective: 800px on the parent container to establish the 3D space.
Without this, rotateY has no visible depth effect.
- Use @keyframes with transform: rotateY(-90deg) and opacity: 0 at the start,
rotateY(0deg) and opacity: 1 at the end.
- Apply animation: flip-in 0.6s ease-out both; on the animated element.
The "both" fill mode keeps opacity: 0 before the animation starts.
- Add backface-visibility: hidden to prevent a flash of the element's reverse
face during the rotation.
- Use oklch() for any colors — no hex or rgba values.
- Use CSS custom properties (var(--card), var(--text), etc.) for theming.
- Include @media (prefers-reduced-motion: reduce) that sets animation: none,
opacity: 1, and transform: none on the animated element.
Framework variant (pick one):
A) Vanilla CSS — a .flip-container parent class and a .flip-in child class
applicable to any element.
B) React component — accept children, delay (number in seconds), and axis
("X" or "Y") props; apply the keyframes and inline animation-delay via style.
Edge cases to handle:
- Staggered lists should use animation-delay increments (e.g. 0.1s per item)
rather than re-running the animation via JavaScript class toggling.
- For a vertical flip, swap rotateY for rotateX(-90deg) → rotateX(0deg).
- When the animation ends, ensure the element stays in the "to" state —
the fill-mode "both" handles this; document this for teammates.
Return CSS only (or a React component if variant B is chosen).
Why this matters in 2026
The flip-in pattern has become a staple for card-based UIs, dashboards, and list reveals because it implies dimensionality — the element feels like it existed behind the plane and rotated forward into view. Unlike a simple fade or slide, it communicates that the content has substance. And because it relies entirely on CSS perspective and rotateY, it ships with zero JavaScript. The browser's compositor thread handles the 3D transform, keeping the main thread free for more important work.
The logic
The 3D flip effect requires two cooperating pieces: a perspective value on the parent, and a rotateY transform on the child. Perspective controls the "camera distance" — how extreme the foreshortening looks. A value of 800px gives a subtle, realistic depth. Lower values (like 300px) create a dramatic fisheye effect; higher values flatten the rotation toward a 2D appearance.
Starting at rotateY(-90deg) positions the element perpendicular to the screen — it's edge-on and effectively invisible. As the keyframe progresses to rotateY(0deg), the element rotates to face the viewer directly, its full face coming into view. The simultaneous fade from opacity: 0 to opacity: 1 prevents the thin edge of the element from being visible at the start of the animation.
backface-visibility: hidden is a safeguard: it hides the reverse face of the element during the rotation. Without it, some browsers briefly render the back face as a mirrored version of the content during the transition between -90deg and 0deg, causing a distracting flash. To flip vertically instead of horizontally, swap rotateY(-90deg) for rotateX(-90deg).
The both fill mode on the animation is essential: it applies the from keyframe values before the animation begins (keeping the element invisible and edge-on) and holds the to values after it ends. Without both, the element would be fully visible in its natural position before the animation fires, then snap back after it ends.
Accessibility & performance
3D CSS transforms like rotateY are composited on the GPU, meaning the browser handles the animation independently of the main thread. This makes flip-in performant even on resource-constrained devices — the animation won't stutter because of unrelated JavaScript activity.
The prefers-reduced-motion: reduce media query is critical here. Rotational motion can be more disorienting than a simple fade or slide, particularly for users with vestibular disorders. Setting animation: none, opacity: 1, and transform: none inside the query ensures those users see the element in its final, fully-visible state immediately — no rotation, no flash of invisible content.
One subtlety with staggered animations: if you add delays with animation-delay, each element starts invisible (due to fill-mode: both) until its delay expires. For very long lists this can feel like the page is broken. Keep stagger increments short (0.08–0.15s) and consider capping the maximum delay around 0.4s regardless of list length.