Files
2nd/10_Wiki/Topics/Coding/Web_View_Transitions_Cross_Doc.md
T
2026-05-10 22:08:15 +09:00

7.2 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
web-view-transitions-cross-doc View Transitions API — same-doc / cross-doc Coding draft B conceptual 2026-05-09 2026-05-09
web
animation
vibe-coding
language applicable_to
TS / CSS
Frontend
View Transitions
startViewTransition
view-transition-name
cross-doc
MPA transition
navigation API

View Transitions API

SPA 의 fancy transition 가 표준 web. Same-doc (SPA) 과 cross-doc (MPA) 둘 다. Browser 가 magic.

📖 핵심 개념

  • 두 state snapshot.
  • 매칭된 element 가 morph.
  • CSS animation 가 매 transition pair.
  • Native browser support.

💻 코드 패턴

Same-doc (SPA)

async function navigate(newContent) {
  if (!document.startViewTransition) {
    // Fallback
    document.body.innerHTML = newContent;
    return;
  }
  
  const transition = document.startViewTransition(() => {
    document.body.innerHTML = newContent;
  });
  
  await transition.finished;
}

→ Browser 가 자동:

  1. 현재 snapshot.
  2. Callback 실행.
  3. 새 snapshot.
  4. Crossfade (default).

CSS (default)

::view-transition-old(root) {
  animation: 0.3s fade-out;
}
::view-transition-new(root) {
  animation: 0.3s fade-in;
}

root = 페이지 전체.

매칭된 element (morph)

.thumbnail {
  view-transition-name: hero-img;
}

/* Detail page */
.full-image {
  view-transition-name: hero-img;
}

→ Same name = morph (Hero animation).

Custom CSS animation

@keyframes slide-in {
  from { transform: translateX(100%); }
}
@keyframes slide-out {
  to { transform: translateX(-100%); }
}

::view-transition-old(root) {
  animation: 0.3s slide-out;
}
::view-transition-new(root) {
  animation: 0.3s slide-in;
}

Cross-doc (MPA)

<!-- old.html, new.html — 둘 다 -->
<meta name="view-transition" content="same-origin">
@view-transition {
  navigation: auto;
}

→ Anchor click → 새 page 로 transition (MPA 도).

→ 2024 Chrome 126+ 지원.

Astro / Next 통합

---
// Astro view transitions
import { ViewTransitions } from 'astro:transitions';
---
<head>
  <ViewTransitions />
</head>
// Next.js (App Router)
// Layout 에 추가.
<head>
  <meta name="view-transition" content="same-origin" />
</head>

→ MPA 가 SPA 같은 UX.

TanStack Router / React Router

// Manually trigger
const navigate = useNavigate();

const handleClick = async () => {
  if (document.startViewTransition) {
    document.startViewTransition(() => {
      flushSync(() => navigate('/profile'));
    });
  } else {
    navigate('/profile');
  }
};

React 19 + view transitions

import { useViewTransitionState } from 'react-router';

const isTransitioning = useViewTransitionState('/profile');
// → CSS class 추가 가능

Skip transition (specific case)

const transition = document.startViewTransition(() => render(newState));

if (!important) {
  transition.skipTransition();   // 즉시 끝남
}

State-based animation

// List item delete + re-render
async function deleteItem(id) {
  document.startViewTransition(() => {
    items = items.filter(i => i.id !== id);
    rerender();
  });
}

→ List item 가 자연 animate.

Dynamic name

// 매 element 가 unique name
listItems.forEach((item, i) => {
  item.style.viewTransitionName = `item-${item.id}`;
});

함정: 같은 name 두 element

.a, .b { view-transition-name: same; }
/* → conflict, transition 깨짐 */

Old / new pseudo-element

::view-transition          /* root */
::view-transition-image-pair(name)   /* container */
::view-transition-old(name)
::view-transition-new(name)

→ Animatable.

Group, image-pair, old, new

::view-transition-group(name)       — wrapper, position
::view-transition-image-pair(name)  — old + new layered
::view-transition-old(name)         — outgoing
::view-transition-new(name)         — incoming

Nested transitions

document.startViewTransition(async () => {
  await updateA();
  await updateB();
});

→ Promise resolve = transition 끝.

Async render

document.startViewTransition(async () => {
  const data = await fetch('/data').then(r => r.json());
  render(data);
});

→ Network 동안 사용자 가 "frozen" 안 보고 (snapshot 후 wait).

→ 너무 느림 = bad UX.

Reduce motion (a11y)

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none;
  }
}

Browser support

- Chrome 111+ (same-doc)
- Chrome 126+ (cross-doc)
- Edge 동일
- Firefox: 안 (no plan)
- Safari: 진행 중

→ Progressive enhancement. Fallback graceful.

Use case

- Hero animation (list → detail)
- Tab switch (smooth)
- Modal open/close
- Navigation (page change)
- Theme toggle (color smooth)
- Filter / sort animation

Theme toggle

async function toggleTheme() {
  if (!document.startViewTransition) return setTheme();
  
  const transition = document.startViewTransition(setTheme);
  await transition.ready;
  
  document.documentElement.animate(
    { clipPath: ['circle(0% at 0 0)', 'circle(150% at 0 0)'] },
    { duration: 500, pseudoElement: '::view-transition-new(root)' }
  );
}

→ Circle reveal — Twitter style.

Performance

- 매 transition = 2 snapshot (hardware accelerated)
- 큰 page 가 느림 (snapshot cost)
- 매우 많은 element name = 폭발

→ Hero element 만 name. 나머지 = root crossfade.

vs Framer Motion / GSAP

Library:
- Cross-browser
- 정밀 control
- 큰 bundle

Native View Transitions:
- 0 bundle
- Browser-native
- 모든 element 가 native

→ Native 가 default. Library 가 specific 정밀.

Animation API combine

.box {
  view-transition-name: my-box;
}

::view-transition-old(my-box) {
  animation: scale-down 0.3s ease, fade-out 0.3s;
}

→ 다중 animation property.

Debugging

Chrome DevTools:
- Animations panel.
- 매 transition 가 timeline.
- Pause / step.

Production tips

1. Hero element 만 name (모든 element X).
2. Reduce motion 항상 존중.
3. Async render < 500ms (frozen 시간 길면 bad).
4. Fallback 가 있어야 (Firefox).
5. Cross-doc 가 Chrome 126+ 만.

🤔 의사결정 기준

작업 추천
List → detail morph view-transition-name
Page navigation MPA Cross-doc transition
Page navigation SPA startViewTransition
Theme toggle Custom + clip-path
작은 element animate Native CSS animation
Cross-browser Framer Motion
Modal View Transitions
정밀 timeline GSAP

안티패턴

  • 같은 name 여러 element: conflict.
  • Async 가 길음 (>1s): frozen UX.
  • 모든 element 가 name: 성능.
  • Fallback 없음: Firefox 깨짐.
  • Reduce motion 무시: a11y.
  • Cross-doc + 다른 origin: 안 됨.

🤖 LLM 활용 힌트

  • View Transitions = native + CSS-driven.
  • Same-doc (Chrome 111+) / Cross-doc (Chrome 126+).
  • view-transition-name 가 morph.
  • Astro / Next 가 자체 wrapper.

🔗 관련 문서