Disappearing Features
Animate feature content in and out while scrolling through a product section.
08
Get the code → Demo
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
DestroyRefso 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
- Build a two-column feature section with a pill, headline, body and two stacked visual panels.
- Wrap it in a scrollable viewport with a tall track so there is enough scroll distance to scrub through.
- Lazy-load
gsapandScrollTriggerinsidengAfterViewInit. - Create a
gsap.timeline({ scrollTrigger: { scrub: 1 } }). - Animate the text and panels together from time 0 so they respond to the same scroll progress.
- Add a reduced-motion fallback that leaves all content visible and static.
- 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 totransformandopacity. - Forgetting to scope the ScrollTrigger to the local viewport when the demo is embedded in a page.