View Transitions APIが一段使いやすくなった。画面遷移の演出をtypesで切り替える実践パターン

はじめに

ページ遷移やUI切り替えに少し動きを足したいけれど、場面ごとにアニメーションを分岐させるのが面倒、ということはよくあります。前へ進むときと戻るときで動きを変えたい、モーダルを開くときだけ軽く拡大したい、といった要望は定番です。

View Transitions APIは以前から便利でしたが、2026年1月にMDNでViewTransition.typesがBaseline 2026として整理され、遷移の種類を扱う書き味がかなり分かりやすくなりました。document.startViewTransition()でDOM更新を包みつつ、typesで「この遷移はforward」「これはmodal」と名前を付けてCSS側で演出を切り替えられます。

この記事では、同一ページ内のUI切り替えを例に、まずは基本の使い方を押さえたうえで、実務でよくある「進む・戻る」とprefers-reduced-motionを考慮した実装までつなげます。

機能やライブラリの概要

View Transitions APIは、DOM更新の前後をブラウザにキャプチャさせて、その差分を自然につなぐためのWeb標準APIです。単純なクロスフェードだけでも便利ですが、最近は「どの遷移なのか」をtypesで表現できるようになり、同じ更新処理でも文脈に応じてアニメーションを変えやすくなりました。

MDNのViewTransition関連ドキュメントでは、ViewTransition自体はBaseline 2025、typesプロパティはBaseline 2026として案内されています。つまり、最新環境では「遷移オブジェクトを受け取り、あとから種類を足す」という使い方がかなり現実的になってきた、という見方ができます。

実務で覚えておくと便利なのは次の3点です。

  • document.startViewTransition()でDOM更新をひとまとまりにする
  • types配列、またはvt.types.add()で遷移に名前を付ける
  • :active-view-transition-type()でCSS側の演出を出し分ける

これが揃うと、「処理は同じだが見せ方だけ変えたい」場面に強くなります。

基本の使い方

最初はタブ風のUIで試すのが分かりやすいです。このコードでは、パネルの中身を切り替えるときにView Transitionを開始し、detailsという種類名を付けています。

HTML
<div class="panel-switcher">
  <div class="actions">
    <button type="button" data-view="overview">概要</button>
    <button type="button" data-view="details">詳細</button>
  </div>

  <section class="panel" data-panel>
    <h2>概要</h2>
    <p>この製品の特徴を短くまとめています。</p>
  </section>
</div>
JavaScript
const panel = document.querySelector("[data-panel]");
const buttons = document.querySelectorAll("[data-view]");

const views = {
  overview: {
    title: "概要",
    body: "この製品の特徴を短くまとめています。",
  },
  details: {
    title: "詳細",
    body: "導入条件やサポート範囲、運用時の注意点まで確認できます。",
  },
};

for (const button of buttons) {
  button.addEventListener("click", () => {
    const nextView = views[button.dataset.view];

    if (!document.startViewTransition) {
      renderPanel(nextView);
      return;
    }

    document.startViewTransition({
      update() {
        renderPanel(nextView);
      },
      types: ["details"],
    });
  });
}

function renderPanel(view) {
  panel.innerHTML = `
    <h2>${view.title}</h2>
    <p>${view.body}</p>
  `;
}
CSS
.panel {
  view-transition-name: panel;
}

html:active-view-transition-type(details)::view-transition-old(panel) {
  animation: panel-fade-out 180ms ease both;
}

html:active-view-transition-type(details)::view-transition-new(panel) {
  animation: panel-rise-in 220ms ease both;
}

@keyframes panel-fade-out {
  to {
    opacity: 0;
    transform: translateY(-8px);
  }
}

@keyframes panel-rise-in {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
}

このコードでやっていることは単純で、DOM更新をupdate()にまとめ、typesで遷移の名前を付けているだけです。CSS側ではその名前に反応して、入れ替え方だけを差し替えています。

従来ならクラスの付け替えやタイミング制御をJavaScript側に寄せがちでしたが、View Transitionを使うと「更新処理」と「見せ方」を分けやすくなります。

便利な使いどころ

この機能が効きやすいのは、画面の切り替え自体は単純なのに、見せ方だけ場面ごとに変えたいUIです。

  • 商品ギャラリーの「次へ」「前へ」
  • フィルター切り替えで一覧を更新するUI
  • モーダル、ドロワー、詳細パネルの開閉
  • SPAのページ切り替えや小さなルーター実装

特に便利なのは、「forward」「backward」「modal」くらいの少ない語彙を先に決めておけることです。デザイナーと相談するときも、「戻る遷移は右から左ではなく、左から右に戻す」といった話をコードに落としやすくなります。

また、同じ描画関数を使い回しながら演出だけ変えられるので、状態管理のコードを必要以上に複雑にしなくて済みます。アニメーション用のif分岐が描画ロジックに混ざりにくいのは、実務ではかなり大きいです。

応用コード

次は、前後移動で演出を変える例です。このコードでは、押したボタンに応じてforwardbackwardを付け、同じ画像切り替えでもアニメーションの方向を切り替えています。

HTML
<section class="gallery">
  <div class="gallery-card" data-card>
    <img
      src="/images/feature-01.jpg"
      alt="展示会ブースのメインビジュアル"
      width="960"
      height="540"
    />
    <p data-caption>展示会ブースのメインビジュアル</p>
  </div>

  <div class="gallery-nav">
    <button type="button" data-dir="backward">前へ</button>
    <button type="button" data-dir="forward">次へ</button>
  </div>
</section>
JavaScript
const items = [
  {
    src: "/images/feature-01.jpg",
    alt: "展示会ブースのメインビジュアル",
  },
  {
    src: "/images/feature-02.jpg",
    alt: "ライト演出を使ったキービジュアル",
  },
  {
    src: "/images/feature-03.jpg",
    alt: "案内サインを含めた全体レイアウト",
  },
];

const card = document.querySelector("[data-card]");
const caption = document.querySelector("[data-caption]");
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
let currentIndex = 0;

document.querySelectorAll("[data-dir]").forEach((button) => {
  button.addEventListener("click", () => {
    const direction = button.dataset.dir;
    const delta = direction === "forward" ? 1 : -1;
    const nextIndex = (currentIndex + delta + items.length) % items.length;
    const nextItem = items[nextIndex];

    const update = () => {
      const image = card.querySelector("img");
      image.src = nextItem.src;
      image.alt = nextItem.alt;
      caption.textContent = nextItem.alt;
      currentIndex = nextIndex;
    };

    if (!document.startViewTransition || reduceMotion.matches) {
      update();
      return;
    }

    const transition = document.startViewTransition({ update });
    transition.types.add(direction);
  });
});
CSS
.gallery-card img,
.gallery-card p {
  view-transition-name: gallery-content;
}

html:active-view-transition-type(forward)::view-transition-old(gallery-content) {
  animation: slide-out-left 240ms ease both;
}

html:active-view-transition-type(forward)::view-transition-new(gallery-content) {
  animation: slide-in-right 240ms ease both;
}

html:active-view-transition-type(backward)::view-transition-old(gallery-content) {
  animation: slide-out-right 240ms ease both;
}

html:active-view-transition-type(backward)::view-transition-new(gallery-content) {
  animation: slide-in-left 240ms ease both;
}

@keyframes slide-out-left {
  to {
    opacity: 0;
    transform: translateX(-24px);
  }
}

@keyframes slide-in-right {
  from {
    opacity: 0;
    transform: translateX(24px);
  }
}

@keyframes slide-out-right {
  to {
    opacity: 0;
    transform: translateX(24px);
  }
}

@keyframes slide-in-left {
  from {
    opacity: 0;
    transform: translateX(-24px);
  }
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*) {
    animation-duration: 1ms;
  }
}

ここでは、更新処理そのものは1つにまとめつつ、transition.types.add(direction)だけで演出を分岐しています。こうしておくと、将来的にzoommodalを足したくなっても、CSSの責務として追加しやすいです。

実務では、商品カルーセル、実績ギャラリー、ケーススタディの前後ページなどでかなり使いやすい形です。特にprefers-reduced-motionを先に見ておけば、演出を入れつつ配慮も崩しにくくなります。

注意点

  • view-transition-nameを付けすぎない
    画面全体に広く付けると、想定外の要素までキャプチャされて管理しづらくなります。最初はカードや画像など、動かしたい要素だけに絞るほうが安全です。
  • typesは少数の語彙に整理する
    何でもかんでも別名にすると、CSSの分岐が増えて保守しづらくなります。forwardbackwardmodalくらいから始めるのが現実的です。
  • 未対応環境のフォールバックを先に決める
    document.startViewTransitionがない環境では、そのまま通常更新に落とすだけでも十分です。アニメーションがないと破綻するUIにはしないほうが安定します。
  • MPAでの高度な使い分けは別途検証する
    MDNではpageswappagerevealを使ったクロスドキュメント遷移の例も案内されていますが、こちらは周辺APIを含めて段階的に試すのがよさそうです。まずは同一ページ内の切り替えで手応えをつかむのがおすすめです。

まとめ

View Transitions APIのよさは、派手なデモよりも「同じ更新処理に、場面ごとの意味を持たせられる」ところにあります。typesが入ることで、前後移動やモーダル表示のような定番パターンを、かなり自然に設計できるようになりました。

まずはギャラリーやタブ、詳細パネルのような小さいUIから試すと導入しやすいです。アニメーションの実装そのものより、更新ロジックと見せ方をきれいに分けられる感触が、現場では効いてきます。

ポイント

  • typesを使うと、同じDOM更新でもforwardbackwardなど意味ごとに演出を切り替えやすい
  • 画像ギャラリーや詳細パネルのような小さなUIから入れると導入しやすい
  • prefers-reduced-motionと通常更新へのフォールバックを最初からセットで考えると安全

参考リンク

read next