Skip to main content
← Back to Motion Recipes
intermediateFeatures

Disappearing Features

Animate feature content in and out while scrolling through a product section.

standalone componentsignalsDestroyRef cleanupSSR-safe animation setupScrollTriggerscrubbed timelineopacityyPercent
Features

Learn and grow with Angular motion

Build scroll-driven experiences that feel premium without sacrificing accessibility or performance.

Top panel fades away as the section scrolls.

Bottom panel rises and takes the spotlight.

How it works

A two-column feature section is placed inside a scrollable viewport. A single scrubbed GSAP timeline is linked to the scroll position of that viewport. As the user scrolls:

  • the left text drifts up and fades to keep it readable but de-emphasized;
  • the top visual panel fades out and moves up;
  • the bottom panel rises and scales into focus, becoming the hero element.

All motion is driven by transform and opacity, so it stays smooth even on lower-end devices.

The Angular way

  • The component has no per-frame signal writes. Scroll progress is handled entirely by GSAP's scrubbed timeline.
  • Browser-only GSAP code is lazy-loaded behind isPlatformBrowser.
  • gsap.context() keeps selectors scoped to the component host.
  • Cleanup happens through DestroyRef so it works even if the component is destroyed before the animation finishes.

Source code

<div
  class="disappearing-viewport relative h-[28rem] w-full overflow-y-auto rounded-2xl border border-border bg-sky-50 dark:bg-sky-950/30"
  [class.reduced-motion]="reducedMotion()"
>
  <div #track class="disappearing-track relative min-h-[250%] w-full px-6 py-10">
    <div class="grid min-h-[24rem] grid-cols-1 gap-8 md:grid-cols-2 md:items-center">
      <div class="disappearing-text space-y-4">
        <span class="pill">Features</span>
        <h2>Learn and grow with Angular motion</h2>
        <p>
          Build scroll-driven experiences that feel premium without sacrificing
          accessibility or performance.
        </p>
      </div>

      <div class="relative h-64 md:h-80">
        <div class="disappearing-panel-top absolute inset-x-0 top-0 z-10 ...">
          Top panel fades away as the section scrolls.
        </div>
        <div class="disappearing-panel-bottom absolute inset-x-0 bottom-0 z-20 ...">
          Bottom panel rises and takes the spotlight.
        </div>
      </div>
    </div>
  </div>
</div>

Implementation recipe

  1. Build a two-column feature section with a pill, headline, body and two stacked visual panels.
  2. Wrap it in a scrollable viewport with a tall track so there is enough scroll distance to scrub through.
  3. Lazy-load gsap and ScrollTrigger inside ngAfterViewInit.
  4. Create a gsap.timeline({ scrollTrigger: { scrub: 1 } }).
  5. Animate the text and panels together from time 0 so they respond to the same scroll progress.
  6. Add a reduced-motion fallback that leaves all content visible and static.
  7. Revert the GSAP context and kill ScrollTriggers on destroy.

Accessibility notes

  • prefers-reduced-motion keeps all panels visible
  • no essential content is hidden permanently
  • headings maintain logical order

Performance notes

  • scrubbed timeline tied to scroll progress
  • avoid reading layout per frame
  • kill timeline and ScrollTrigger on destroy

Common pitfalls

  • Hiding content permanently when the animation finishes. In a scroll-driven demo the resting state should still expose all content.
  • Using filter: blur() or heavy shaders during scroll; stick to transform and opacity.
  • Forgetting to scope the ScrollTrigger to the local viewport when the demo is embedded in a page.