09스크롤 줌아웃
확대돼 있던 이미지가 스크롤할수록 줄어들어 주변 레이아웃이 드러난다.
쓰임제품 상세 첫 화면, 갤러리 인트로
scale 1.4→1scroll(root)줌아웃

<main class="stage is-demo">
<div class="zoomout">
<div class="zoomout__media" aria-hidden="true"></div>
<span class="zoomout__label">스크롤하면 작아진다</span>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개. 변형은 이것만 바꾼다 ──────────────────────────
$scale-from: 1.45; // 처음 확대 배율
$duration: $dur-loop; // 데모 왕복 주기
$color: $subject-cream; // 라벨 글자 색 (ink 무대라 cream)
@include stage($stage-ink);
.zoomout {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
display: grid;
place-items: center;
}
.zoomout__media {
position: absolute;
inset: 6%;
border-radius: $r-card;
background: linear-gradient(135deg, rgba($color, .5), rgba($subject-blue, .5));
transform: scale(#{$scale-from});
// ── 실제 사용 코드: 확대돼 있던 화면이 스크롤할수록 줄어든다 ──
@supports (animation-timeline: scroll()) {
animation: zoom-out linear both;
animation-timeline: scroll(root);
}
}
.zoomout__label {
position: relative;
z-index: 1;
padding: 10px 18px;
border-radius: $r-pill;
background: $stage-ink;
border: 1px solid rgba($color, .5);
color: $color;
font: 700 clamp(12px, 3.8vw, 14px)/1 -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
@keyframes zoom-out { from { transform: scale(#{$scale-from}); } to { transform: scale(1); } }
// ── 데모(.is-demo): 왕복 루프로 흉내 ────────
.stage.is-demo .zoomout__media { animation: zoom-loop $duration ease-in-out infinite; }
@keyframes zoom-loop { 0%, 100% { transform: scale(#{$scale-from}); } 50% { transform: scale(1); } }
@media (prefers-reduced-motion: reduce) {
.zoomout__media, .stage.is-demo .zoomout__media { animation: none !important; transform: scale(1); }
}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);
}
.zoomout {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
display: grid;
place-items: center;
}
.zoomout__media {
position: absolute;
inset: 6%;
border-radius: 16px;
background: linear-gradient(135deg, rgba(255, 247, 230, 0.5), rgba(47, 109, 246, 0.5));
transform: scale(1.45);
}
@supports (animation-timeline: scroll()) {
.zoomout__media {
animation: zoom-out linear both;
animation-timeline: scroll(root);
}
}
.zoomout__label {
position: relative;
z-index: 1;
padding: 10px 18px;
border-radius: 999px;
background: #17141a;
border: 1px solid rgba(255, 247, 230, 0.5);
color: #fff7e6;
font: 700 clamp(12px, 3.8vw, 14px)/1 -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
@keyframes zoom-out {
from {
transform: scale(1.45);
}
to {
transform: scale(1);
}
}
.stage.is-demo .zoomout__media {
animation: zoom-loop 2s ease-in-out infinite;
}
@keyframes zoom-loop {
0%, 100% {
transform: scale(1.45);
}
50% {
transform: scale(1);
}
}
@media (prefers-reduced-motion: reduce) {
.zoomout__media, .stage.is-demo .zoomout__media {
animation: none !important;
transform: scale(1);
}
}import type { CSSProperties, ReactNode } from "react";
import styles from "./ZoomOutScroll.module.scss";
export type ZoomOutScrollProps = {
children: ReactNode;
/** 처음 확대 배율 (기본 1.45) */
scaleFrom?: number;
/** 라벨 글자 색 */
color?: string;
className?: string;
};
type Vars = CSSProperties & { "--scale-from"?: string; "--color"?: string };
export default function ZoomOutScroll({
children,
scaleFrom = 1.45,
color = "#fff7e6",
className = "",
}: ZoomOutScrollProps) {
const vars: Vars = { "--scale-from": `${scaleFrom}`, "--color": color };
return (
<div className={`${styles.wrap} ${className}`} style={vars}>
<div className={styles.media} aria-hidden="true" />
<span className={styles.label}>{children}</span>
</div>
);
}09 스크롤 줌아웃 — 측정 누적면적 22.201% · 움직인 프레임 12/23GODRICH 원작 · zip 에 SCSS·React 포함 · CSS 패럴랙스 스크롤 9종 — AOS·GSAP 없이, JS 0줄
이 데모가 속한 글
CSS 패럴랙스 스크롤 9종 — AOS·GSAP 없이, JS 0줄CSS 패럴랙스 스크롤 아홉 개를 모았습니다. 레이어마다 이동 속도를 다르게 주는 원리 하나로 배경이 내용보다 느리게 움직이는 착시를 만듭니다. 격자에서는 아홉 개가 2초 간격으로 자동 반복되니 먼저 눈으로 훑고 마 이 글의 데모 전부 보기


