MUST

·

Layout · sticky

Footer reveal

This is the best effort-to-impact ratio in the entire lab. There is no animation in the core of it — the footer is pinned to the bottom of the viewport from the moment the section loads, the page above it is simply opaque, and the final screen of scroll is a transparent window that uncovers it. Three layers and a spacer. The GSAP is optional garnish: a slow parallax on the footer's contents, scrubbed against that spacer, so the footer feels like it is settling into place rather than being slid under a door. Get the stacking right and the rest is decoration; get it wrong and no amount of animation will save it.

Open the live demo →

How to build

Panel + script

Free plan gets you part of the way

The panel covers some of this. The rest needs the script, which needs a paid plan — build the panel half now, come back for the rest.

How the two routes work →

Plugins

ScrollTrigger

Build time

25 min. Ten of those are getting the z-index right.

No-code?

The reveal itself is pure CSS — position, z-index and a spacer — so it is entirely no-code. Only the parallax on the footer contents needs the panel or a script.

01

Webflow structure

element · class · attribute

<b>Div</b> <i>page_stage</i> ← position: relative. Wraps everything. <b>Div</b> <i>page_footer-layer</i> ← position: absolute; inset: 0; z-index: 0 <b>Div</b> <i>footer</i> ← position: sticky; bottom: 0; height: 88vh <b>Div</b> <i>footer_top</i> <b>Div</b> <i>footer_mark</i> ← the oversized wordmark <b>Div</b> <i>footer_bottom</i> <b>Div</b> <i>page_main</i> ← position: relative; z-index: 1; OPAQUE background … every section of the page … <b>Div</b> <i>page_spacer</i> ← position: relative; z-index: 1; height: 88vh NO background. This is the window.

02

Styles to set in the Designer

page_stage | <code>position: relative</code>. It is the containing block for the footer layer — without it the layer escapes to the body and the whole thing falls apart. page_footer-layer | <code>position: absolute; inset: 0; z-index: 0;</code> <strong>plus <code>display: flex; flex-direction: column; justify-content: flex-end</code></strong>. The footer's natural position must be the bottom of the stage — see the gotchas. footer | <code>position: sticky; bottom: 0;</code> and a fixed height that <strong>matches the spacer exactly</strong>. Sticky, not fixed — fixed escapes the section and shows up on top of whatever follows. page_main | <code>position: relative; z-index: 1;</code> and an <strong>opaque background</strong>. This is the entire mechanism: the footer is not hidden, it is covered. page_spacer | Same height as the footer, <code>z-index: 1</code>, and <strong>no background</strong>. Transparent, so the footer shows through it. footer height | Keep it under 90vh. A footer taller than the viewport can never be seen in one piece, and the reveal reads as a broken layout.

03

Building it with the Interactions panel

no code, free plan
  1. The reveal itself needs no interaction at all — it's the position: sticky + z-index + spacer stack from the structure above, set entirely in the Designer's style panel.
  2. For the optional content parallax: select footer_mark → Interactions → trigger Scroll into view → While scrolling in view, scoped to page_spacer.
  3. Animate Move Y from 130px to 0px, Ease None — scrubbed motion should never ease against the scrollbar.
  4. Repeat with smaller offsets for footer_top (70px → 0) and footer_bottom (40px → 0, plus Opacity 20% → 70%) so the layers settle at slightly different rates.

What the panel still can’t do. Only custom code can close these gaps — there is no panel setting for any of them.

See “The script” below for the exact code.

04

Custom attributes

Element settings → Custom attributes

05

CSS

Page settings → Inside <head> tag

Inside <head> tag

Copy

06

The script

Page settings → Before </body> tag

Before </body> tag

Copy

<!-- Optional. The reveal works with zero JavaScript — this is the polish. --> <!-- Page settings → Before </body> tag --> <script> document.addEventListener("DOMContentLoaded", () => { gsap.registerPlugin(ScrollTrigger); if (matchMedia("(prefers-reduced-motion: reduce)").matches) return; const spacer = document.querySelector(".page_spacer"); if (!spacer) return; // The footer is on screen the entire time — it is only ever covered — so // there is nothing to trigger on. Scrub against the SPACER instead: that // is the window, and its progress IS the reveal's progress. gsap.timeline({ scrollTrigger: { trigger: spacer, start: "top bottom", // window starts entering end: "bottom bottom", // window fully open scrub: true, invalidateOnRefresh: true }, defaults: { ease: "none" } // scrubbed: any ease fights the scrollbar }) .fromTo(".footer_top", { y: 70 }, { y: 0 }, 0) .fromTo(".footer_mark", { y: 130 }, { y: 0 }, 0) .fromTo(".footer_bottom", { y: 40, opacity: 0.2 }, { y: 0, opacity: 0.7 }, 0); }); </script>

07

Gotchas that will cost you an hour

  1. The footer must sit at the bottom of its layer. position: sticky; bottom: 0 only engages when the element's natural position is below the viewport bottom. Leave the footer at the top of the layer (the default) and it never sticks at all — it scrolls past like any static block and you see nothing but background. justify-content: flex-end on the layer is the fix.
  2. The main content must have an opaque background. Not the body, not the section — the wrapper with z-index: 1. Leave it transparent and the footer shows through the entire page, which looks like a rendering bug and is the number one failure of this pattern.
  3. Spacer height must equal footer height. Spacer shorter, and the footer is never fully revealed. Spacer taller, and you get a strip of empty page under the footer. Use the same value, ideally a CSS variable.
  4. Sticky, not fixed. position: fixed takes the footer out of the section entirely, so it floats over the next page section too. Sticky stops at the end of its containing block, which is exactly what you want.
  5. A transform on any ancestor kills it. Sticky and absolute both resolve against the nearest transformed ancestor. One page-wide parallax wrapper and the footer detaches — check for will-change: transform too.
  6. overflow: hidden anywhere above the stage kills sticky. Silently. Same rule as the stacking-cards lesson, and the usual culprit is a marquee section elsewhere on the page.
  7. Keep the footer under 90vh. Taller than the viewport and it can never be seen whole; the visitor scrolls into a footer that is still scrolling, which feels like the page has stopped ending.
08

Push it further

  • Round the bottom corners of page_main and scale it to 0.98 across the same scrub — the page then reads as a card lifting away rather than a curtain going up.
  • Put the oversized wordmark on a slower parallax than the links (130 vs 70) — two speeds inside the footer is what stops it reading as one flat block.
  • Wire the footer's background colour into the theme-on-scroll system so the nav inverts as the footer arrives.

Webflow Motion Lab — GSAP 3.15, all plugins free.

6

/ 30