webdevpuneet.com

Edge Swipe Back Navigation — Free HTML CSS JS Snippet

Edge Swipe Back Navigation · Navigation · Plain HTML, CSS & JS · Live preview

CategoryNavigation

Copy & paste

Source Code

<div class="es-phone">
  <div class="es-statusbar"></div>
  <div class="es-stack" id="esStack"></div>
  <div class="es-edge-hint" aria-hidden="true"></div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Real Pointer Events-based drag tracking, not a canned CSS animation triggered on tap alone
Gesture recognition restricted to a configurable left-edge zone so it never conflicts with scrolling or row taps
Live 1: 1 finger-to-screen movement mapping during the drag, with transitions disabled mid-drag for zero lag
Threshold-based commit-or-cancel on release, matching the real iOS edge-swipe-back interaction
Genuine array-based navigation stack supporting any number of pushed screens, not a fixed two-screen demo
Parallax-style peeking previous screen with a darkening scrim, reinforcing stack depth visually
Back button in the header calls the identical pop() function as a completed swipe gesture
Pure vanilla JS and CSS transforms — no animation or gesture library

About this UI Snippet

Edge Swipe Back Navigation — iOS-Style Drag-From-Edge Screen Stack in Vanilla JS

Screenshot of the Edge Swipe Back Navigation snippet rendered live

Mobile apps built with a push/pop navigation stack almost universally support swiping in from the very left edge of the screen to go back, dragging the current screen aside in real time to reveal the previous one peeking in underneath — a much more direct, cancelable interaction than only offering a small back-arrow tap target. This snippet implements the real gesture physics with the Pointer Events API: the drag distance is tracked live, both screens move together in proportion to the drag, and lifting your finger either commits the pop or snaps back based on how far you dragged.

A real navigation stack, not two hardcoded screens

stack is a plain array of indices into a SCREENS list. push(screenIdx) and pop() mutate that array and call renderStack(), which rebuilds only the top two visible layers with CSS transforms — the top screen at translateX(0) and the one beneath it partially shifted left at translateX(-30%), exactly matching how iOS renders a peeking previous screen with parallax rather than a hard cut. Any number of screens can be pushed; this is a genuine stack, not a fixed two-screen demo.

Restricting the gesture to the edge zone

The pointerdown handler checks localX > EDGE_ZONE and bails immediately if the touch didn't start within the leftmost 24px of the screen — this is the defining constraint of an *edge* swipe gesture as opposed to a general swipe-anywhere gesture, and it's essential so that normal scrolling or tapping rows elsewhere on the screen is never mistaken for a back-navigation attempt.

Live 1:1 drag tracking, not a fixed animation

While dragging, pointermove computes progress as the raw drag distance divided by the screen width (clamped to 0–1) and applies it directly as a percentage transform to both the top screen (sliding right) and the screen beneath it (sliding the rest of the way in from its -30% offset toward 0) — every pixel of finger movement maps directly to a pixel of screen movement, with transition: none set during the drag so there is zero animation lag between touch and visual response.

Commit-or-cancel on release

endDrag() reads the final progress value against a 0.35 threshold: past it, pop() runs and completes the transition off-screen; below it, both screens animate back to their resting positions instead. This threshold-based commit/cancel behavior — rather than always completing or always reverting — is what makes the gesture feel physically responsive instead of all-or-nothing, and matches the same threshold pattern iOS itself uses for its edge-swipe-back gesture.

A visible scrim on the receding screen

The .es-below::after pseudo-element darkens whichever screen sits beneath the active one with a semi-transparent overlay, reinforcing the sense that it is "behind" the active screen in a real depth stack rather than simply positioned off to the side.

Step by step

How to Use

  1. 1
    Tap a row to push a new screenTapping any row slides in the next screen from the right, adding it to the top of the navigation stack.
  2. 2
    Swipe from the very left edge to go backPress down within the leftmost ~24px of the phone frame and drag right — the current screen follows your finger in real time.
  3. 3
    Release past the halfway point to commitDragging more than about 35% of the screen width and releasing completes the pop; releasing before that snaps both screens back to their resting position.
  4. 4
    Tap the back arrow as an alternativeThe header back button calls the same pop() function as a completed swipe, for users who prefer tapping over gesturing.
  5. 5
    Adjust the edge zone widthChange the EDGE_ZONE constant in the JS panel to widen or narrow how close to the left edge a touch must start to begin tracking.
  6. 6
    Add more screens to the stackExtend the SCREENS array — push() and pop() work with any stack depth without additional changes.

Real-world uses

Common Use Cases

Mobile app shell prototypes
A realistic, physically-interactive base for prototyping any stack-based mobile navigation flow — settings drill-downs, message threads, detail views.
Hybrid and PWA app navigation
Progressive web apps that want native-feeling navigation without a full mobile framework can adapt this gesture-driven stack directly.
Teaching Pointer Events drag physics
A concrete, complete example of live drag tracking, progress-based transform interpolation, and threshold-based commit/cancel logic using only the Pointer Events API.
Design system navigation documentation
Demonstrate exactly how an edge-swipe-back interaction should feel to designers and stakeholders who may only have seen static navigation stack diagrams.
Reference for stack-based push/pop UI
The array-based stack plus renderStack() pattern generalizes well beyond gestures — useful for any push/pop UI including modal stacks and wizard flows.

Got questions?

Frequently Asked Questions

The pointerdown handler measures the touch's x-position relative to the container and immediately returns without starting a drag if that position is greater than the EDGE_ZONE constant (24px by default). Only a touch starting within that narrow left-edge band begins tracking a back-swipe.

pointermove computes progress as the raw horizontal drag distance divided by the container's width, clamped between 0 and 1, and applies it directly as a percentage transform to both the top screen (translateX(progress * 100%)) and the screen beneath it (translating the remaining distance from its -30% resting offset toward 0%) — a direct, un-eased mapping so there is no perceptible lag between finger and screen movement.

endDrag() compares the final progress value against a 0.35 threshold. If the drag passed that threshold, pop() runs and the screen finishes animating off-screen; if not, both screens animate back to their original resting transforms instead of completing the navigation.

A CSS transition adds a delay between a style change and its visual result. During an active drag, every pointermove event must move the screen instantly to match the finger exactly — any transition lag would make the drag feel disconnected from the touch. Transitions are re-enabled only once the drag ends, so the snap-back or completing animation is smooth.

Yes — stack is a plain array and can hold any number of pushed screen indices. renderStack() only ever positions the top two entries visually (the active screen and the one directly beneath it), which is both how the real interaction should look and keeps rendering cheap regardless of stack depth.

Keep the stack array in component state (e.g. useState<number[]>), and drive the pointerdown/pointermove/pointerup handlers with refs to the current and previous screen elements rather than direct DOM queries, applying the same progress-based transform math inside the move handler.

Yes. While the Edge Swipe Back Navigation snippet gives you one pattern to copy, the free Responsive Navbar Builder at webdevpuneet.com/navbar-builder/ designs a whole navigation bar: logo, links and dropdown menus, a mobile hamburger menu (dropdown panel, slide-in drawer or full screen) and a scroll effect (sticky, shrink, hide on scroll or transparent). It has six presets, a live preview that runs the real code, and exports HTML + CSS + vanilla JS, React, Tailwind, Vue 3 or Angular — or you can click Fork & Edit to keep editing it in My Code.