Pinned scenes
A pinned (or "sticky") scroll scene is a tall section with content that stays fixed on screen while the section scrolls past behind it — the classic "scroll-driven story" pattern. It's the foundation nearly every advanced scroll recipe in this site builds on (see Advanced recipes).
Two things make a pinned scene: a pair of Fancy settings you toggle on in the panel ("I am parent
container" and "Get parent", below), and the actual sticky layout itself — the tall section and
its pinned stage — which is built with ordinary CSS (position: sticky, a height), the same way
you'd build it without the plugin. The CSS for that part is covered further down this page, for
whoever is setting up the layout.
See it live: the Section card stack and Stacked cards templates.
I am parent / Get parent
Inside a pinned stage, the pinned element itself never moves — so if it tried to measure its own scroll progress, that progress would freeze the moment it became pinned. The fix is to measure a different box: the whole tall section around it, which keeps moving the entire time.
| Setting | Goes on | What it does |
|---|---|---|
| I am parent container | The tall, outer section — the box everyone should measure against | Marks it as a measuring reference |
| Get parent | Every animated element inside the pinned stage | Measures against the nearest "I am parent" ancestor instead of itself |
With no "I am parent" ancestor above it, "Get parent" silently falls back to measuring the element's direct parent instead — which, inside a pinned stage, is also stuck. If animations freeze the moment the stage pins, this is almost always why: check that "I am parent" is on the outer section and "Get parent" is on every animated element inside the stage.
Structure
section "I am parent" · height: a multiple of 100vh · padding: 0 · overflow-x reset
└─ stage position: sticky · top: 0 · at least one screen tall
└─ content "Get parent" + scroll trigger rows
- The section needs its horizontal overflow reset (see the Safari pitfall below) and its own padding removed — the content's side gutter has to move to the stage (and to every other non-stage content of the section) instead, including on phones.
- A sticky element moves relative to its own parent, not the whole section — so if the stage sits inside an extra wrapper that's shorter than the section, it releases earlier than you'd expect.
The pinned window
A scene that's k screens tall (e.g. 300vh = 3 screens) is pinned from scroll progress
1/(k+1) to k/(k+1). Every animation row inside the scene should be planned to fall inside that
window:
| Scene height | Pinned window | Rows on the 5% grid |
|---|---|---|
| 200vh | 33% – 67% | 35 – 65 |
| 300vh | 25% – 75% | 25 – 75 |
| 400vh | 20% – 80% | 20 – 80 |
| 500vh | 17% – 83% | 20 – 80 |
A row that starts before the window plays while the scene is still scrolling into place (fine for an arriving parallax effect); one that ends after it plays while the stage is being released and already sliding away, which usually looks like it finished too late.
As the very first section of a page (a hero), the scene is already sitting at its pinned-start value the moment the page loads — so rows inside the window start on the visitor's very first scroll, same as anywhere else. Rows placed before that value are shown already partly played. For timings that don't land on the 5% grid, see Scroll math & custom CSS.
If the pinned scene is your page's hero, keep its headline and first line of copy static and visible from load rather than animating them in. That text is very likely your page's largest contentful paint — if it starts hidden (an entrance, or a Fade row whose window hasn't started yet), you delay LCP and show an empty-looking screen on a slow connection. Animate the stage's visuals instead and let the words stay put.
A height that still works without JavaScript
A fixed scene height (say, height: 400vh) can cut off the stage's content on short screens when
there's no JavaScript running — in the Bricks/Etch canvas with the preview off, say, or with
JavaScript blocked. Make the height itself depend on the animation existing, using a Custom row
with no window on the section:
%root% {
--scene-h: calc(var(--fa-scroll--custom) * 0vh + 400vh);
height: var(--scene-h, auto);
}
Without the scroll script, --fa-scroll--custom doesn't exist, which makes --scene-h invalid,
which falls back to height: auto — so the stage simply isn't pinned and its content flows
normally, with nothing cut off.
- Bricks
- Etch
- Section: "I am parent container" in the ScrollTrigger Animations panel, padding set to 0 in the
Spacing control, and in its custom CSS:
%root% { overflow-x: initial; }. - Stage: either the CSS above, or the native controls (Position: Sticky, Top: 0, Min height: 100vh, Overflow: hidden).
- Every animated element inside: "Get parent", plus its own ScrollTrigger rows with Start/End inside the pinned window.
- Never put a gradient overlay directly on the sticky stage at any breakpoint — Bricks then
outputs
position: relativeon that same element, which kills the sticky behaviour. Use a separate absolute "shade" element instead. - Remember: nothing animates in the canvas until the toolbar's FA toggle is on (it's off every time you reopen the builder).
- Section: an element with
padding: 0andoverflow-x: initialin its own class (a class beats Etch's defaults and the plugin's own basesectionrule). - Scene: classes
fa--iparent fa--custom, plus its own class with the height CSS above. - Animated children:
fa--parentplus their row classes, e.g.fa--custom fa--custom--start-25 fa--custom--end-75. - The Etch canvas runs the scroll script live, so the scene animates while you edit (unless you've turned the Fancy toolbar toggle off).
Common problems
- The stage jumps or stutters in Safari / on iPhone. The plugin sets
overflow-x: cliponbodyandsection, and Safari can make a sticky child jump when that's active. Resetoverflow-x: initialon every section that holds a sticky stage. This doesn't reproduce in automated Chromium testing — check it in real Safari. - Animations freeze the moment the stage becomes pinned. The animated elements (or the section itself) are measuring themselves instead of the section. Add "I am parent" to the section and "Get parent" to every animated element inside it.
- Content is cut off on short screens, or in the builder. The scene has a fixed height and the
stage has a fixed
height: 100vhinstead of amin-height. Use the height-with-fallback CSS pattern above, andmin-height(notheight) on the stage. - The scene collapses to a narrow column, or something with
container-typeinside it measures 0px. In Bricks, a section is a centred flex column, so a plaindivinside it shrinks to the width of its content. Give the scene (and any wrapper between the section and the stage) an explicitwidth: 100%. - Text touches the edge of the screen on phones. The section's own padding was removed for the
sticky stage, and nothing picked up that gutter in its place. Add
padding-inline(e.g.clamp(1rem, 5vw, 2rem)) to the stage and to every other direct content of the section, at every breakpoint. - An animation plays after the stage has already been released, or finishes visibly early. Its window falls outside the pinned stretch for that scene height — move the row, or make the scene taller.
- A horizontal scrollbar appears on the page. Something inside the stage is moving sideways
beyond its edges — add
overflow: hiddento the stage itself.