Swap Column Features
Swap text and visual columns with a smooth GSAP layout transition.
Demo
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 + GSAPgsap.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 andaria-pressedto 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-motionskips the timeline and applies theorderchange instantly.DestroyRefkills 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
- Create a standalone component with a two-column grid and a toggle button.
- Add a
swapped = signal(false)and atoggleSwap()method. - Lazy-load GSAP in an
effect()that readsswapped(). - Skip the first effect run to avoid an entrance animation on load.
- Query the two columns and animate them out with
xPercent,opacityandscale. - Swap the CSS
orderat the midpoint of the timeline. - Animate the columns back in to their new positions.
- Handle
prefers-reduced-motionby applying the order change instantly. - 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-reverseinstead oforder;orderworks 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.