\n\n\ncss\n.modal {\n view-transition-name: app-modal;\n}\n@keyframes modal-out {\n to { transform: scale(0."}},{"@type":"Question","name":"どのような場面で役立ちますか?","acceptedAnswer":{"@type":"Answer","text":"ネイティブアニメーション、ネイティブアクセシビリティ(<dialog> 経由)、startViewTransition 1回呼び出しを超えるオーバーヘッドゼロを求める場面で、カスタムモーダルライブラリ(React Modal、Reach Dialog、Headless UI Dialog)を置き換える用途。"}}]}
View Transitions によるモーダル開閉
壊れやすい portal ベースのアニメーションライブラリをネイティブ View Transitions に置き換え、DOM 順序とアクセシビリティを尊重したスムーズな開閉を実現。
UI Patterns
詳細な説明
3フェーズのモーダルライフサイクル
洗練されたモーダルアニメーションには (1) バックドロップのフェードイン、(2) ダイアログのスケール+フェードイン、(3) ページ全体の控えめな後退、の協調が必要です。View Transitions API は (1) と (3) を無料でやってくれます。あなたが書くのは (2) だけ。
<dialog id="dlg" class="modal">…</dialog>
<button onclick="open()">Open</button>
<script>
function open() {
document.startViewTransition(() => {
document.getElementById('dlg').showModal();
});
}
</script>
.modal {
view-transition-name: app-modal;
}
@keyframes modal-out {
to { transform: scale(0.96) translateY(8px); opacity: 0; }
}
@keyframes modal-in {
from { transform: scale(0.96) translateY(8px); opacity: 0; }
}
::view-transition-old(app-modal) {
animation: 180ms ease-in both modal-out;
}
::view-transition-new(app-modal) {
animation: 240ms cubic-bezier(0.16, 1, 0.3, 1) both modal-in;
}
<dialog> + View Transitions が勝つ理由
ネイティブ <dialog> 要素はフォーカストラップ・Esc で閉じる・スクロールロックを自動処理します。View Transitions は視覚的振り付けを担当します。この組み合わせは、ほとんどのユースケースで React Modal/Headless UI Overlay スタック全体を、依存関係ではなく約12行のコードで置き換えます。
クロージングアニメーション
閉じる際も同じラッパーを呼び出します:
function close() {
document.startViewTransition(() => {
document.getElementById('dlg').close();
});
}
ダイアログがスナップショットから消えるため、::view-transition-old(app-modal) ルールが自動発火します。
アクセシビリティ:prefers-reduced-motion
カスタム keyframes は必ずメディアクエリでラップし、reduced motion を要求するユーザーには即時切替(アニメーションなし)を提供します:
@media (prefers-reduced-motion: reduce) {
::view-transition-old(app-modal),
::view-transition-new(app-modal) {
animation-duration: 0ms !important;
}
}
ユースケース
ネイティブアニメーション、ネイティブアクセシビリティ(<dialog> 経由)、startViewTransition 1回呼び出しを超えるオーバーヘッドゼロを求める場面で、カスタムモーダルライブラリ(React Modal、Reach Dialog、Headless UI Dialog)を置き換える用途。