페이지 전환 애니메이션을 구현할 때마다 Framer Motion을 쓰거나 CSS transition을 복잡하게 엮었다. 그 수고를 브라우저가 직접 해주는 API가 생겼다. View Transitions API다.
Chrome에 먼저 들어왔고, 지금은 Safari, Firefox도 지원한다. 모바일 Safari는 18버전부터 기본 지원이니까 이제 실제 프로젝트에 쓸 수 있는 타이밍이 됐다.
기본 사용법
document.startViewTransition(() => {
updateTheDOMSomehow();
});
startViewTransition에 DOM 변경 로직을 넘기면, 브라우저가 전환 전/후 스냅샷을 찍고 크로스페이드 애니메이션을 붙여준다. 기본은 크로스페이드지만 CSS로 커스터마이징할 수 있다.
CSS로 애니메이션 제어하기
전환 중에 브라우저가 ::view-transition-old, ::view-transition-new 슈도 엘리먼트를 만든다.
@keyframes slide-in {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
@keyframes slide-out {
from {
transform: translateX(0);
}
to {
transform: translateX(-100%);
}
}
::view-transition-old(root) {
animation: 300ms ease-out slide-out;
}
::view-transition-new(root) {
animation: 300ms ease-out slide-in;
}
특정 요소만 전환 지정하기
페이지 전체가 아니라 특정 요소만 부드럽게 전환하고 싶을 때 view-transition-name을 쓴다.
.hero-image {
view-transition-name: hero;
}
같은 이름을 전환 전/후 두 DOM에 모두 붙여두면, 브라우저가 위치와 크기를 보간해서 FLIP 애니메이션처럼 동작한다. 카드 목록 → 상세 페이지 전환에 쓰면 요소가 자연스럽게 이어지는 느낌을 준다.
이름은 페이지 내에서 유일해야 한다. 중복되면 전환이 동작하지 않는다. 동적으로 여러 개를 쓸 때는 style 속성에 인라인으로 넣는 방법을 쓴다.
Next.js에서 쓰기
App Router 기준으로 useRouter의 push를 래핑하면 된다.
import { useRouter } from 'next/navigation';
export function useViewTransitionRouter() {
const router = useRouter();
function push(href: string) {
if (!document.startViewTransition) {
router.push(href);
return;
}
document.startViewTransition(() => {
router.push(href);
});
}
return { push };
}
document.startViewTransition이 없는 브라우저를 위한 폴백을 넣어두는 게 좋다. 있으면 쓰고 없으면 그냥 이동하는 패턴이라 progressive enhancement로 깔끔하게 쓸 수 있다.
브라우저 지원
Chrome, Edge는 111부터, Safari는 18.2부터 지원한다. Firefox는 아직 플래그 뒤에 있다. 지원하지 않는 브라우저에서는 전환 효과 없이 그냥 동작하면 되니까, 체크만 잘 넣으면 점진적으로 개선되는 UX를 거의 공짜로 얻는 셈이다.
prefers-reduced-motion 대응
접근성 설정에서 애니메이션을 끈 사용자에게 전환 효과가 불편할 수 있다.
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-image-pair(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
라이브러리 없이 브라우저 기본 기능으로 이 정도 품질의 전환 애니메이션을 만들 수 있게 됐다. 특히 view-transition-name으로 공유 요소 전환을 구현하면, 예전엔 꽤 복잡한 작업이었던 게 CSS 한 줄로 끝난다. Framer Motion처럼 정교한 물리 기반 애니메이션은 아니지만, 화면 전환이나 요소 이동 같은 기본적인 UX 개선에는 충분하다.