20 CSS Page Transitions

6 Pure CSS14 Light JSPublished 9 new designs · Updated on

A page transition is an overlay that covers the viewport, a content swap that happens while it is opaque, and a reveal — swap too early and the change shows, too late and a blank frame flashes. These 20 hand-coded patterns cover that lifecycle, exit and enter timing, holding scroll position, height changes mid-transition, FLIP element continuity, route preloading, pending states, back and forward direction, and accessible focus handling. Scoped under .pt-NN; drops into React, Vue, Svelte, or Astro.

Related20 CSS View Transitions22 CSS Transitions16 CSS Fade In Animation Designs20 CSS Loading Animations

Course pickPartner: Zero to Mastery

Affiliate link

Want to build production-ready CSS layouts, animations and UI from scratch?

You just browsed 20 page transitions. Learn the Flexbox, Grid and animation thinking behind them, so the next component starts in your head, not in a copy-paste.

10% off at checkout with code
Check ZTM Course

Every ZTM course in one membership: $25/month, paid yearly at $299 · 30‑day money-back guarantee

The CSS Bootcamp. We may earn a commission, at no extra cost to you.

01. Page Transition Overlay Cover Swap Reveal (opens in a new tab)

Published

Light JSNEW
The reference build the other nineteen demos borrow from. Four phases, each labelled and timed: an overlay covers the viewport, the route content swaps while the overlay is fully opaque, the overlay reveals the new route, and the overlay is removed from the accessibility tree. One rule governs the whole pattern — the swap must land inside the opaque window, because swapping early shows the content changing and swapping late flashes a blank frame.

02. Exit and Enter Timing Without Overlap (opens in a new tab)

Published

Light JSNEW
Two strategies, run side by side from one control so the difference is felt rather than described. Sequential animates the outgoing route out and then the incoming route in — safe, never overlapping, but the total is the sum of both halves and starts to feel sluggish past about 500ms. Overlapping runs both at once for a faster, smoother change, at the cost of having two routes in the DOM together, which needs absolute positioning and a height strategy.

03. Holding Scroll Position Across a Route Swap (opens in a new tab)

Published

Light JSNEW
Swapping route content resets the scroll offset, which is right for a genuine navigation and wrong for a filter change or a tab switch. Three behaviours a router has to tell apart, each running on the same scroll container with the offset shown live: reset to top for a new page, restore a saved offset for back and forward, and preserve exactly where the user was for an in-page swap.

04. Route Transitions Between Different Page Heights (opens in a new tab)

Published

Pure CSSNEW
The most common visible bug in a hand-rolled transition: a short route becoming a tall one moves the scrollbar and the footer mid-animation. The broken version is shown first so the symptom is recognisable, then three fixes side by side — lock the container height for the duration, animate the height as part of the transition, or take both routes out of flow so document height never changes at all.

05. FLIP Element Continuity Without the API (opens in a new tab)

Published

Light JSNEW
Making one element look like it survives a route change with no View Transitions API: measure First, apply Last, Invert the difference with a transform, then Play to identity. A thumbnail grows into a detail hero, with the measured rectangles drawn on screen. This is the technique the API automates for you — knowing it by hand is what lets you ship the effect in Safari today.

06. Preloading Routes So Transitions Never Stall (opens in a new tab)

Published

Light JSNEW
A transition that waits on the network is not a transition, it is a stall with an overlay on top. Intent-based prefetch fixes it: warm the next route on hover or on pointer-down, well before the click, then start the animation only once the content is in hand. The chart shows both numbers — how long the fetch took and how long the transition ran — so you can see which one the user actually waited on.
Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

07. Pending State When the Next Route Is Slow (opens in a new tab)

Published

Light JSNEW
When the next route genuinely takes time, the transition needs a third state between exit and enter. Three timings make it feel right: show nothing for the first 200ms so fast navigations never flash an indicator, show the pending state after that, and hold it for a minimum once shown so it cannot flicker. All three are labelled and adjustable, with a live region announcing the state without stealing focus.

08. Back and Forward Navigation Transition Behavior (opens in a new tab)

Published

Light JSNEW
Browser back should reverse the transition, not replay it forward. A slide that moves left on navigate has to move right on back, or the user's sense of direction breaks. Direction is tracked through the history entry itself and applied to the transition as a signed custom property, with the whole stack drawn on screen so you can watch the index move.

09. Accessible Route Change Focus and Announcements (opens in a new tab)

Published

Light JSNEW
A client-side route change is invisible to assistive technology: the URL changes, the DOM swaps, and nothing is announced — often with focus stranded on a link that no longer exists. Three parts fix it: move focus to the new route's heading, announce the new title through a polite live region, and keep the transition overlay out of the accessibility tree. The announcement log shows exactly what a screen reader would receive.

10. CSS Page Fade Transition on Route Change (opens in a new tab)

PublishedUpdated

Pure CSS
The simplest correct transition and the one to reach for by default. Opacity only, no transform, so it never fights scroll position or layout and costs almost nothing to composite. Everything else in this collection is measured against it — if a fancier effect is not clearly better than this, ship this.

11. Slide Page Transition Left and Right (opens in a new tab)

PublishedUpdated

Light JS
A directional slide where the direction comes from navigation intent rather than from the effect: moving forward through a flow slides one way, going back slides the other. Built on a wizard with a progress rail, which is the case where direction carries the most meaning — the movement tells the user whether they advanced or retreated.

12. Zoom Page Transition Scale In and Out (opens in a new tab)

PublishedUpdated

Pure CSS
A scale-based route change: the outgoing screen recedes slightly and the incoming one arrives from just under full size. It carries more weight than a fade and suits full-bleed, single-purpose surfaces like a kiosk or a display screen. The one thing to know is that scaling a full route can soften text mid-animation unless the transform lands on whole pixels.

13. Clip Path Wipe Page Transition Effect (opens in a new tab)

PublishedUpdated

Pure CSS
A clip-path wipe used as the overlay's cover and reveal: the panel is not moved, its visible shape is. Cheap to composite, easy to angle, and the most versatile of the mask-based effects — a straight edge, a diagonal, or a chevron all come from editing one polygon. Three angles are switchable so the difference is visible rather than described.

14. Blur Page Transition Between Routes (opens in a new tab)

PublishedUpdated

Light JS
Blur as the cover: the outgoing route defocuses, the swap happens at peak blur, and the incoming route sharpens. It reads as depth of field and suits a glass interface better than any geometric effect — with an honest caveat. A filter: blur() across a full viewport is the most expensive transition in this collection, and the cost meter says so.
Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

15. Book Page Turn Transition Effect CSS (opens in a new tab)

PublishedUpdated

Light JS
A 3D page turn: the outgoing page rotates away around its spine edge while the incoming page is revealed underneath. It belongs to editorial content, where a page turn is the reader's real mental model, and it is the one effect here that genuinely needs 3D. The trap is flattening — perspective on the wrong ancestor and the whole thing collapses to a flat squash.

16. Ink Bleed Mask Page Transition (opens in a new tab)

PublishedUpdated

Pure CSS
An organic reveal driven by mask-image: an inked panel spreads across the viewport from a point, holds while the route swaps, then contracts away toward the opposite corner. No script, no clip-path polygon — the shape is a soft radial mask, which is what makes the edge read as ink on paper rather than as a geometric wipe.

17. Shatter Page Transition with Clip Path Tiles (opens in a new tab)

PublishedUpdated

Light JS
The overlay is a grid of triangular shards rather than one panel. Each shard is clipped with a polygon and animated on its own delay, so the cover assembles and the reveal breaks apart in sequence. It is the most decorative effect in the collection and also the most expensive per frame — the shard count is the cost, and the demo lets you feel where the limit is.

18. Split Curtain Page Transition Effect (opens in a new tab)

PublishedUpdated

Pure CSS
Two panels close from the edges, meet in the middle while the route swaps, then part again. It is the most theatrical effect here and the one most easily overdone — the whole thing has to stay under about half a second or a navigation starts to feel like a stage production. Built with radios and two @keyframes, no script.

19. Terminal Text Page Transition Overlay (opens in a new tab)

PublishedUpdated

Light JS
A monospace overlay that types a short line while the route swaps, then clears. It suits developer tooling, where a command line is the interface metaphor rather than a decoration. Two requirements are non-negotiable: the typed text is decorative, so the overlay is aria-hidden, and under reduced motion nothing types at all — the line appears complete and the swap happens immediately.

20. Brush Stroke Page Transition Mask (opens in a new tab)

PublishedUpdated

Light JS
An SVG mask carries a thick, tapered brush path; animating its stroke-dashoffset paints the mask on, which sweeps the overlay across the viewport as a single stroke and then lifts it the same way. Higher fidelity than the radial bleed in demo 16, and the place where namespacing matters most — SVG mask ids are document-global, so two demos both defining one means the second silently wins.
FAQ

Frequently asked questions

How do you make a page transition in CSS and JavaScript?
The pattern is four phases in a fixed order, and the ordering is what makes or breaks it. An overlay covers the viewport; the route content is swapped while that overlay is opaque; the overlay reveals; and then it is removed from the accessibility tree. Almost every hand-rolled transition that looks broken has the swap in the wrong place — swap too early and the user watches the content change through a semi-transparent overlay, swap too late and a blank frame flashes between the cover and the reveal. Practically, that means the swap belongs in a callback timed to the cover animation's end rather than fired alongside it, and the overlay needs aria-hidden="true" throughout so a screen reader never announces a decorative full-viewport element. Demo 01 renders the phase timeline as a visible diagram with each step labelled.
Should I use the View Transitions API instead?
Where you can, yes — and it is worth being straight about that. document.startViewTransition handles the snapshot, the crossfade and the element morphing for you, and it does so more smoothly than hand-rolled code. Our css-view-transitions collection covers it in depth across 20 demos. Three situations still call for building it yourself. Safari support arrived late and a large share of production traffic sits on older versions, so a fallback path is required rather than optional. Some routers own the DOM in ways that make handing control to the browser's snapshot mechanism awkward. And some effects — a book page turn, a shatter, a brush stroke mask — are not crossfades and cannot be expressed through the API's model at all. The machinery in this collection is what runs in those cases, and it is also what the API's fallback path degrades to.
Why does my page jump to the top during a route transition?
Because swapping route content resets the scroll position, which is right for a genuine navigation and wrong for an in-page swap like a tab change or a filtered list. Three behaviours are needed and they are not interchangeable: reset to the top for a real navigation, restore a saved offset for back and forward, and preserve the current position exactly for an in-page swap. The detail that trips people up is timing — the offset has to be captured before the swap, because after it the old content is gone and the number is already lost. Set history.scrollRestoration to 'manual' so the browser stops applying its own guess on top of yours. Demo 03 shows all three policies side by side with the live scroll offset displayed.
How do I stop the layout jumping when routes have different heights?
This is the most visible bug in hand-rolled transitions: a short route transitioning to a tall one changes the document height mid-animation, so the scrollbar resizes and anything below the fold shifts while the user is watching. Three fixes work, with different trade-offs. Lock the container to the outgoing height for the duration, which is simplest but can clip a taller incoming route. Animate the height as part of the transition, which looks deliberate but re-runs layout every frame. Or position both routes absolutely during the transition so the document height never changes at all, which is cheapest but needs a wrapper with a known height. Demo 04 shows the broken version first, so the symptom is recognisable, then all three fixes labelled.
How do I animate an element from one page to another without the API?
The FLIP technique, which is what the View Transitions API automates internally. Measure the element's First position with getBoundingClientRect, apply the Last state by moving it to its destination, Invert the visual change with a transform that puts it back where it appeared to be, then Play by transitioning that transform to identity. The result is an element that appears to travel continuously between two layouts even though it was never actually animated along a path. The trap is measurement timing: read layout before the browser paints the Last state, or you measure the wrong frame and the element animates from somewhere unexpected. Demo 05 shows a thumbnail growing into a detail hero with the measurement overlay visible.
How do I show a loading state when the next route is slow?
With two thresholds, not one. Show nothing for roughly the first 200ms, so a fast navigation never flashes a spinner that appears and vanishes faster than the eye resolves it — that flash reads as a glitch and makes a quick app feel broken. Once you do show a pending indicator, hold it for a minimum time even if the content arrives immediately after, so it never flickers out mid-appearance. Below the first threshold the transition simply plays; above it, the pending state becomes a real third phase between exit and enter. Pair it with a live region announcing the wait, since a purely visual spinner tells a screen-reader user nothing. Demo 07 shows all three timings labelled; spinner design itself belongs to css-loading-animations.
Should the back button replay the transition or reverse it?
Reverse it, in almost every case. If navigating forward slides content in from the right, going back should slide it out to the right — replaying the forward direction breaks the spatial model the transition exists to build, and users read it as the app having moved forward again. The implementation detail that matters is that popstate fires after the URL has already changed, so by the time you handle it the direction is no longer inferable from the current state. Direction has to be recorded at push time and read back on pop, which usually means storing an index in the history state object and comparing it against the previous one. Demo 08 shows direction tracked through an in-memory history stack, since a sandboxed demo cannot push real entries.
Are client-side route transitions accessible?
Not by default, and the gap is larger than most teams realise. When a client-side route changes, the URL updates and the DOM swaps, but a screen reader is given no signal that anything happened — and focus is frequently left on a link that no longer exists, which strands keyboard users somewhere undefined. Three things fix it. Move focus deliberately to the new route's heading, giving it tabindex="-1" so it can receive focus without entering the tab order. Announce the new page title through a polite live region, so the change is heard without interrupting. And keep the transition overlay aria-hidden, so its decorative content is never read aloud. Under prefers-reduced-motion, reduce the transition to a near-instant swap rather than removing it in a way that strands an element mid-animation. Demo 09 ships all of it with a simulated announcement log.

Related collections

20 CSS Animated Buttons preview

20 CSS Animated Buttons

20 hand-coded CSS animated buttons — neon glow, ripple, 3D press, liquid fill, jelly bounce, shine sweep, animated border, moving gradient CTA, text flip, submit success state, add-to-cart progress, download icon, hamburger-to-close, toggle switch, loading spinner inside button, next/prev arrow nav, and ghost button background reveal. Half pure CSS, half lightweight JS for production interactions.

11 CSS animation-timeline Demos preview

11 CSS animation-timeline Demos

11 pure CSS animation-timeline demos built on the W3C scroll() and view() spec — reading progress bar, view-timeline fade & reveal, shrinking sticky header, stacking cards, horizontal scroll section, parallax hero, container shadow indicators, reverse-scroll columns, text scrim reveal, cover card to sticky header, and image zoom / clip-path wipe. Zero JS, off the main thread, Core Web Vitals safe. Copy-paste ready.

25 CSS Background Animations preview

25 CSS Background Animations

25 hand-coded CSS background animations for SaaS hero sections, developer tool documentation, agency portfolios, gaming and Web3 landing pages, seasonal e-commerce campaigns, and dark-mode dashboards. Covers full-screen gradient shifts, animated mesh gradients, aurora borealis glow, cursor spotlight follow, interactive water ripples, dot grid and diagonal stripe patterns, SVG grain and noise texture overlays, topographic contour lines, layered SVG waves, plasma and lava lamp fluids, bokeh light blur, particle constellation networks, matrix digital rain, synthwave 3D perspective grids, starfield warp speed, CRT scanline overlays, fog and mist drift, morphing organic blobs, floating glassmorphism orbs, halftone dot patterns, starry night skies, floating geometric shapes, rising bubbles, and falling snow. 20 are pure CSS with zero JavaScript; the 5 that need it use vanilla canvas or pointer tracking with no dependencies. Every background animates compositor-only properties so it never blocks the main thread, is scoped under a .bga-NN prefix for no-collision pasting, guards prefers-reduced-motion, and ports unchanged to React, Vue, Svelte, Astro, Next.js and Tailwind.

Search CodeFronts

Loading…