Transitions that do not nauseate: screen changing patterns for mobile and web
Main chat
A chat for vibe coders: news, guides, live cases, marketplace, and finding executors.
Switching between screens is the most common motion pattern and the most common breaking UX. A bad transition is disorienting: the user does not understand where he was, where he is now, what happened.
A good transition is invisible. You've just noticed that it's clearer.
Why transitions are important for orientation
Without animation, the transition is teleportation. The user was on the list, clicked, was on a detailed page. The brain did not have time to build a spatial connection between the two states.
With the right animation, movement. The user sees that “slide left,” that “slide right,” that “something popped up.” The mental map of the product is built through these transitions.
The wrong transition breaks the mental map. Slide left when expected to right. Fade is where scale is needed. The user feels "something is wrong" - even if they can't give a reason.
Seven patterns with the logic of choice
Fade (crossfade)
The current screen dissolves, a new one appears. The most neutral does not convey direction.
When: screens are not hierarchically linked (change partitions via side menu), modal dialogues, change tabs.
Not when: drill-down navigation where you need to show "I went deeper.".
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.page-enter { animation: fadeIn 0.25s ease-out; }
Slide horizontal
The next screen comes to the right, the current one goes to the left. Back is the opposite.
When: drill-down navigation (list → detail), iOS navigation stack, any hierarchical structure.
Why does it work: mimics physical flipping. The user understands that he “got deep” and knows which way to return.
.page-enter {
transform: translateX(100%);
animation: slideIn 0.3s ease-out forwards;
}
.page-exit {
animation: slideOut 0.3s ease-in forwards;
}
@keyframes slideIn { to { transform: translateX(0); } }
@keyframes slideOut { to { transform: translateX(-30%); opacity: 0.3; } }
Slide vertical (bottom sheet)
Appearance from the bottom is the standard for modal bottom sheets on mobile. Disappearing down at closing.
When: bottom sheets, action sheets, drawer panels on mobile. Creates a clear distinction between “mainstream content” and “temporary content.”.
Scale (zoom)
The element increases to a new screen. The most powerful way to show that a new screen has come out of a particular item.
**When:**Clicking on the card opens the detailed screen, thumbnail → full screen, avatar → user profile.
Implementation: transform: scale(0.9) → transform: scale(1) with transform-origin at the pressing point.
Shared Element Transition (morph)
An element on one screen smoothly transforms into an element on another. The card in the list becomes the header of the detailed screen.
When: list → detail, thumbnail → full screen photo.
CSS: View Transitions API.
In React: layoutId in Framer Motion.
Framer Motion – one layoutId on both screens
// On the list screen:
<motion.div layoutId={`card-${item.id}`} >>
// Details on the screen:
<motion.div layoutId={`card-${selectedId}`} >>
Reveal
A new screen appears from under the old one. For sidebar navigation, content “shifts” by opening the panel.
Cross-fade with blur
The current screen blurs and disappears, the new one appears clear. It creates a feeling of “another level” – a deeper immersion.
Rule of consistency
One product, one transition system. Mixing slide and scale depending on the mood breaks the mental model.
Minimum system:
- Drill-down - slide left
- Back to slide right
- Modal/overlay: fade or slide up
- Change of sections → fade
Write it down in DESIGN.md and give the AI in context - otherwise each screen will have a different transition.
View Transitions API – transitions without JavaScript
For multi-page sites (MPA) – automatic transitions without router libraries:
/* Включить для всего сайта */
@view-transition {
navigation: auto;
}
/* Кастомизация */
::view-transition-old(root) {
animation: slide-out 0.3s ease-in;
}
::view-transition-new(root) {
animation: slide-in 0.3s ease-out;
}
@keyframes slide-out { to { transform: translateX(-100%); } }
@keyframes slide-in { from { transform: translateX(100%); } }
Support: Chrome, Edge, Safari 18+ For Firefox, it's a polyphill.
Prompt for Codex/Claude Code
Add the transition system between pages in the Next.js project.
Rules:
Navigation in depth (list → detail): slide left
- Navigation back: slide right
Modal windows: fade + scale(0.95→1)
- Changing sections with nav: fade
Implement: View Transitions API for native transitions.
Fallback for unsupported browsers – instant transition without animation.
Duration: 300ms, easing: cubic-bezier(0.16, 1, 0.3, 1).
Prefers-reduced-motion: Instant transitions.
Do not use Framer Motion only for transitions – View Transitions API is sufficient.
Why switching between screens is the hardest thing in motion
Hover animation involves one element. The transition between screens affects everything – the old screen disappears, a new one appears, sometimes individual elements move between them. The error is visible immediately and on the whole screen.
The three most common mistakes are:
Too long transition. 600ms to navigate between pages is almost a second of waiting for each click. On a site with 20 pages, the user will spend minutes only on animation.
** Wrong direction.** The user presses the back button and the screen goes to the right (correctly). Clicks the link deep - the screen also goes to the right (wrong, should be left). The mental model is broken.
Transition is interrupted. The user clicks quickly, the next transition begins before the previous one is completed. If not treated correctly, visual debris.
Shared Element Transition: When to Do When Not to Do
Shared Element Transition is one of the most impressive patterns. But it has a price.
Works well when: one item is “transformed” into another, the user sees a visual connection, screens are physically close in content (list of products → product page).
When does not work: there are a lot of elements and it is not clear which “transforms”, the content of the screens is very different, the transition works slowly on weak devices.
Technical implementation through the View Transitions API or Framer Motion layoutId is relatively simple. The design implementation is complex: you need to accurately design the initial and final state of both screens to make the transition look natural.
Mobile vs desktop: different patterns
On mobile, the user interacts with his finger and expects a physical sensation. The Slide transition that follows the swipe is natural. Fade that appears regardless of the gesture is dissonance.
On the desktop, the mouse leaves no “trace of movement.” Fade or subtle slide is fine. Aggressive slide, like on mobile, is usually excessive.
In adaptive design: define different motion systems for mobile and desktop. @media (pointer: coarse) for touch devices – more physical transitions. @media (pointer: fine) for the mouse is more restrained.
When the transition is not necessary
Not every navigation requires an animated transition. Three cases when the transition is better to remove:
** Fast repetitive navigation.** The user scrolls through the list by pressing back → forward quickly. Each transition 300ms — 10 taps he spent 3 seconds only on animation. Solution: After the third quick press in a second, turn off the transition.
Heavy content. Page with lots of images and data – the transition starts before the new page loads. The user sees an empty screen with animation. Solution: show the transition only after the basic content is uploaded.
Actions in the same area. The filter has changed. Do I need to move to the page level? Probably not – it is enough to animate the cards themselves.
System defined: what pattern for which navigation
Drill-down: slide direction coincides with Back
● No mixing of different patterns without logic
The system is recorded in DESIGN. md
View Transitions API or Framer Motion AnimatePresence
Prefers-reduced-motion: instantaneous transitions
Duration 250-350ms (not 500ms+ for basic transitions)