# SSGOI - Page Transition Library > Universal page transition library that works in all browsers (Chrome, Firefox, Safari), unlike the View Transition API (Chrome-only). This document is for AI assistants to help developers set up SSGOI page transitions in Next.js. **Per-transition reference (options, physics, examples):** see the `Transitions` section below — each entry links to its own page like `https://ssgoi.dev/llms/.txt`. --- ## Installation ```bash npm install @ssgoi/react (or @ssgoi/svelt, @ssgoi/vue dependings on your framework) ``` --- ## Layout Setup SSGOI works on any web app. There are only three things to get right: 1. **Decide your scroll element** — the element whose scroll position SSGOI should preserve across transitions. Often a `
` or a centered content container. 2. **Apply `relative z-0` (and optionally `overflow-x-clip`) to the element that wraps ``.** 3. **Wrap each page in ``** (see the next section). Why those classes: | Class | Why | |-------|-----| | `relative` | OUT page is cloned with `position: absolute` — needs a positioned ancestor | | `z-0` | Creates a stacking context so the OUT page doesn't fall behind backgrounds | | `overflow-x-clip` | Prevents horizontal overflow flashing during slide/drill (only needed if you use horizontal transitions) | The scroll element and the `` wrapper are typically **the same element**, so all four classes (`overflow-y-auto relative z-0 overflow-x-clip`) sit together. ### Layout example (mobile-style web app) The library is most often used to ship app-like, mobile-width experiences, so this example shows that shape — a centered max-width column where the column itself scrolls. Adapt as needed for desktop layouts. ```tsx // src/app/layout.tsx import { Ssgoi } from "@ssgoi/react"; import { drill } from "@ssgoi/react/view-transitions"; const config = { transitions: [ { from: '*', to: '/post/*', transition: drill({ direction: 'enter' }) }, { from: '/post/*', to: '*', transition: drill({ direction: 'exit' }) }, ], }; export default function RootLayout({ children }: { children: React.ReactNode }) { return (
{children}
{/* Optional: outside
, fixed-positioned */}
); } ``` --- ## Page Setup Wrap each page with `SsgoiTransition`: ```tsx // app/page.tsx import { SsgoiTransition } from "@ssgoi/react"; export default function HomePage() { return (
Home content
); } // app/post/[id]/page.tsx export default function PostPage({ params }: { params: { id: string } }) { return (
Post content
); } ``` --- ## SsgoiConfig ```ts type SsgoiConfig = { // Route-specific transitions transitions?: { from: string; // Source path (wildcards: '/post/*', '*') to: string; // Destination path transition: Transition; symmetric?: boolean; // Auto-create reverse transition }[]; // Fallback transition defaultTransition?: Transition; // Native edge-swipe gestures (iOS Safari swipe-back, Android system back) are // detected from touch events and automatically suppress the page transition, // so the OS gesture animation isn't fought by SSGOI. No configuration needed. // Scroll preservation. Defaults are sensible for mobile + desktop — // typically you don't need to set this. See section below. preserveScroll?: ...; }; ``` ### preserveScroll **Defaults work out of the box.** Mobile viewports automatically preserve scroll position across transitions; desktop does not (browsers handle it). You normally don't need to configure this. **Customizing — exclude specific paths:** ```ts const config = { preserveScroll: { exclude: ["/post/*"] }, }; ``` **How it works:** When navigating *to* a path matching an `exclude` pattern, the destination page starts at scroll position 0 (typical for detail pages where you want to read from the top). All other navigations keep their preserved scroll position. --- ## Transitions Browse the list, find the UX you want, then open the per-transition page for full options and examples. Each detail page is self-contained — you only need to read the ones you'll use. ### `drill` — https://ssgoi.dev/llms/drill.txt iOS-style hierarchical navigation. New page slides in from the right; old page eases left underneath. Use for **List → Detail** flows where the user feels they're going deeper into content. ### `fade` — https://ssgoi.dev/llms/fade.txt Calm cross-fade between pages. No motion, just opacity. Use as a **safe default** for unrelated pages, or anywhere you don't want a directional cue. ### `scroll` — https://ssgoi.dev/llms/scroll.txt Vertical "page scroll" — old page slides off and new page slides in along the same axis. Use for **sequential content** like onboarding steps or paginated long-form pages. ### `slide` — https://ssgoi.dev/llms/slide.txt Horizontal slide — both pages translate sideways at once, like swiping between tabs. Use for **tab navigation** or swipeable sections. ### `swap` — https://ssgoi.dev/llms/swap.txt Subtle scale + fade. OUT fades, then IN scales up (0.95 → 1) and fades in. Use for **bottom tab navigation** between sibling pages where there's no natural direction. ### `sheet` — https://ssgoi.dev/llms/sheet.txt Bottom sheet style. New page slides up from below while the previous page scales down behind it. Use for **modal-like** flows (FAB → compose, "new item" screens) that should feel temporary. ### `hero` — https://ssgoi.dev/llms/hero.txt Shared element transition. The same logical object (a thumbnail, an avatar) is tweened in size and position from one page to the next. Requires matching `data-hero-key` on both pages — see the detail page. ### `pinterest` — https://ssgoi.dev/llms/pinterest.txt Card expansion. The tapped gallery card grows to fill the detail viewport while the rest fades out; reverses on back. Requires `data-pinterest-gallery-key` / `data-pinterest-detail-key` — see the detail page. ### `instagram` — https://ssgoi.dev/llms/instagram.txt Like `pinterest` but only the IN page animates — the gallery just unmounts. Use over `pinterest` when you have **many gallery items** and want lighter animation cost. Requires `data-instagram-gallery-key` / `data-instagram-detail-key`. ### `depth` — https://ssgoi.dev/llms/depth.txt Material Design Z-axis. Both pages scale + fade in place, suggesting depth (forward/backward) rather than horizontal navigation. Use for **drilling within the same surface** (Settings → sub-setting). ### `snap` — https://ssgoi.dev/llms/snap.txt Fast, subtle slide + fade — punchy and decisive (~250ms). Use when you want **feedback that "something changed"** without a full directional animation, especially in chains of quick navigations. ### Basic usage ```tsx import { drill, fade, scroll } from "@ssgoi/react/view-transitions"; const config = { transitions: [ { from: '*', to: '/post/*', transition: drill({ direction: 'enter' }) }, { from: '/post/*', to: '*', transition: drill({ direction: 'exit' }) }, { from: '/', to: '/about', transition: scroll({ direction: 'up' }) }, ], defaultTransition: fade(), }; ``` --- ## Troubleshooting ### Page jumps during transition - **Cause**: Missing `position: relative` on container - **Check**: Do you have a fixed header/nav? If not, add `relative` ### OUT page disappears behind background - **Cause**: Missing `z-index: 0` on container - **Fix**: Add `z-0` to the div wrapping `{children}` ### Horizontal overflow during slide transitions - **Cause**: Missing overflow clip - **Fix**: Add `overflow-x-clip` to container ### Transition not applying - **Cause**: `id` prop doesn't match route pattern - **Fix**: Ensure `SsgoiTransition id` matches the actual route path ### hero / pinterest / instagram transition not running - **Cause**: Data attribute keys don't match between pages - **Fix**: Verify the same key value is on both source and destination elements --- ## How It Works (For Debugging) When a route changes, SSGOI orchestrates two simultaneous animations: - **OUT**: The leaving page animates out - **IN**: The entering page animates in ### Core Mechanism: Clone & Absolute Position **The key insight**: When a page unmounts, SSGOI clones it back into the DOM with `position: absolute` so it can animate out while the new page animates in. ```ts // Internal behavior (packages/core/src/lib/utils/prepare-outgoing.ts) export const prepareOutgoing = (element, context) => { element.style.position = "absolute"; // Removed from flow element.style.width = "100%"; element.style.top = `${-1 * (context?.scrollOffset?.y ?? 0)}px`; element.style.left = "0"; }; ``` **Why this matters:** - The OUT page is re-inserted into DOM as a clone - It becomes `position: absolute` to overlay with the IN page - It needs a positioned ancestor (`relative`) to stay in place - Without proper CSS setup, the OUT page will jump to wrong position ### Transition Flow ``` /home -> /about navigation: 1. User clicks link to /about 2. /home page unmounts from React 3. SSGOI clones /home and re-inserts it with position: absolute (OUT) 4. React renders /about page (IN) 5. Both pages animate simultaneously 6. Animation complete, cloned /home removed from DOM ``` --- ## Links - Per-transition reference (one file per transition): https://ssgoi.dev/llms/.txt - Docs: https://ssgoi.dev - Mobile Transitions: https://ssgoi.dev/en/docs/mobile-transitions - Desktop Transitions: https://ssgoi.dev/en/docs/desktop-transitions - Physics Options: https://ssgoi.dev/en/docs/core-concepts/spring-presets - GitHub: https://github.com/meursyphus/ssgoi - NPM: https://www.npmjs.com/package/@ssgoi/react