GODRICH
#css애니메이션#버튼#로딩#전환효과#스크롤#대시보드#실전ui#디자인트렌드

홈페이지 스크롤 효과 9가지 — 애플 페이지처럼 되는 CSS

홈페이지 스크롤 효과는 페이지를 내리는 손가락 하나에 화면이 반응하게 만드는 장치로, 애플 제품 페이지가 이 분야의 기준입니다. 자바스크립트 라이브러리 하나도 없이 CSS만으로 가로 패널·고정 장면 교체·단어 채움·배경색 전환까지 아홉 가지를 실제로 돌려본 코드로 모았습니다.

자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip

아홉 개는 화면에 나오는 순서입니다. 페이지를 열자마자 보이는 것(01)에서 시작해, 읽던 본문이 끝나며 만나는 것(02), 본문 흐름 속에서 카드가 쌓이고 문장이 차오르는 것(03~07), 그리고 목록이 반응하거나 한 장이 공개되는 마무리(08~09) 순서입니다. 부드러운 스크롤 효과로 애플식 페이지를 위에서 아래로 한 번 훑는 길과 같습니다. 같은 축에서 상단 바가 붙어 있고 떨어지는 쪽은 CSS 헤더 고정 9가지 응용이고, 장면 하나하나가 이야기를 끌어당기는 쪽은 스크롤 스토리텔링 섹션 9종입니다.

01가로로 넘어가는 이야기 패널

스크롤 애니메이션 css 로 무엇이 보이나

세로로 스크롤하면 고정된 화면 안에서 패널이 가로로 넘어가는, 애플 제품 페이지의 대표 구성입니다. scroll-timeline이 스크롤 상자의 위치를 그대로 읽어 트랙을 밀고, position: sticky가 화면을 고정합니다.

가로로 넘어가는 이야기 패널
새 창에서 열기

01 · 가로로 넘어가는 이야기 패널 — 측정 누적면적 51.539% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

animation-timeline: scroll()scroll-timeline: --hp-scrollposition: sticky

코드에서 볼 곳

제품 특징 소개나 포트폴리오 스토리에 씁니다.

.hp__scroller {
  overflow-y: auto;
  scroll-timeline: --hp-scroll block;
}
.hp__inner { height: 300%; }          // 스티키 영역이 화면 3번 깊이
.hp__pin {
  position: sticky;
  top: 0;
  height: calc(100% / 3);
  overflow: hidden;
}
.hp__track {
  display: flex;
  width: 300%;                        // 패널 3장 = 화면의 300%
  animation: hp-slide linear both;
  animation-timeline: --hp-scroll;
}
@keyframes hp-slide {
  from { transform: translateX(0); }
  to   { transform: translateX(-66.6667%); } // 두 판 너비 ÷ 트랙 폭
}

02끝에서 드러나는 커튼 푸터

화면에서 일어나는 일

본문 패널이 푸터 위를 지나가다가, 스크롤 끝에서 비켜주며 푸터를 커튼처럼 드러냅니다. 푸터는 문서 맨 끝에 두되 position: sticky; bottom: 0으로 뷰포트 바닥에 미리 붙여 둡니다.

끝에서 드러나는 커튼 푸터
새 창에서 열기

02 · 끝에서 드러나는 커튼 푸터 — 측정 누적면적 16.541% · 움직인 프레임 13/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

z-indexposition: stickySafari에서도 그대로

코드 읽는 법

연락처·저작권 푸터나 마무리 인사에 어울립니다.

.fc__main {
  position: relative;
  z-index: 1;                    // 본문이 푸터 위를 지나간다
  min-height: calc(100% + 64px); // 시작부터 푸터를 완전히 덮는다
  margin-bottom: 64px;           // 스크롤 끝에서 푸터 칸이 열린다
  background: #fff;
}
.fc__foot {
  position: sticky;
  bottom: 0;
  z-index: 0;
  height: 64px;
  background: linear-gradient(120deg, #b9f0d8, #ffd6e8);
}

03겹쳐 쌓이는 카드

어떤 움직임인가

카드마다 position: sticky가 걸려 있어 다음 카드가 와서 위에 포개지고, 지나간 카드는 자리에 쌓인 채 남습니다. top 값을 한 칸씩 깊어지게 주면 포개지는 간격이 생깁니다.

겹쳐 쌓이는 카드
새 창에서 열기

03 · 겹쳐 쌓이는 카드 — 측정 누적면적 30.731% · 움직인 프레임 15/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

position: stickytop 간격JS 0줄

어디를 바꾸면 되나

서비스 소개 단계나 요금제 비교 카드에 씁니다.

.sk__card {
  position: sticky;
  min-height: 40px;
  margin-bottom: 8px;
  border-radius: 16px;
  box-shadow: 0 6px 12px rgba(23, 20, 26, .32);
}
.sk__card--1 { top: 8px;  background: #ffd6e8; }
.sk__card--2 { top: 16px; background: #d3ecff; }
.sk__card--3 { top: 24px; background: #e4f7d9; }
.sk__card--4 { top: 32px; background: #fff3c9; }

04고정 화면 장면 교체

무엇이 보이나

화면은 제자리에 고정한 채 스크롤한 만큼 장면이 서로 번갈아 부드럽게 바뀌는, 스크롤텔링의 핵심 동작입니다. 장면 셋이 스크롤 타임라인 한 줄을 공유하고 키프레임 구간으로 나눠 갖습니다.

고정 화면 장면 교체
새 창에서 열기

04 · 고정 화면 장면 교체 — 측정 누적면적 51.54% · 움직인 프레임 14/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

animation-timeline: scroll()키프레임 구간opacity

코드에서 볼 곳

사용법 3단계 안내나 제품 스토리에 씁니다.

.ps__scroll {
  overflow-y: auto;
  scroll-timeline: --ps-scroll block;
}
.ps__pin {
  position: sticky;
  top: 0;
  height: calc(100% / 3);
  display: grid;
}
.ps__scene--1 { animation: ps-1 linear both; animation-timeline: --ps-scroll; }
.ps__scene--2 { animation: ps-2 linear both; animation-timeline: --ps-scroll; }
.ps__scene--3 { animation: ps-3 linear both; animation-timeline: --ps-scroll; }
@keyframes ps-1 { 0% { opacity: 1; } 33%, 100% { opacity: 0; } }
@keyframes ps-2 { 0%, 30% { opacity: 0; } 38%, 62% { opacity: 1; } 70%, 100% { opacity: 0; } }
@keyframes ps-3 { 0%, 64% { opacity: 0; } 74%, 100% { opacity: 1; } }

05단어마다 차오르는 문장

화면에서 일어나는 일

문장의 단어가 스크롤 위치를 따라 하나씩 선명해지는, 애플 소개 문장에서 볼 수 있는 효과입니다. 단어마다 각자의 view() 타임라인이 붙어서, 따로 시간차를 주지 않아도 지오메트리가 저절로 순서를 만듭니다.

단어마다 차오르는 문장
새 창에서 열기

05 · 단어마다 차오르는 문장 — 측정 누적면적 4.778% · 움직인 프레임 17/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

animation-range 겹침view()글자색만

코드 읽는 법

랜딩 첫 문장이나 브랜드 선언 문구에 씁니다.

.wf__w {
  color: rgba(23, 20, 26, .16);   // 옅은 상태가 기본
  animation: wf-in linear both;
  animation-timeline: view();     // 단어 각자의 타임라인
  animation-range: entry 0% cover 42%;
}
@keyframes wf-in {
  from { color: rgba(23, 20, 26, .16); }
  to   { color: #17141a; }
}

06형광펜이 그어지는 강조

어떤 움직임인가

스크롤이 닿으면 강조할 말 뒤로 형광펜 색이 왼쪽에서 오른쪽으로 그어지듯 채워집니다. 글자는 그대로 두고 배경 그라디언트의 폭(background-size)만 자라게 하는 것이 핵심입니다.

형광펜이 그어지는 강조
새 창에서 열기

06 · 형광펜이 그어지는 강조 — 측정 누적면적 1.592% · 움직인 프레임 14/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

background-sizeview()글자 안 건드림

어디를 바꾸면 되나

섹션 제목 강조나 세일 문구에 씁니다.

.mk__hl {
  background: linear-gradient(#2f6df6, #2f6df6) no-repeat 0 62%;
  background-size: 0% 58%;
  padding: 0 4px;
  animation: mk-sw linear both;
  animation-timeline: view();
  animation-range: entry 30% cover 60%;
}
@keyframes mk-sw {
  from { background-size: 0% 58%; }
  to   { background-size: 100% 58%; }
}

07구간마다 바뀌는 배경색

무엇이 보이나

페이지를 내리는 동안 배경색이 구간마다 부드럽게 바뀌어 장이 바뀌는 느낌을 줍니다. 배경 레이어를 position: sticky로 뒤에 붙이고, 스크롤 타임라인이 색 키프레임을 돌립니다.

구간마다 바뀌는 배경색
새 창에서 열기

07 · 구간마다 바뀌는 배경색 — 측정 누적면적 48.633% · 움직인 프레임 8/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

animation-timeline: scroll()background-colorroot 타임라인

코드에서 볼 곳

서사형 랜딩이나 루틴·요금 안내에 씁니다.

.ts__scroll {
  overflow-y: auto;
  scroll-timeline: --ts-scroll block;
}
.ts__bg {
  position: sticky;
  top: 0;
  height: 100%;
  margin-bottom: -100%;  // 섹션이 배경 위로 오게 끌어올린다
  animation: ts-bg linear both;
  animation-timeline: --ts-scroll;
}
@keyframes ts-bg {
  0%   { background-color: #fff7e6; }
  50%  { background-color: #b8f0d0; }
  100% { background-color: #ff9db8; }
}

08스크롤 속도에 기우는 목록

화면에서 일어나는 일

스크롤을 빨리 하면 목록 카드가 그 속도만큼 옆으로 기울었다가, 멈추면 제자리로 돌아옵니다. 속도를 재는 짧은 스크립트가 --vel 변수에 값을 넣고 기울기 계산은 CSS calc가 맡습니다.

스크롤 속도에 기우는 목록
새 창에서 열기

08 · 스크롤 속도에 기우는 목록 — 측정 누적면적 18.627% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

JS 10줄--vel 변수skewY

코드 읽는 법

갤러리 피드나 뉴스 목록에 씁니다.

var box = document.querySelector('.feed');
var last = box.scrollTop, vel = 0;
box.addEventListener('scroll', function () {
  vel = Math.max(-24, Math.min(24, box.scrollTop - last));
  last = box.scrollTop;
});
(function decay() {
  vel *= 0.86;
  box.style.setProperty('--vel', vel.toFixed(2));
  requestAnimationFrame(decay);
})();
.feed__row {
  transform: skewY(calc(var(--vel, 0) * -0.18deg));
}

09스포트라이트 서클

어떤 움직임인가

고정된 화면에서 원형 창이 스크롤에 맞춰 점점 커지며 감춰둔 이미지를 드러냅니다. 밝은 화면을 위에 얹고 clip-path: circle()의 반지름을 스크롤 타임라인으로 키웁니다.

스포트라이트 서클
새 창에서 열기

09 · 스포트라이트 서클 — 측정 누적면적 46.333% · 움직인 프레임 11/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

clip-path: circle()scroll-timeline: --sl-scroll이미지 공개

어디를 바꾸면 되나

제품 공개 순간이나 아티스트 소개에 씁니다.

.sl__spot {
  grid-area: 1 / 1;
  background: linear-gradient(135deg, #2f6df6, #8fb4ff);
  clip-path: circle(14% at 50% 50%);
  animation: sl-open linear both;
  animation-timeline: --sl-scroll;
}
@keyframes sl-open {
  from { clip-path: circle(14% at 50% 50%); }
  to   { clip-path: circle(75% at 50% 50%); }
}

어디서 깨지는가 — 함정 하나

가장 많이 걸린 함정은 position: sticky가 흐름 안에서 제 자리를 차지한다는 점이었습니다. 02번을 처음 만들 때 푸터를 본문 앞에 두었더니 스크롤 끝까지 내려도 푸터가 한 번도 나타나지 않았습니다. 푸터를 문서 맨 끝으로 옮기고 본문에 margin-bottom을 줘서야 비로소 본문이 푸터 위를 지나가다 끝에서 비켜주는 그림이 나왔습니다. 07번도 같은 병을 앓았습니다. 배경 레이어를 sticky로 두었더니 배경이 흐름에서 화면 한 칸을 통째로 차지해서 섹션 칩들이 화면 밑에 밀려 캡처에 한 번도 잡히지 않았고, margin-bottom: -100%로 끌어올린 뒤에야 배경 위에 올라앉았습니다. 또 하나, 이 편의 스크롤 타임라인(scroll()·view())은 설치된 크롬에서 직접 렌더해 확인했지만 파이어폭스·사파리는 아직 지원하지 않아서, 지원하지 않는 브라우저에서는 05의 문장이 처음부터 선명하고 09의 원이 처음부터 열려 있는 상태로 고정됩니다. 그래서 스크롤 타임라인을 쓰는 여섯 개는 @supports not이나 기본값으로 최종 모습을 보여 주고, 스크롤 자체는 어떤 브라우저에서도 막히지 않게 했습니다. 스크롤과 무관하게 글자가 스스로 흘러나오는 쪽은 CSS 타이핑 효과 9종에서 다룹니다. 아홉 폴더를 이렇게 다시 확인한 뒤 압축했고, ry7akxw2을 안내 문장 그대로 입력하면 이 결과물이 통째로 열립니다.

접근성

아홉 개 모두 prefers-reduced-motion: reduce에서 자동으로 움직이는 부분이 꺼집니다. 05의 단어는 처음부터 진한 색으로, 06의 형광펜은 처음부터 다 그어진 상태로, 09의 원은 활짝 열린 상태로 고정되고, 08은 속도 반응 자체를 잠가 카드가 기울지 않게 했습니다. 스크롤 상자 안은 탭 키로 요소를 차례로 방문할 수 있고, 04처럼 화면이 고정되는 구성에서도 건너뛸 수 있는 다음 섹션이 그 아래 그대로 이어져 키보드로 끝까지 읽을 수 있습니다.

FAQ

스크롤 애니메이션 효과를 넣으면 사이트가 느려지나요?

01·04·07·09의 스크롤 타임라인과 02·03의 스티키는 브라우저가 렌더 단계에서 직접 처리해서, 매 프레임 자바스크립트가 계산하는 방식보다 오히려 가볍습니다. 08만 자바스크립트가 매 프레임 값을 갱신하지만 하는 일은 변수 하나에 숫자를 넣는 것뿐이라 부담이 작습니다. 포인터의 움직임을 좌표로 읽는 쪽은 마우스 이펙트 9가지가 담당합니다.

파이어폭스나 사파리에서는 어떻게 보이나요?

01·04·05·06·07·09는 스크롤 타임라인을 쓰지 못하는 브라우저에서 각각 최종 모습으로 고정됩니다. 패널 1이 보이고, 문장은 선명하고, 형광펜은 그어져 있고, 원은 열려 있습니다. 02·03·08은 이 기능과 무관해서 어느 브라우저에서도 같게 동작합니다.

아이폰에서도 되나요?

02·03의 스티키와 08의 속도 반응은 아이폰 사파리에서도 그대로 됩니다. 스크롤 타임라인을 쓰는 나머지는 아이폰 사파리가 아직 지원하지 않아 최종 모습으로 보입니다. 다만 화면 구조 자체는 그대로라 내용을 읽는 데는 지장이 없습니다.

스크롤 하위 효과는 CSS 애니메이션 카테고리에 더 있고, 시차를 주는 패럴랙스 모음과 헤더가 반응하는 스티키 헤더 모음이 이 글과 이어집니다. animation-timeline의 정식 명세는 MDN animation-timeline 문서에서, position: sticky의 동작은 MDN position 문서에서 확인할 수 있습니다.

압축 비밀번호를 입력하세요

비밀번호는 이 글 본문 안에 있습니다. 읽다 보면 나옵니다.