StickySectionStack

Stack child sections as sticky layers that scale down progressively as the container scrolls.

StickySectionStack is a client component that makes each child a sticky layer. Earlier layers shrink as later ones scroll over them, driven by scroll progress. Use it for process or feature sections on long pages. It depends on motion/react.

Plan

Define scope, owners and milestones.

Build

Ship small increments behind feature flags.

Ship

Roll out gradually and watch the metrics.

Import#

typescript
import { StickySectionStack } from "@reactberry/system/blocks";

Usage#

typescript
import { StickySectionStack } from "@reactberry/system/blocks";
import { Box, Text } from "@reactberry/system/elements";

const steps = ["Plan", "Build", "Ship"];

export default function Process() {
  return (
    <StickySectionStack
      maxWidth="960px"
      mx="auto"
      sectionMinHeight="70vh"
      sectionProps={{ skin: "card", shape: "rounded", p: "l" }}
    >
      {steps.map((step) => (
        <Box key={step} minHeight="50vh">
          <Text as="h2" fontSize="xxxl" fontWeight="700">
            {step}
          </Text>
        </Box>
      ))}
    </StickySectionStack>
  );
}

Examples#

Tight stack#

Lower stickyStep, scaleStep and a higher minScale give a denser stack with less scaling.

v3.2 — Offline mode
v3.1 — Shared folders
v3.0 — New editor
v2.9 — Dark theme

API#

StickySectionStack#

PropTypeDefaultDescription
children*
ReactNode
—

Each non-empty child becomes one sticky section

topPadding
string
10vh

Top padding of the container

stickyTop
string
4vh

Sticky top offset of every section

stackOffsetBase
number
0

Base vertical offset in pixels applied to each section

stickyStep
number
24

Extra offset in pixels added per section index, so stacked sections peek out below each other

scaleStep
number
0.06

Scale reduction per section that follows

minScale
number
0.82

Lower bound for the final scale

sectionMinHeight
string
auto

minHeight of each sticky wrapper

bottomPadding
string
30vh

Bottom padding of the container

sectionProps
BoxProps
—

Props spread onto the inner Box that wraps each child

children (ReactNode, required): each non-empty child becomes one section. All remaining BoxProps are spread onto the container Box. StickySectionStackProps is exported from the component module but not from @reactberry/system/blocks.

Accessibility#

  • Sections are plain div elements with no roles; content order is unchanged.
  • When the user prefers reduced motion, sections render in normal flow without sticky positioning, scaling, or container padding; sectionProps and the remaining props still apply.

Notes#

  • Section index scales from 1 to max(minScale, 1 - (total - index - 1) * scaleStep) over scroll progress from index / total to 1. The last section never scales.
  • Scroll progress uses useScroll with the container as target and offset ["start start", "end end"]. No scroll container is passed, so progress follows the window scroll even when the stack sits inside a scrollable Box; sticky positioning still follows the nearest scroll container.
  • Children are keyed by index after Children.toArray, so reordering children remounts sections.