FootersSimpleApril 7, 2025
Footer Parallax Text
Large background text in footer drifts vertically as you scroll past it, creating a layered depth effect behind the footer content. Source: itsjay.us footer section.
View Full Demo →Preview
Source
demo.jsx
import FooterParallaxText from "./index.jsx";
import styles from "./demo.module.css";
const PROJECTS = [
{ name: "Sheabinta", cat: "Shopify" },
{ name: "PATHS Nonprofit", cat: "Web Design" },
{ name: "Socialstats", cat: "Branding" },
{ name: "Kevin Davis", cat: "Portfolio" },
{ name: "Delivrd", cat: "Web App" },
];
export default function FooterParallaxTextDemo() {
return (
<div>
{/* Hero — dark full-viewport */}
<section className={styles.hero}>
<h1 className={styles.heroTitle}>
<span>Motion-forward</span>
<span>design studio</span>
</h1>
<p className={styles.heroSub}>No-5 Studio · New York</p>
</section>
{/* Work section — light, gives vertical space above the footer */}
<section className={styles.work}>
<p className={styles.workLabel}>Selected work — 2023/25</p>
<ul className={styles.workList}>
{PROJECTS.map((p) => (
<li key={p.name} className={styles.workItem}>
<span className={styles.workName}>{p.name}</span>
<span className={styles.workCat}>{p.cat}</span>
</li>
))}
</ul>
</section>
{/* The actual component — scroll here to see the parallax text */}
<FooterParallaxText
backgroundText="no-5.studio"
navLinks={[
{ label: "Work", href: "#" },
{ label: "About", href: "#" },
{ label: "Contact", href: "#" },
]}
email="hello@no-5.studio"
copyright="© 2025 No-5 Studio. All rights reserved."
/>
</div>
);
}
index.jsx
"use client";
import { useRef } from "react";
import { useScroll, useTransform, motion } from "framer-motion";
import styles from "./styles.module.css";
export default function FooterParallaxText({
backgroundText = "no-5.studio",
navLinks = [],
email = "",
copyright = "",
}) {
const ref = useRef(null);
const { scrollYProgress } = useScroll({
target: ref,
offset: ["start end", "end start"],
});
const y = useTransform(scrollYProgress, [0, 1], ["-10vh", "10vh"]);
return (
<footer ref={ref} className={styles.footer}>
<motion.h2
className={styles.backgroundText}
style={{ y }}
aria-hidden="true"
>
{backgroundText}
</motion.h2>
<div className={styles.content}>
<div className={styles.top}>
<p className={styles.email}>{email}</p>
<nav className={styles.nav}>
{navLinks.map((link) => (
<a key={link.label} href={link.href} className={styles.navLink}>
{link.label}
</a>
))}
</nav>
</div>
<div className={styles.bottom}>
<p className={styles.copyright}>{copyright}</p>
</div>
</div>
</footer>
);
}
demo.module.css
.hero {
height: 100vh;
background: #0d0d0d;
color: #ffffff;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 3rem;
}
.heroTitle {
display: flex;
flex-direction: column;
font-size: clamp(3rem, 8vw, 7.5rem);
font-weight: 700;
letter-spacing: -0.04em;
line-height: 0.95;
margin-bottom: 2rem;
}
.heroSub {
font-size: 0.8125rem;
letter-spacing: 0.08em;
text-transform: uppercase;
opacity: 0.35;
}
.work {
background: #f5f5f0;
padding: 3rem 3rem 4rem;
}
.workLabel {
font-size: 0.75rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: #9b9b9b;
margin-bottom: 2rem;
}
.workList {
list-style: none;
padding: 0;
margin: 0;
}
.workItem {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem 0;
border-top: 1px solid #e5e5e5;
}
.workItem:last-child {
border-bottom: 1px solid #e5e5e5;
}
.workName {
font-size: clamp(1.25rem, 2.5vw, 2rem);
font-weight: 600;
letter-spacing: -0.02em;
color: #1a1a1a;
}
.workCat {
font-size: 0.8125rem;
color: #9b9b9b;
}
styles.module.css
.footer {
position: relative;
overflow: hidden;
background-color: #111111;
color: #ffffff;
padding: 6rem 3rem 3rem;
min-height: 280px;
}
.backgroundText {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: clamp(4rem, 12vw, 10rem);
font-weight: 700;
letter-spacing: -0.04em;
color: #ffffff;
opacity: 0.06;
white-space: nowrap;
user-select: none;
will-change: transform;
pointer-events: none;
z-index: 0;
}
.content {
position: relative;
z-index: 1;
max-width: 1100px;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 3rem;
}
.top {
display: flex;
justify-content: space-between;
align-items: flex-start;
flex-wrap: wrap;
gap: 1.5rem;
}
.email {
font-size: clamp(1rem, 2vw, 1.25rem);
color: rgba(255, 255, 255, 0.6);
}
.nav {
display: flex;
gap: 2rem;
}
.navLink {
font-size: 0.9375rem;
color: rgba(255, 255, 255, 0.7);
text-decoration: none;
transition: color 250ms ease;
}
.navLink:hover {
color: #ffffff;
}
.bottom {
border-top: 1px solid rgba(255, 255, 255, 0.1);
padding-top: 1.5rem;
}
.copyright {
font-size: 0.8125rem;
color: rgba(255, 255, 255, 0.4);
}
Dependencies
framer-motion