Presentation Mode

Full-page section scrolling like a slide deck with automatic transitions.

Presentation mode turns a set of full-screen sections into a gesture-driven slide deck. Each section occupies the full viewport, gestures (wheel, swipe, pointer) navigate between them with smooth fade and slide transitions, and per-section animations play automatically as each section enters.

How It Works

Presentation mode is a configuration mode inside .onGesture(). Pass a presentation key to activate the full-page section engine: sections are stacked absolutely and sized to fill the viewport, a gotoSection() function handles the enter/exit transitions, and the gesture trigger is wired to navigate forward and backward.

Requires each: true. The presentation engine creates one timeline per matched section. You must enable per-element instancing via each: true on the gesture config.


Basic Usage

typescript
import { Motion } from "@motion.page/sdk";

Motion("deck", ".section", {
  from: { opacity: 0, y: 30 },
  duration: 0.6,
  ease: "power2.out",
}).onGesture({
  types: ["wheel", "touch", "pointer"],
  each: true,
  presentation: {
    sectionSelector: ".section",
  },
});

The .section elements are positioned absolutely, stacked on top of each other. The initial section (initialSection: 0 by default) is shown immediately; the rest start hidden. Gestures navigate between them.


Options

OptionTypeDefaultDescription
sectionSelectorstring'.section'CSS selector identifying the full-page sections inside the container.
direction'vertical' | 'horizontal''vertical'Transition axis. Vertical slides sections up/down; horizontal slides them left/right.
durationnumber1Section transition duration in seconds (min 0.1, max 5).
cooldownnumber0.5Minimum time in seconds between transitions. Prevents rapid consecutive triggers on sensitive trackpads (min 0.1, max 5).
effects('opacity' | 'translate')[]['opacity', 'translate']Visual effects applied during transitions. Include both for a fade-and-slide, one for fade-only or slide-only, or an empty array for an instant cut.
infiniteRepeatbooleanfalseWhen reaching the last section, loop back to the first (and vice versa).
reverseGesturebooleanfalseSwap gesture direction mapping. When true, up/left gestures navigate forward and down/right navigate backward.
useVerticalWheelbooleanfalseHorizontal mode only. Maps vertical wheel events to horizontal section navigation — useful for mice without horizontal scroll.
initialSectionnumber0Zero-based index of the section shown on load.

Direction & Gesture Mapping

The direction you set maps gestures to forward/backward navigation:

DirectionForward gestureBackward gesture
'vertical' (default)Down / Swipe downUp / Swipe up
'horizontal'Right / Swipe rightLeft / Swipe left
'horizontal' + useVerticalWheel: trueDown (wheel)Up (wheel)

Setting reverseGesture: true flips the mapping — up/left becomes forward, down/right becomes backward.


Transition Effects

The effects array controls which visual properties are animated during section transitions:

effects valueOut animationIn animation
['opacity', 'translate'] (default)Fade out + slide awayFade in + slide in
['translate']Slide away (no fade)Slide in (no fade)
['opacity']Fade out (no movement)Fade in (no movement)
[]Instant cutInstant cut
fade-only.ts
// Crossfade only — sections dissolve into each other without movement
Motion("deck", ".section", {
from: { opacity: 0 },
duration: 0.5,
}).onGesture({
types: ["wheel", "touch"],
each: true,
presentation: {
  sectionSelector: ".section",
  effects: ["opacity"],
  duration: 0.8,
  cooldown: 0.6,
},
});

Horizontal Slides

Set direction: 'horizontal' for a left-right slide deck. Add useVerticalWheel: true so users with a standard mouse wheel can still navigate:

horizontal.ts
import { Motion } from "@motion.page/sdk";

Motion("deck", ".slide", {
from: { opacity: 0, x: 40 },
duration: 0.5,
ease: "power2.out",
}).onGesture({
types: ["wheel", "touch", "pointer"],
each: true,
presentation: {
  sectionSelector: ".slide",
  direction: "horizontal",
  duration: 0.8,
  cooldown: 0.4,
  useVerticalWheel: true, // vertical mouse wheel also navigates
},
});

Infinite Loop

Set infiniteRepeat: true to create a looping deck — the last section wraps back to the first and vice versa:

typescript
Motion("deck", ".section", {
  from: { opacity: 0, y: 30 },
  duration: 0.6,
}).onGesture({
  types: ["wheel", "touch"],
  each: true,
  presentation: {
    sectionSelector: ".section",
    infiniteRepeat: true,
    cooldown: 0.6,
  },
});

Per-Section Animations

Each section gets its own independent timeline scoped to that section’s subtree. Animations defined on the Motion() call animate the elements inside each section individually — they play in when the section enters and reverse when it exits.

per-section.ts
import { Motion } from "@motion.page/sdk";

// Each section reveals its heading and body text as it enters
Motion("deck", ".section", {
from: { opacity: 0, y: 40 },
duration: 0.7,
ease: "power2.out",
}).onGesture({
types: ["wheel", "touch", "pointer"],
each: true,
presentation: {
  sectionSelector: ".section",
  direction: "vertical",
  duration: 1,
  cooldown: 0.5,
  initialSection: 0,
},
});

The animation’s target selector is applied within each section’s scope — .section:nth-child(2) .section selects elements only inside that section, not across the entire page.


External Navigation

The presentation engine exposes a gotoSection function on window for programmatic navigation from outside the gesture system:

typescript
// Navigate to a specific section by zero-based index
window.__mp_presentation_deck.gotoSection(2);

// Navigate forward (+1) or backward (-1)
window.__mp_presentation_deck.gotoSection(1);   // next
window.__mp_presentation_deck.gotoSection(-1);  // previous (relative)

The global key is __mp_presentation_ followed by the timeline name you pass to Motion(). For a timeline named "deck", the key is window.__mp_presentation_deck.

Use this to wire up custom dot indicators, keyboard shortcuts, or navigation buttons:

nav-buttons.ts
// External navigation buttons
document.querySelector("#btn-prev")?.addEventListener("click", () => {
window.__mp_presentation_deck?.gotoSection(-1);
});

document.querySelector("#btn-next")?.addEventListener("click", () => {
window.__mp_presentation_deck?.gotoSection(1);
});

// Dot indicators
document.querySelectorAll(".dot").forEach((dot, i) => {
dot.addEventListener("click", () => {
  window.__mp_presentation_deck?.gotoSection(i);
});
});


Builder Usage

In the Motion.page builder, presentation mode is found in the Left Panel → Trigger Section → Gesture trigger. You must first enable “Trigger each iteration individually” before the presentation mode toggle appears.

Once enabled, the builder exposes all PresentationOptions as labeled controls:

Builder controlMaps to
Presentation mode toggleEnables the presentation config
Directiondirection
Transition durationduration
Cooldowncooldown
Effects checkboxeseffects
Infinite repeatinfiniteRepeat
Reverse gesturereverseGesture
Vertical wheel (horizontal mode)useVerticalWheel
Initial sectioninitialSection

Tips & Gotchas

each: true is required. The presentation engine allocates one timeline per section. Without each: true there is no per-section timeline to play on enter, and navigation will not work correctly.

Sections are absolutely positioned. The engine sets position: absolute, top: 0, left: 0, width: 100%, height: 100% on every section. The container element must be position: relative (or absolute/fixed) with an explicit height — typically height: 100vh.

cooldown prevents trackpad jitter. On macOS trackpads, a single fast swipe can produce many wheel events. The cooldown (default 0.5s) ignores new navigation attempts until the previous transition settles. Increase it for slower transitions.

duration and cooldown are in seconds. Unlike stopDelay in the base gesture config (which is milliseconds), these values use seconds — consistent with other SDK timing properties.

initialSection is zero-based. Index 0 is the first section, 1 is the second, and so on.

useVerticalWheel only applies to horizontal mode. In vertical mode the wheel already maps naturally to navigation. The option is ignored when direction: 'vertical'.

External navigation bypasses the cooldown. Calling window.__mp_presentation_NAME.gotoSection(i) navigates immediately regardless of the cooldown timer. Add your own guard if needed.


  • Observer & Gesture — the full gesture trigger API that presentation mode builds on
  • Scroll Trigger — position-based scroll animations with scrub and pin
  • Click Trigger — toggle animations on click with per-element instancing