01Hero — an intro where background and heading shift at different scroll ratios
As the page's scroll value changes, the background image and heading move at different ratios, and the offset between the two layers creates depth.
Best forA landing page's top intro area, a product's first-view screen
--svtranslateY@property

<main class="stage" id="stage">
<div class="hero__bg" aria-hidden="true"></div>
<div class="hero__head">
<p class="hero__eyebrow">New</p>
<h1 class="hero__title">A lightweight landing page</h1>
<p class="hero__sub">One scroll value moves two layers at different ratios.</p>
<button class="hero__cta" type="button">Get started</button>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-loop;
$easing: $ease-spring;
$color: $subject-cream;
@include stage($stage-ink);
@property --sv {
syntax: "<number>";
inherits: true;
initial-value: 0;
}
.stage {
--sv: 0;
position: relative;
overflow: hidden;
animation: scroll-sim $duration $easing infinite;
}
.hero__bg {
position: absolute;
inset: -10% -10% -30% -10%;
border-radius: $r-card;
background: linear-gradient(180deg, rgba($color, .28), rgba($color, 0));
transform: translateY(calc(var(--sv) * -18px));
}
.hero__head {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: $sp-2;
padding: $sp-8 $sp-6;
transform: translateY(calc(var(--sv) * -42px));
}
.hero__eyebrow {
margin: 0;
padding: $sp-1 $sp-3;
border-radius: $r-pill;
background: rgba($color, .16);
color: $color;
font-weight: 600;
font-size: 11px;
line-height: 1.6;
}
.hero__title {
margin: 0;
color: $stage-paper;
font-weight: 700;
font-size: clamp(20px, 6vmin, 28px);
line-height: 1.25;
}
.hero__sub {
margin: 0;
max-width: 34ch;
color: rgba($stage-paper, .78);
font-weight: 400;
font-size: 13px;
line-height: 1.55;
}
.hero__cta {
margin-top: $sp-2;
border: 0;
border-radius: $r-pill;
padding: $sp-3 $sp-6;
font-family: inherit;
font-weight: 600;
font-size: 13px;
line-height: 1;
color: $stage-ink;
background: $color;
cursor: pointer;
}
@keyframes scroll-sim {
0%, 8% { --sv: 0; }
50% { --sv: 1; }
92%, 100% { --sv: 0; }
}
@media (max-width: 340px) {
.hero__head { padding: $sp-3 $sp-4; gap: $sp-1; }
.hero__sub { max-width: 26ch; }
.hero__cta { margin-top: 0; padding: $sp-2 $sp-4; }
}
@media (prefers-reduced-motion: reduce) {
.stage { animation: none; }
.hero__bg, .hero__head { transform: none; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #17141a;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
@property --sv {
syntax: "<number>";
inherits: true;
initial-value: 0;
}
.stage {
--sv: 0;
position: relative;
overflow: hidden;
animation: scroll-sim 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
.hero__bg {
position: absolute;
inset: -10% -10% -30% -10%;
border-radius: 16px;
background: linear-gradient(180deg, rgba(255, 247, 230, 0.28), rgba(255, 247, 230, 0));
transform: translateY(calc(var(--sv) * -18px));
}
.hero__head {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
padding: 32px 24px;
transform: translateY(calc(var(--sv) * -42px));
}
.hero__eyebrow {
margin: 0;
padding: 4px 12px;
border-radius: 999px;
background: rgba(255, 247, 230, 0.16);
color: #fff7e6;
font-weight: 600;
font-size: 11px;
line-height: 1.6;
}
.hero__title {
margin: 0;
color: #ffffff;
font-weight: 700;
font-size: clamp(20px, 6vmin, 28px);
line-height: 1.25;
}
.hero__sub {
margin: 0;
max-width: 34ch;
color: rgba(255, 255, 255, 0.78);
font-weight: 400;
font-size: 13px;
line-height: 1.55;
}
.hero__cta {
margin-top: 8px;
border: 0;
border-radius: 999px;
padding: 12px 24px;
font-family: inherit;
font-weight: 600;
font-size: 13px;
line-height: 1;
color: #17141a;
background: #fff7e6;
cursor: pointer;
}
@keyframes scroll-sim {
0%, 8% {
--sv: 0;
}
50% {
--sv: 1;
}
92%, 100% {
--sv: 0;
}
}
@media (max-width: 340px) {
.hero__head {
padding: 12px 16px;
gap: 4px;
}
.hero__sub {
max-width: 26ch;
}
.hero__cta {
margin-top: 0;
padding: 8px 16px;
}
}
@media (prefers-reduced-motion: reduce) {
.stage {
animation: none;
}
.hero__bg, .hero__head {
transform: none;
}
}import styles from "./Hero.module.scss";
// 스크롤 값 하나(--sv)를 두 층(배경·헤드)이 서로 다른 비율(-18px, -42px)로 받아 시차 효과를 만든다.
// 계산 겹: 스크롤 값(--sv) -> 두 층 각각의 translateY 비율 보간. 리스너 없음(자동 재생).
export default function Hero() {
return (
<main className={styles.stage}>
<div className={styles.hero__bg} aria-hidden="true" />
<div className={styles.hero__head}>
<p className={styles.hero__eyebrow}>신제품</p>
<h1 className={styles.hero__title}>가벼운 랜딩 페이지</h1>
<p className={styles.hero__sub}>스크롤 값 하나로 두 층이 서로 다른 비율로 움직입니다.</p>
<button className={styles.hero__cta} type="button">
지금 시작하기
</button>
</div>
</main>
);
}01 Hero — an intro where background and heading shift at different scroll ratios — Measured area 17.827% · moving frames 20/23Original by GODRICH · SCSS and React ship in the zip · Hero Section Design Examples: 9 Patterns
The post this demo lives in
Hero Section Design Examples: 9 PatternsHero section design examples: One sets a card reveal delay from its order value past a visibility threshold; one resizes See all demos in this post


