이번 편의 결과물: projects.html을 스크롤하면 카드가 순서대로 나타난다. 지원하지 않는 브라우저에서는 처음부터 모든 카드가 그대로 보인다 · 다루는 개념: animation-timeline의 view() 함수, 타임라인 축과 animation-range, 제한적 지원 상황에서의 @supports 폴백
이 편에서 만드는 파일
portfolio/
└── css/
└── styles.css ~ 카드 등장 애니메이션과 폴백 추가개념 정리
animation-timeline은 애니메이션의 진행 기준을 시간이 아니라 스크롤 위치로 바꾸는 속성입니다. 사용자가 스크롤할 때마다 애니메이션이 그만큼 진행되고, 스크롤을 멈추면 애니메이션도 그 자리에 멈춥니다.
scroll()과 view()의 차이
| 함수 | 기준 | 용도 |
|---|---|---|
scroll() | 스크롤 컨테이너 전체의 스크롤 진행률(0퍼센트–100퍼센트) | 페이지 상단 진행바 등 |
view() | 그 요소가 뷰포트(또는 지정한 컨테이너)에 들어오고 나가는 구간 | 요소별 등장·퇴장 연출 |
카드가 화면에 들어올 때 나타나는 효과에는 view()가 맞습니다.
animation-range로 구간 지정
view()만으로는 요소가 뷰포트에 걸쳐 있는 전체 구간(들어오기 시작 ~ 완전히 나가기)을 타임라인으로 씁니다. 등장 효과만 원하면 animation-range로 “들어오는 구간”만 좁힙니다.
| 키워드 | 의미 |
|---|---|
entry 0% | 요소가 뷰포트에 처음 닿는 지점 |
entry 100% | 요소가 뷰포트에 완전히 들어온 지점 |
cover 0%부터 100%까지 | 요소가 뷰포트를 지나가는 전체 구간 |
exit 0%부터 100%까지 | 요소가 뷰포트를 빠져나가는 구간 |
animation-range: entry 0% cover 30%;요소가 뷰포트에 닿기 시작해서(entry 0%) 전체 구간의 30%만큼 지나갈 때까지(cover 30%) 애니메이션을 끝내겠다는 뜻입니다. 카드가 완전히 화면 가운데 오기 전에 등장 효과가 끝나도록 좁힌 값입니다.
지원 상황과 폴백 전략
2026-09 기준 animation-timeline: view()는 Chrome·Edge·Safari 26 이상에서 동작하지만 Firefox는 아직 플래그 뒤에 있어 Baseline에 도달하지 못했습니다(Interop 2026 과제). 이 실습은 지원 브라우저에서만 애니메이션을 걸고, 미지원 브라우저에서는 처음부터 카드가 다 보이게 만듭니다.
기본 원칙: 카드는 기본 상태로 opacity: 1(항상 보임)로 두고, @supports (animation-timeline: view())를 만족하는 브라우저에서만 초기 상태를 opacity: 0으로 낮춘 뒤 애니메이션으로 다시 1로 올립니다. 이렇게 하면 미지원 브라우저는 애니메이션도 못 보고 카드가 안 보이는 최악의 상황도 피할 수 있습니다.
실습
1. 등장 애니메이션 키프레임 작성
/* portfolio/css/styles.css (@layer components 안) */
@keyframes card-reveal {
from {
opacity: 0;
transform: translateY(24px);
}
to {
opacity: 1;
transform: translateY(0);
}
}2. 지원 브라우저에서만 초기 숨김 + 스크롤 연동 적용
/* portfolio/css/styles.css (@layer components 안) */
.project-card {
opacity: 1; /* 기본값: 항상 보임(폴백) */
}
@supports (animation-timeline: view()) {
.project-card {
opacity: 0;
animation: card-reveal linear both;
animation-timeline: view();
animation-range: entry 0% cover 30%;
}
}@supports 블록 안에서만 opacity: 0으로 낮추므로, 이 속성을 모르는 브라우저는 블록 전체를 무시하고 기본값인 opacity: 1을 그대로 씁니다.
3. 모션 선호 대응 추가
/* portfolio/css/styles.css */
@media (prefers-reduced-motion: reduce) {
.project-card {
animation: none !important;
opacity: 1;
}
}prefers-reduced-motion: reduce인 사용자에게는 스크롤 연동 애니메이션도 꺼서 항상 카드가 바로 보이게 합니다.
4. 실행
npx serve portfolioprojects.html을 열고 페이지 아래로 천천히 스크롤합니다.
5. 확인
확인 1. 지원 브라우저(Chrome·Edge 최신)에서 스크롤하면 카드가 아래에서 위로 살짝 이동하며 나타난다
확인 2. 스크롤을 멈추면 애니메이션도 그 진행 지점에서 멈춘다(시간이 아니라 스크롤 위치가 기준)
확인 3. 개발자 도구에서 CSS.supports("animation-timeline", "view()")를 콘솔에 입력해 지원 여부 확인
확인 4. 미지원 브라우저(또는 Firefox)에서는 스크롤 없이도 카드가 처음부터 전부 보인다
확인 5. 운영체제 설정에서 "동작 줄이기"를 켜면 지원 브라우저에서도 애니메이션 없이 카드가 바로 보인다직접 해보기
animation-range를entry 0% cover 30%에서entry 0% cover 60%로 늘려 등장 속도가 어떻게 느려지는지 비교해 봅니다.- 카드마다 등장 순서가 살짝 어긋나 보이도록
nth-child로animation-delay처럼 보이는 효과를animation-range시작점을 다르게 줘서 만들어 봅니다(시간 기반이 아니므로animation-delay는 스크롤 타임라인에 그대로 쓸 수 없습니다).
정답 보기
/* portfolio/css/styles.css */
.project-card:nth-child(2) {
animation-range: entry 5% cover 35%;
}
.project-card:nth-child(3) {
animation-range: entry 10% cover 40%;
}스크롤 타임라인에서는 animation-delay가 시간 단위라 스크롤 진행과 맞물리지 않습니다. 대신 각 카드의 animation-range 시작 지점을 조금씩 밀어서 순차 등장 느낌을 냅니다.
자주 하는 실수
| 증상 | 원인 | 고치는 법 |
|---|---|---|
| Firefox에서 카드가 계속 안 보임 | @supports 밖에서 opacity: 0을 기본값으로 지정 | 기본값은 opacity: 1로 두고 @supports 안에서만 0으로 낮춤 |
| 스크롤해도 애니메이션이 전혀 안 걸림 | animation-timeline: view()만 있고 animation 속성에 키프레임 이름을 안 넣음 | animation: card-reveal linear both;와 animation-timeline을 함께 지정 |
| 카드가 화면 한가운데를 넘도록 계속 움직임 | animation-range를 지정하지 않아 전체 cover 구간을 다 씀 | animation-range: entry 0% cover 30%처럼 구간을 좁힘 |
| 동작 줄이기 설정을 켜도 애니메이션이 남아 있음 | prefers-reduced-motion 미디어 쿼리를 추가하지 않음 | @media (prefers-reduced-motion: reduce)에서 animation: none 처리 |