Skip to content

Page Transitions

This guide explains the page transition system using Swup in LamaPress.

Table of Contents


Overview

LamaPress uses Swup for client-side page transitions, providing smooth navigation without full page reloads. The system supports custom transition animations and automatic component lifecycle management.

Swup Integration

Configuration

Swup is configured in src/js/core/router/index.js:

javascript
this.swup = new Swup({
  containers: ['#page'],
  cache: true,
  linkSelector: 'a[href^="/"]',
  animateHistoryBrowsing: false,
  plugins: [
    // ... plugins
  ],
})

Plugins

Swup plugins used:

  • BodyClassPlugin - Adds body classes for page transitions
  • PreloadPlugin - Preloads linked pages
  • JsPlugin - Handles JavaScript execution
  • ParallelPlugin - Parallel page loading
  • ScriptsPlugin - Script execution
  • ScrollPlugin - Scroll management
  • MorphPlugin - Morphs specific elements (header, menu)

Page Transition Components

Panel Transition

Panel transition (components/blocks/layout/page_transition/panel/index.js) provides a panel-based transition:

Features:

  • Panel animation with scale and clip-path
  • Page fade and scale
  • Progress counter
  • Smooth easing

Usage:

javascript
// Automatically used when component is present
<div class="js-page-transition" data-component="blocks/layout/page_transition/panel">
  <div class="js-counter">0</div>
</div>

Basic Transition

Basic transition (components/blocks/layout/page_transition/basic/index.js) provides a simple fade transition.

Transition Lifecycle

Transition Out

When a link is clicked:

  1. Router detects click via Swup
  2. PageTransition.transitionOut() is called
  3. Previous page leaves (calls page.leave())
  4. Transition animation plays
  5. Promise resolves when animation completes

Example:

javascript
transitionOut = () => {
  document.body.style.pointerEvents = 'none'
  
  const page = app.instances.get('page')
  if (page?.leave) page.leave()
  
  this.tl = gsap.timeline({
    onComplete: () => resolve(),
  })
  
  // Animation code
}

Transition In

After new page loads:

  1. New page created via createNextPage()
  2. Page loaded via loader.loadPage()
  3. Transition animation plays
  4. New page enters (calls page.enter())
  5. Pointer events restored

Example:

javascript
transitionIn = async () => {
  await createNextPage()
  await app.instances.get('loader').loadPage(this.updateLoader)
  
  this.tl = gsap.timeline({
    onComplete: () => {
      document.body.style.pointerEvents = null
    },
  })
  
  // Animation code
  this.tl.addAsync(enterNextPage)
}

Custom Transitions

Create custom transition component:

javascript
import { app } from '@src/js/core/app'
import { getRouterMethods } from '@src/js/core/router'
import gsap from 'gsap'

export default class CustomTransition {
  constructor() {
    this.element = document.querySelector('.js-transition')
  }

  transitionOut = () => {
    return new Promise((resolve) => {
      const page = app.instances.get('page')
      if (page?.leave) page.leave()

      gsap.to(this.element, {
        opacity: 1,
        duration: 0.5,
        onComplete: resolve,
      })
    })
  }

  transitionIn = async () => {
    const { createNextPage, enterNextPage } = getRouterMethods()
    
    await createNextPage()
    await app.instances.get('loader').loadPage()

    return new Promise((resolve) => {
      gsap.to(this.element, {
        opacity: 0,
        duration: 0.5,
        onComplete: () => {
          enterNextPage()
          resolve()
        },
      })
    })
  }
}

Best Practices

1. Use Router Methods

Use router helper methods:

javascript
import { getRouterMethods } from '@src/js/core/router'

const { leavePreviousPage, createNextPage, enterNextPage } = getRouterMethods()

2. Handle Loading States

Show loading progress:

javascript
updateLoader = (progress) => {
  this.counter.innerHTML = Math.round(progress * 100)
}

3. Clean Up Animations

Kill timelines in cleanup:

javascript
destroy = () => {
  this.tl?.kill()
}

Next Steps:

Released under the MIT License.