Skip to main content
← Back to Motion Recipes
intermediateLayout

Swap Column Features

Swap text and visual columns with a smooth GSAP layout transition.

signalsclass bindingsstandalone componentOnPushtimelinefromToxPercentscale
Swap layout with GSAP

They're all here

Show a part of your product that explains what "They're all here" means. Swap the visual and text columns to change the rhythm.

Angular + GSAP
gsap.to(el, {
  xPercent: -100,
  duration: 0.6,
  ease: 'power3.out'
});

How it works

A signal called swapped tracks which column is on the left. When the user clicks the toggle, an Angular effect() reacts to the signal change and runs a GSAP timeline that animates both columns out, swaps their CSS order, then animates them back in.

The animation uses xPercent for the lateral movement, opacity for the cross-fade, and scale for a subtle zoom. Because the motion is handled by GSAP, Angular change detection only runs once per toggle, not on every animation frame.

The first effect run is skipped so the demo loads in its resting state without an unwanted entrance animation.

The Angular way

  • State is a plain signal(false). The template binds the button label and aria-pressed to it.
  • The animation is triggered inside an effect(), so it responds to state changes without manual subscription management.
  • GSAP is lazy-loaded once and reused for subsequent toggles.
  • prefers-reduced-motion skips the timeline and applies the order change instantly.
  • DestroyRef kills any active tweens when the component is destroyed.

Source code

<div class="flex w-full max-w-3xl flex-col gap-4">
  <div class="toolbar ...">
    <span>Swap layout with GSAP</span>
    <button
      type="button"
      [attr.aria-pressed]="swapped()"
      (click)="toggleSwap()"
    >
      {{ swapped() ? 'Reset columns' : 'Swap columns' }}
    </button>
  </div>

  <div #stage class="swap-stage ...">
    <div class="grid grid-cols-1 md:grid-cols-2 items-center gap-6">
      <div #textCol class="swap-text space-y-3">
        <h3>They're all here</h3>
        <p>...</p>
        <span class="badge">Angular + GSAP</span>
      </div>

      <div #visualCol class="swap-visual ...">
        <pre><code>...</code></pre>
      </div>
    </div>
  </div>
</div>

Implementation recipe

  1. Create a standalone component with a two-column grid and a toggle button.
  2. Add a swapped = signal(false) and a toggleSwap() method.
  3. Lazy-load GSAP in an effect() that reads swapped().
  4. Skip the first effect run to avoid an entrance animation on load.
  5. Query the two columns and animate them out with xPercent, opacity and scale.
  6. Swap the CSS order at the midpoint of the timeline.
  7. Animate the columns back in to their new positions.
  8. Handle prefers-reduced-motion by applying the order change instantly.
  9. Kill tweens on destroy to avoid orphan animations.

Accessibility notes

  • toggle button is a real <button> with visible focus
  • aria-pressed reflects the swapped state
  • prefers-reduced-motion swaps instantly without motion

Performance notes

  • animate transform and opacity only
  • no layout reads during the animation
  • kill timeline on destroy

Common pitfalls

  • Writing to a signal inside the GSAP tween — this would trigger Angular change detection on every frame. Let GSAP own the DOM transforms.
  • Forgetting to kill the previous timeline before starting a new one, causing conflicting tweens when the user clicks rapidly.
  • Relying on flex-direction: row-reverse instead of order; order works better with CSS Grid and keeps the source order stable for screen readers.
  • Running an entrance animation on load because the effect fires immediately. Use a flag to skip the first run.