CSS ‘overscroll-behavior’ 고무밴딩: 페이지를 끌면 뒤에 보이는 색상

발행: (2026년 6월 12일 AM 08:06 GMT+9)
12 분 소요
원문: Dev.to

Source: Dev.to

여러분도 다들 오버스크롤 러버밴딩 효과를 알고 있을 겁니다. 페이지 끝을 넘어 스크롤하거나 위쪽을 끌어당기면 내용이 튕겨 나오는 현상이죠. 모든 최신 브라우저에 존재하지만, 각 브라우저마다 끌어당길 수 있는 거리 감각이 다릅니다. 데스크톱 Chrome은 의외로 긴 끌어당김을 허용합니다 — 저는 페이지를 창 높이의 약 70 % 정도 끌어당길 수 있었습니다. Firefox는 거의 움직이지 않으며, 약 10 % 정도만 가능합니다. macOS와 모바일 Safari는 중간 정도인 40–50 % 정도에서 머뭅니다.

개인적으로 이 효과는 꽤 즐겁습니다 — 사용자 경험에 멋진 터치를 더해 주니까요. 하지만 큰 단점이 하나 있습니다: 오버스크롤 영역 뒤쪽을 채우는 CSS 속성이나 API가 존재하지 않는다는 점입니다. body에 배경색을 지정할 수는 있지만, 이는 헤더와 푸터가 같은 단색 배경을 공유할 때만 유효합니다. 솔직히 대부분의 웹사이트는 그렇지 않죠. 일부 사이트는 overscroll-behavior: none을 설정하고 이 효과를 포기합니다 — 멋진 효과를 잃는 것이 안타깝습니다.

간단한 랜딩 페이지처럼 별다른 끌어당김 동작(서버 호출, 추가 콘텐츠 로드 등)이 필요 없는 경우, JS로 커스텀 풀 효과를 구현하는 것도 좋은 선택은 아닙니다.

덧붙여서, Declarative Overscroll Actions라는 새로운 제안이 초기에 논의되고 있습니다. 아직 오버스크롤 영역을 색칠할 쉬운 방법을 제공할지는 미정이지만, 앞으로 가능성이 있을 것이라 기대됩니다.

그래서 저는 헤더에서 푸터까지 밝은 색에서 어두운 색으로 변하는 그라디언트를 가진 꽤 단순한 랜딩 페이지와 씨름하게 되었습니다. 문제를 해결하려다가 여러 ‘이상한’ 상황들을 마주했으며, 여기서는 그 과정을 정리해 공유하고자 합니다.

데모는 여기서 확인할 수 있습니다.

중요한 CSS 파일은 여기에 있습니다: https://github.com/a-dev/probes/blob/main/src/pages/overscroll/overscroll.css.


그라디언트 배경

첫 번째 시도는 body에 배경을 지정하는 것이었습니다. 단색이 아니라 그라디언트를 사용했죠. 여기서 문제는 단색혹은에 적용하면 ‘튕겨 나오는’ 영역을 채우지만, linear-gradient, radial-gradient, conic-gradient 등은 채우지 못한다는 점입니다.

하지만 html::before 혹은 body::before 같은 가상 요소를 만들면 됩니다. 모든 종류의 그라디언트가 작동합니다! 단, 한 가지 귀찮은 점이 있는데, position: fixed 로 지정해야 합니다. position: absolute 로 하면 요소가 body에 상대적으로 배치돼 페이지를 끌어당길 때 내용과 함께 움직이기 때문이죠.

고정 위치(fixed)는 문제를 해결하지만 또 다른 문제를 만들어요: 요소가 스크롤을 전혀 인식하지 못합니다. 따라서 위쪽이 밝고 아래쪽이 어두운 그라디언트를 사용한다면, 오버스크롤 영역까지 그라디언트를 이어줄 수 없습니다. 최선은 가장 바깥쪽 색을 고정 색으로 확장하는 것이죠 — 시작은 밝게, 끝은 어둡게. 대충은 괜찮아 보입니다.

여기서 한 가지 더 짚고 넘어가야 할 상황이 있습니다: 가로 방향 오버스크롤도 러버밴딩이 발생합니다. 그리고 종종 세로보다 더 눈에 거슬리죠 — 디자인에 색상이 다른 섹션이나 풀블리드 사진 등이 많기 때문입니다. 완벽한 세상이라면 이런 영역을 주변 조명 효과 등으로 채우고 싶지만, 실제로는 맞지 않을 수도 있습니다. 솔직히 저는 가로 오버스크롤을 끄는 편이 좋습니다. overscroll-behavior-x: nonebody/html에 한 줄만 적으면 되며, 대부분의 웹사이트에 큰 손실이 없습니다. 게다가 iOS Safari는 기본적으로 이렇게 동작합니다.

또 한 가지 강조하고 싶은 점은 원뿔형(conic)이나 방사형(radial) 그라디언트는 오버스크롤 영역에 거의 의미가 없다는 것입니다. 가상 요소가 고정돼 스크롤에 전혀 반응하지 않기 때문이죠.

아래는 흰색 → 검은색 그라디언트 예시 코드입니다:

body::before {
  pointer-events: none;
  content: "";

  position: fixed;
  z-index: -1;
  inset: 0;

  width: 100%;
  height: 100%;

  background-image: linear-gradient(white 0%, white 50%, black 50%, black 100%);
  background-repeat: no-repeat;
}

핵심 포인트: 그라디언트를 50 % 지점에서 나눠준 이유는 단순히 재미를 위해서가 아니라, 사용자가 페이지를 끌어당길 수 있기 때문입니다! 여기서 첫 번째 장애물이 등장합니다. Chrome에서 충분히 끌어당기면 페이지가 창 높이의 70 % 정도 이동합니다. 양쪽 모두에서! 따라서 이 접근법은 큰 BUT와 함께 작동합니다.


고정 요소

다음으로 시도한 것은 페이지 경계 안에 존재하면서 빈 공간을 메우는 고정 요소였습니다. 헤더와 푸터에 붙여 보았습니다. 실제로는 그라디언트 배경과 같은 벽에 부딪혔습니다: 절대 위치(absolute)는 사용할 수 없고(내용과 함께 움직이므로), 고정(fixed)이어야 하지만 고정 요소는 스크롤을 무시합니다. 그래서 두 개의 절반으로 나누어야 했고, 사용자가 페이지를 70 % 끌어당기면… 여러분도 알다시피 상황이 복잡해집니다.

.header::after,
.footer::after {
  content: "";
  position: fixed;
  z-index: -1;
  left: 0;
  width: 100dvw;
  height: 50dvh;
}

header::after {
  top: 0;
  background-color: var(--color-bg-light);
}

footer::after {
  bottom: 0;
  background-color: var(--color-bg-dark);
}

애니메이션

여기서 스크롤 연동 애니메이션이라는 새로운 API를 떠올렸습니다. 아직 초기 단계이지만 Chrome과 Safari에서는 이미 지원되고 있습니다(Firefox는 곧 지원 예정). 아이디어는 고정 요소를 스크롤에 맞춰 움직이게 하는 것입니다:

/* 이전 코드에 추가 */
header::after,
footer::after {
  transform-origin: top;
  height: 100dvh;
  animation: scale-part;
  animation-timeline: scroll();
}

footer::after {
  animation-direction: alternate-reverse;
}

@keyframes scale-part {
  from {
    transform: scaleY(1);
  }
  to {
    transform: scaleY(0);
  }
}

스크롤이 페이지 끝에 도달하면 애니메이션이 고정된 푸터 블록을 100dvh까지 확장합니다 — 따라서 70 % 정도 끌어당겨도 환상이 깨지지 않죠. 헤더도 반대로 동작합니다. 멋지지만, Safari에서는 작동하지 않습니다. Chrome에서만 어느 정도 동작하는 수준이죠.


Safari?

Safari와의 싸움에서 나는 애니메이션 방식을 바꾸었습니다. 고정 요소를 스케일링하는 대신, body 배경색 자체를 애니메이션해 보기로 했습니다. 사용자는 이 레이어를 전혀 보지 못하니, 내용 뒤에 숨겨두고 가장 바깥쪽 위치에서만 살짝 보이게 하면 됩니다. CSS는 아주 간단합니다:

html {
  animation: scroll-background;
  animation-timeline: scroll();
}

@keyframes scroll-background {
  from {
    background-color: white;
  }
  to {
    background-color: black;
  }
}

Safari에서도, Chrome에서도, Firefox에서도 동작합니다!


범용 솔루션?

다양한 시도를 해본 뒤, Firefox는 포기하기로 했습니다. 두 가지 이유가 있습니다:

  1. 끌어당길 수 있는 거리가 짧다.
  2. 사용자 비중이 작다 (죄송합니다, Firefox— 여전히 사랑하고 곧 스크롤 애니메이션을 지원해 주길 바랍니다).

Firefox에서는 최소한 단색 배경을 유지하는 것이 좋습니다. 디자인과 맞추기 위해 사이트 메인 배경색을 사용하면 됩니다.

최종 솔루션은 다음과 같습니다:

html {
  animation: scroll-background;
  animation-timing
0 조회
Back to Blog

관련 글

더 보기 »

Eidentic 소개

Today we're releasing Eidentic, an open-source TypeScript SDK for building AI agents with self-improving memory and the production fundamentals built in — not b...

Typescript의 타입

Introdução Tipos são uma forma de definir a “forma” ou o contrato dos dados que estamos usando no código. Pensando em Javascript puro, ele é dinâmico: você pode...