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 viaeach: trueon the gesture config.
Basic Usage
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
| Option | Type | Default | Description |
|---|---|---|---|
sectionSelector | string | '.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. |
duration | number | 1 | Section transition duration in seconds (min 0.1, max 5). |
cooldown | number | 0.5 | Minimum 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. |
infiniteRepeat | boolean | false | When reaching the last section, loop back to the first (and vice versa). |
reverseGesture | boolean | false | Swap gesture direction mapping. When true, up/left gestures navigate forward and down/right navigate backward. |
useVerticalWheel | boolean | false | Horizontal mode only. Maps vertical wheel events to horizontal section navigation — useful for mice without horizontal scroll. |
initialSection | number | 0 | Zero-based index of the section shown on load. |
Direction & Gesture Mapping
The direction you set maps gestures to forward/backward navigation:
| Direction | Forward gesture | Backward gesture |
|---|---|---|
'vertical' (default) | Down / Swipe down | Up / Swipe up |
'horizontal' | Right / Swipe right | Left / Swipe left |
'horizontal' + useVerticalWheel: true | Down (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 value | Out animation | In animation |
|---|---|---|
['opacity', 'translate'] (default) | Fade out + slide away | Fade in + slide in |
['translate'] | Slide away (no fade) | Slide in (no fade) |
['opacity'] | Fade out (no movement) | Fade in (no movement) |
[] | Instant cut | Instant cut |
// 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:
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:
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.
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:
// 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:
// 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 control | Maps to |
|---|---|
| Presentation mode toggle | Enables the presentation config |
| Direction | direction |
| Transition duration | duration |
| Cooldown | cooldown |
| Effects checkboxes | effects |
| Infinite repeat | infiniteRepeat |
| Reverse gesture | reverseGesture |
| Vertical wheel (horizontal mode) | useVerticalWheel |
| Initial section | initialSection |
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.
Related
- 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