Stretchy Footer
Dia-style rubber overscroll — pull past the bottom and a rainbow aurora stretches up, then springs back when you let go.
Not a scroll-progress footer reveal — an overflow rubber-band. In the preview, scroll past the end (or hit Show effect): the page lifts on a spring and a stepped pyramid of blurred gradient columns (magenta through sunlight to deep navy) stretches up to fill the gap. Every column has its own spring, so the top edge ripples as it snaps back. Swap palettes, blur, glow, and snap below, or Flip it into a valley — tall edges, dipped middle — so the ripple runs in from the sides. Rotate 180° turns the aurora upside down in place: same pull past the bottom, but the columns hang from the lifted page with the gradient reversed.
Preview
Tall edges, dipped middle
Aurora hangs from the page
Installation
pnpm dlx shadcn@latest add @23rd/stretchy-footerOr install straight from the public GitHub source registry:
pnpm dlx shadcn@latest add radiumcoders/23rd.dev/stretchy-footerUsage
Use it as the scroll viewport. Put your page (and a normal footer block) in children. Wheel or touch past the bottom stretches the aurora with a rubber-band falloff; scrolling back up retraces it, and letting go snaps it home. Trackpad momentum can't re-pull it, and nested scrollers such as code blocks keep their own wheel. Honors prefers-reduced-motion (no stretch). One vanilla engine drives both the React and Svelte versions, and it doesn't need motion.
"use client"
import { StretchyFooter } from "@/components/ui/stretchy-footer"
export function Page() {
return (
<StretchyFooter className="h-svh bg-background">
<main className="min-h-[120%] px-8 py-24 text-foreground">
{/* …page content… */}
</main>
<footer className="px-8 pb-10 text-center text-muted-foreground">
23rd — opinionated UI for shippers
</footer>
</StretchyFooter>
)
}On a full document (window scroll), pass windowScroll and mark the page root with data-stretchy-page so the content rolls up into the aurora:
"use client"
import { StretchyFooter } from "@/components/ui/stretchy-footer"
export function DocsChrome() {
return (
<>
<div data-stretchy-page>{/* page content */}</div>
<StretchyFooter windowScroll />
</>
)
}Props
| Prop | Type | Default |
|---|---|---|
children | ReactNode / snippet | — |
scrollRef | RefObject<HTMLElement | null> | React — self as scroller |
scrollEl | HTMLElement | Svelte — self as scroller |
windowScroll | boolean | false |
contentSelector | string | [data-stretchy-page] |
maxStretch | number | 280 |
colors | string[] | magenta → navy — gradient stops, top to floor |
stiffness | number | 380 |
damping | number | 32 |
columns | number | 9 — steps down from the middle |
blur | number | 16 — px |
glow | number | 0 — white floor bloom, 0–1 |
flip | boolean | false — tall edges, dipped middle |
rotate | 0 | 180 | 0 — 180 hangs the aurora from the page |
label | string | Stretchy overflow — scroll region label |
demoId | string | — pairs with playStretchyFooterDemo({ target }) |
className | string | React only |
class | string | Svelte only |