ビュートランジション
Astroでは、オプトイン式ページ単位のビュートランジションを数行のコードで実現できます。ビュートランジションは、ブラウザがナビゲーション時に通常おこなうページ全体の更新なしでページコンテンツを更新し、ページ間のシームレスなアニメーションを提供します。
Astroは、単一のページの<head>
に追加可能な<ClientRouter />
ルーティングコンポーネントを提供しており、これにより別ページへの遷移を制御できます。このコンポーネントは軽量なクライアントサイドルーターを提供し、ナビゲーションをインターセプトしてページ間の遷移をカスタマイズできるようにします。
このコンポーネントを共通のヘッドやレイアウトなど再利用可能な.astro
コンポーネントに追加すると、サイト全体でのアニメーション付きのページ遷移(SPAモード)が可能になります。
Astroのビュートランジションは新しいView TransitionsブラウザAPIによって提供されており、また以下の機能も含みます。
fade
やslide
、none
など、いくつかの組み込みのアニメーションオプション。- 戻る・進むの両ナビゲーションでのアニメーションのサポート。
- トランジションアニメーションのすべての要素を完全にカスタマイズし、さらに独自のアニメーションを作成する機能。
- 非ページへのリンクに対しクライアントサイドナビゲーションを防止するオプション。
- まだView Transition APIをサポートしていないブラウザのためのフォールバック用動作の制御。
prefers-reduced-motion
の自動サポート。
デフォルトでは、すべてのページは通常のフルページのブラウザナビゲーションを使用します。ビュートランジションを有効化すると、ページ単位またはサイト全体で使用できます。
ページにビュートランジションを追加する
セクションタイトル: ページにビュートランジションを追加する<ClientRouter />
ルーティングコンポーネントをインポートし、<head>
内に追加することで、ビュートランジションを個別のページで有効化できます。
---import { ClientRouter } from 'astro:transitions';---<html lang="ja"> <head> <title>私のホームページ</title> <ClientRouter /> </head> <body> <h1>私のウェブサイトへようこそ!</h1> </body></html>
サイト全体でのビュートランジション(SPAモード)
セクションタイトル: サイト全体でのビュートランジション(SPAモード)<ClientRouter />
をインポートし、共通の<head>
または共有のレイアウトコンポーネントに追加します。Astroは、旧ページと新ページの類似点に基づいてデフォルトのページアニメーションを作成し、サポートされていないブラウザに対するフォールバック動作も提供します。
以下の例では、このコンポーネントをインポートし<CommonHead />
Astroコンポーネントに追加することで、Astroのデフォルトのページナビゲーションアニメーションをサイト全体に追加しています。これにより、サポートされていないブラウザに対するフォールバック動作も追加されます。
---import { ClientRouter } from 'astro:transitions';---<link rel="icon" type="image/svg+xml" href="/favicon.svg" /><meta name="generator" content={Astro.generator} />
<!-- 主要メタタグ --><title>{title}</title><meta name="title" content={title} /><meta name="description" content={description} />
<ClientRouter />
Astroのデフォルトのクライアントサイドナビゲーションを有効にするために、他に必要な設定はありません!
より細かな制御のためには、トランジションディレクティブを使用するか、個々の要素でデフォルトのクライアントサイドナビゲーションをオーバーライドします。
トランジションディレクティブ
セクションタイトル: トランジションディレクティブAstroは、旧ページと新ページの両方に存在する対応する要素に、共通かつ一意のview-transition-name
を自動的に割り当てます。マッチする要素のペアは、要素の種類とDOM内の位置の両方によって推測されます。
.astro
コンポーネント内のページ要素にオプションのtransition:*
ディレクティブを使用すると、ナビゲーション中のページ遷移の動作をより細かく制御できます。
transition:name
: 新旧コンテンツのアニメーションに関するAstroデフォルトの要素のマッチング方式をオーバーライドし、DOM要素のペアを関連付けるトランジション名を指定できます。transition:animate
: アニメーションの種類を指定することで、旧要素を新要素で置き換える際のAstroのデフォルトのアニメーションをオーバーライドできます。Astroの組み込みのアニメーションディレクティブを使用するか、独自のトランジションアニメーションを作成します。transition:persist
: 旧要素を新要素で置き換えるAstroのデフォルトの挙動をオーバーライドし、別のページに移動した際にコンポーネントとHTML要素を保持します。
トランジションに名前を付ける
セクションタイトル: トランジションに名前を付ける場合によっては、対応するビュートランジション要素を自分で特定したい、または特定する必要があるかもしれません。transition:name
ディレクティブを使用して、要素のペアに名前を付けることができます。
<aside transition:name="hero">
<aside transition:name="hero">
transition:name
に使った値は、各ページにつき1回しか使用できないことに注意してください。Astroが適切な名前を自動的に推測できない場合や、要素のマッチングをより細かく制御したい場合に、手動で設定してください。
状態を保持する
セクションタイトル: 状態を保持するastro@2.10.0
transition:persist
ディレクティブを使用すると、ページ間のナビゲーションでコンポーネントとHTML要素を(置き換えるのではなく)保持できます。
たとえば以下の<video>
は、同じvideo要素を含む別のページに移動しても再生され続けます。これは、進む・戻るの両方のナビゲーションで機能します。
<video controls="" autoplay="" transition:persist> <source src="https://ia804502.us.archive.org/33/items/GoldenGa1939_3/GoldenGa1939_3_512kb.mp4" type="video/mp4"></video>
Astroアイランド(client:
ディレクティブをもつUIフレームワークコンポーネント)にもディレクティブを配置できます。そのコンポーネントが次のページに存在する場合、旧ページのアイランドは現在の状態のまま表示され、新しいページのアイランドに置き換えられることはありません。
以下の例では、transition:persist
属性をもつ<Counter />
コンポーネントを含むページ間を行き来しても、count
はリセットされません。
<Counter client:load transition:persist count={5} />
また、アイランドあるいは要素が2つのページ間で異なるコンポーネントの中にある場合は、対応する要素を手動で特定できます。
<Video controls="" autoplay="" transition:name="media-player" transition:persist />
<MyVideo controls="" autoplay="" transition:name="media-player" transition:persist />
便利な短縮記法として、transition:persist
の値にトランジション名を指定できます。
<video controls="" autoplay="" transition:persist="media-player">
transition:persist-props
セクションタイトル: transition:persist-propsastro@4.5.0
transition:persist-props
ディレクティブを使うと、アイランドのprops
をページ遷移時に保持するかどうかを制御できます。
デフォルトでは、アイランドにtransition:persist
を追加すると状態は保持されますが、コンポーネントは新しいprops
で再レンダリングされます。たとえば、ページごとに異なるtitle
などをprops
として受け取る場合に便利です。
transition:persist-props
をtransition:persist
と併用すると、この動作は上書きされます。アイランドのprops
は再レンダリングされず、現在の状態が維持されます。
組み込みのアニメーションディレクティブ
セクションタイトル: 組み込みのアニメーションディレクティブAstroには、デフォルトのfade
トランジションをオーバーライドするための組み込みのアニメーションがいくつか用意されています。transition:animate
ディレクティブを各要素に追加して、特定のトランジションの動作をカスタマイズできます。
fade
(デフォルト): 独自のクロスフェードアニメーションです。旧コンテンツがフェードアウトし、新コンテンツがフェードインします。initial
: Astroの独自のクロスフェードアニメーションを無効にし、ブラウザのデフォルトのスタイルを使用します。slide
: 旧コンテンツが左にスライドアウトし、新コンテンツが右からスライドインするアニメーションです。戻る場合のナビゲーションでは、アニメーションは逆になります。none
: ブラウザのデフォルトのアニメーションを無効にします。ページの<html>
要素で使用すると、ページ内のすべての要素のデフォルトのフェードを無効にします。
ページのアニメーションを完全に制御するには、ディレクティブを組み合わせます。<html>
要素でページのデフォルト値を設定し、必要に応じて個々の要素でオーバーライドしてください。
以下の例では、ボディコンテンツにスライドアニメーションを設定し、ページの残りの部分に対してはブラウザのデフォルトのフェードアニメーションを無効にしています。
---import { CommonHead } from '../components/CommonHead.astro';---<html transition:animate="none"> <head> <CommonHead /> </head> <body> <header> ... </header> <!-- 個別の要素でページデフォルトをオーバーライドします --> <main transition:animate="slide"> ... </main> </body></html>
アニメーションをカスタマイズする
セクションタイトル: アニメーションをカスタマイズする任意のCSSアニメーションプロパティを使用して、トランジションのすべての要素をカスタマイズできます。
組み込みのアニメーションをカスタマイズするには、まずastro:transitions
からアニメーションをインポートし、続いてカスタマイズ用オプションを渡します。
以下の例では、組み込みのfade
アニメーションの継続時間(duration)をカスタマイズしています。
---import { fade } from 'astro:transitions';---
<header transition:animate={fade({ duration: '0.4s' })}>
また、transition:animate
で独自のアニメーションも定義できます。進む・戻るの挙動と新旧のページについて、以下の型に従って定義します。
export interface TransitionAnimation { name: string; // キーフレームの名前 delay?: number | string; duration?: number | string; easing?: string; fillMode?: string; direction?: string;}
export interface TransitionAnimationPair { old: TransitionAnimation | TransitionAnimation[]; new: TransitionAnimation | TransitionAnimation[];}
export interface TransitionDirectionalAnimations { forwards: TransitionAnimationPair; backwards: TransitionAnimationPair;}
カスタムアニメーションbump
をルートレイアウトファイルの<style is:global>
タグ内で定義するために必要な全てのプロパティは以下の通りです。
---import { ClientRouter } from "astro:transitions";---<html lang="ja"> <head> <ClientRouter /> </head> <body> <slot /> </body></html>
<style is:global> @keyframes bump { 0% { opacity: 0; transform: scale(1) translateX(200px); } 50% { opacity: 0.5; transform: scale(1.1); } 100% { opacity: 1; transform: scale(1) translateX(0); } }</style>
アニメーションの挙動は、アニメーションを使用する各コンポーネントのフロントマターで定義する必要があります。
---const anim = { old: { name: "bump", duration: "0.5s", easing: "ease-in", direction: "reverse", }, new: { name: "bump", duration: "0.5s", easing: "ease-in-out", },};
const customTransition = { forwards: anim, backwards: anim,};---<header transition:animate={customTransition}> ... </header>
カスタムアニメーションを定義はとても柔軟に行えます。目的の動作を実現するために、進む・戻るで異なるオブジェクトを使ったり、old/newで別々のキーフレームアニメーションを指定したりといった工夫もできます。
ルーター制御
セクションタイトル: ルーター制御<ClientRouter />
ルーターは、以下のイベントでナビゲーションを制御します。
<a>
要素のクリック- 戻る・進むのナビゲーションイベント
さらに、以下の属性や関数で挙動を細かく制御できます。
data-astro-reload
:<a>
や<form>
タグに付与すると、クライアントサイドナビゲーションを強制的に無効化し、フルページ遷移にします。data-astro-history="auto | push | replace"
:<a>
タグに付与すると、ブラウザの履歴の扱いを制御できます。navigate(href, options)
: クライアントスクリプトやフレームワークコンポーネントからナビゲーションをトリガーできます。
クライアントサイドナビゲーションの防止
セクションタイトル: クライアントサイドナビゲーションの防止<ClientRouter />
コンポーネントを追加してページでクライアントサイドルーティングを有効にすると、サイト内の別のページにリンクするページ内のすべてのアンカーは、クライアントサイドルーティングによってナビゲートされます。ただ、クライアントサイドルーティングでナビゲートしたくないような場合もあります。たとえば、public/
フォルダ内のPDFや、画像を生成するAPIルートなどの非ページへのリンクです。
こうした場合、data-astro-reload
属性を使用して、リンクごとにクライアントサイドルーティングをオプトアウトできます。
以下の例は、ホームページから記事に移動する際にクライアントサイドルーティングを防止する方法です。同様に、記事一覧ページから同じページに移動する場合のように、ヒーロー画像などの共有要素にアニメーションを適用することも可能です。
<a href="/quarterly-earnings.pdf" data-astro-reload>
これらのリンクはルーターによって無視され、フルページのナビゲーションが発生します。
ナビゲーションのトリガー
セクションタイトル: ナビゲーションのトリガーastro:transitions/client
モジュールのnavigate
関数を使うと、通常のリンク以外のイベントでもクライアントサイドナビゲーションを発生させることができます。
<script> import { navigate } from "astro:transitions/client"; document.querySelector("select").onchange = (event) => { let href = event.target.value; navigate(href); };</script><select> <option value="/play">Play</option> <option value="/blog">Blog</option> <option value="/about">About</option> <option value="/contact">Contact</option></select>
---import Form from "../components/Form.astro";import { ClientRouter } from "astro:transitions";---<html> <head> <ClientRouter /> </head> <body> <Form /> </body></html>
クライアントディレクティブでハイドレートされたフレームワークコンポーネント内でもnavigate
を使用できます。
import { navigate } from "astro:transitions/client";
export default function Form() { return ( <select onChange={(e) => navigate(e.target.value)}> <option value="/play">Play</option> <option value="/blog">Blog</option> <option value="/about">About</option> <option value="/contact">Contact</option> </select> );}
この<Form />
コンポーネントは、<ClientRouter />
ルーターを使用するAstroページに、クライアントディレクティブを使用してレンダリングできます。
---import Form from "../components/Form.jsx";import { ClientRouter } from "astro:transitions";---<html> <head> <ClientRouter /> </head> <body> <Form client:load /> </body></html>
navigate
は以下のオプションを受け取ります。
href
(必須): 遷移先のURLoptions
:history
:"push"
|"replace"
|"auto"
push
: 「history.pushState」を使用して、ブラウザの履歴に新しいエントリを作成します。replace
: 「history.replaceState」を使用して、現在の履歴を上書きします。auto
(デフォルト): ルーターは ‘history.pushState’ を試みますが、遷移先のURLが有効でない場合、現在のURLはブラウザの履歴を変更せずに残ります。
formData
: POSTリクエストを送信するためのFormData
オブジェクト。
ブラウザの履歴を使った「戻る」「進む」ナビゲーションには、navigate()
と組み合わせて、ブラウザ組み込みのhistory.back()
、history.forward()
、history.go()
関数を利用できます。なお、navigate()
がコンポーネントのサーバーサイドレンダリング中に呼び出された場合は、何も起こりません。
履歴の置き換え
セクションタイトル: 履歴の置き換え通常、ナビゲーションごとにブラウザの履歴に新しいエントリが追加されますが、data-astro-history
属性を<a>
タグに付与することで、履歴の追加方法を制御できます。
push
:history.pushState
で新しい履歴エントリを追加します。replace
:history.replaceState
で現在の履歴を上書きします。auto
(デフォルト): 遷移先のURLが有効な場合はpushState
を実行し、そうでなければブラウザの履歴を更新しません。
<a href="/main" data-astro-history="replace">
フォームでのトランジション
セクションタイトル: フォームでのトランジションastro@4.0.0
<form>
要素でもGET/POSTの両方に対応したクライアントサイドトランジションが利用できます。
デフォルトでは、POST
時はmultipart/form-data
で送信されます。通常のブラウザと同じapplication/x-www-form-urlencoded
で送信したい場合はenctype
属性を指定してください。
<form action="/contact" method="POST" enctype="application/x-www-form-urlencoded">
data-astro-reload
属性を付与すると、そのフォームだけクライアントサイドトランジションを無効化できます。
<form action="/contact" data-astro-reload>
フォールバックの制御
セクションタイトル: フォールバックの制御<ClientRouter />
ルーターは、ビュートランジションをサポートするブラウザ(すなわちChromium系ブラウザ)で最も有効に機能しますが、他のブラウザにもデフォルトのフォールバックサポートが含まれています。ブラウザがView Transitions APIをサポートしていない場合でも、Astroのクライアントルーターはフォールバックオプションのいずれかを使ってブラウザ内ナビゲーションを提供できます。
サポート状況によっては、すべてのブラウザで同等の体験を得るために、アニメーションさせたい要素にname
やanimate
のトランジションディレクティブを明示的に設定する必要があります。
---import Layout from "../layouts/LayoutUsingClientRouter.astro";---<title transition:animate="fade">このサイトについて</title>
<ClientRouter />
コンポーネントにfallback
プロパティを追加し、swap
またはnone
に設定することで、Astroのデフォルトのフォールバックサポートをオーバーライドできます。
animate
(デフォルト、推奨) - Astroは、ページコンテンツを更新する前に、カスタム属性を使用してビュートランジションをシミュレートします。swap
- Astroはページのアニメーションを試みません。代わりに、旧ページはすぐに新しいページに置き換えられます。none
- Astroはアニメーションによるページ遷移を一切おこないません。代わりに、サポート外のブラウザではフルページのナビゲーションがおこなわれます。
---import { ClientRouter } from 'astro:transitions';---<title>私のサイト</title>
<ClientRouter fallback="swap">
initial
ブラウザアニメーションはAstroによってシミュレートされません。そのため、このアニメーションを使用する要素は現在アニメーションされません。
クライアントサイドナビゲーションの流れ
セクションタイトル: クライアントサイドナビゲーションの流れ<ClientRouter />
ルーターを使用すると、以下の流れでAstroのクライアントサイドナビゲーションが実行されます。
-
サイト訪問者が以下のいずれかのアクションを実行し、ナビゲーションが実行されます。
- サイト内の別のページへ内部リンクする
<a>
タグをクリックする。 - 戻るボタンをクリックする。
- 進むボタンをクリックする。
- サイト内の別のページへ内部リンクする
-
ルーターが次のページの取得を開始します。
-
ルーターが、
'forward'
または'back'
の値をもつdata-astro-transition
属性をHTML要素に追加します。 -
ルーターが
document.startViewTransition
をコールします。これにより、ブラウザのview transition processがトリガーされます。このとき、ブラウザはページの現在の状態をスクリーンショットします。 -
startViewTransition
コールバック内で、ルーターがswapを実行します。これは以下の一連のイベントから構成されます。-
<head>
のコンテンツが、いくつかの要素を残して入れ替わる。- FOUCを防止するために、スタイルシートのDOMノードが新しいページに存在する場合は残されます。
- 新しいページにスクリプトが存在する場合は残されます。
- 新しいページに対応する要素がある場合、
transition:persist
をもつ他のhead要素は残されます。
-
<body>
は、新しいページのbodyに完全に置き換えられる。 -
transition:persist
とマークされた要素が新しいページに存在する場合、新しいDOMに移動する。 -
必要に応じてスクロール位置が復元される。
-
astro:after-swap
イベントがdocument
でトリガーされます。ここでswapプロセスが終了する。
-
-
ルーターが、トランジションを解決する前に新しいスタイルシートのロードを待ちます。
-
ルーターが、ページに追加された新しいスクリプトを実行します。
-
astro:page-load
イベントが発生します。これでナビゲーションプロセスが完了します。
ページナビゲーション中のスクリプトの動作
セクションタイトル: ページナビゲーション中のスクリプトの動作<ClientRouter />
コンポーネントを使ってページ間を遷移する場合、スクリプトはブラウザの挙動に合わせて順番に実行されます。
- Astroのバンドル済みモジュールスクリプトは1度だけ実行され、再遷移時は無視されます。
- インラインスクリプトは、ページを再訪した場合や、同じスクリプトが含まれるページに戻った場合に再実行されることがあります。
スクリプトを確実に再実行したい場合は、ライフサイクルイベント(後述)でラップしてください。
スクリプトの順序
セクションタイトル: スクリプトの順序<ClientRouter />
コンポーネントを使用してページ間を遷移する場合、スクリプトはブラウザの挙動に合わせて順番に実行されます。
スクリプトの再実行
セクションタイトル: スクリプトの再実行バンドル済みモジュールスクリプト(Astroのデフォルトスクリプト)は、初回実行後は再遷移時に無視されます。同じスクリプトが新しいページに含まれていても再実行されません。
一方で、インラインスクリプトは、訪問者が複数回同じページを訪れた場合や、スクリプトが含まれるページに戻った場合に再実行される可能性があります。
ビュートランジションを使用すると、ページ遷移後に一部のスクリプトがフルページリフレッシュ時のように再実行されなくなる場合があります。クライアントサイドルーティング中に発生するイベントを監視し、適切なタイミングでスクリプトを実行することができます。
以下の例では、モバイル用の「ハンバーガーメニュー」のスクリプトをastro:page-load
イベントリスナーでラップし、ページ遷移後にクリックに反応するようにしています。このイベントはページ遷移の終了時に発生します。
document.addEventListener("astro:page-load", () => { document.querySelector(".hamburger").addEventListener("click", () => { document.querySelector(".nav-links").classList.toggle("expanded"); });});
次の例は、astro:after-swap
イベントに応答して実行される関数を示しています。このイベントは、新しいページが古いページを置き換えた直後、DOM要素が画面に描画される前に発生します。これにより、ページ遷移後にライトモードが一瞬表示されるのを防ぐために、ダークモードのテーマを確認し、必要に応じて設定します。
<script is:inline> function applyTheme() { localStorage.theme === "dark" ? document.documentElement.classList.add("dark") : document.documentElement.classList.remove("dark"); }
document.addEventListener("astro:after-swap", applyTheme); applyTheme();</script>
data-astro-rerun
セクションタイトル: data-astro-rerunastro@4.5.0
インラインスクリプトにdata-astro-rerun
属性を付与すると、ページ遷移ごとに必ず再実行されます。
<script is:inline data-astro-rerun>...</script>
グローバルな状態を設定する場合は、スクリプトが複数回実行される可能性を考慮し、window
などで状態を判定してください。
<script is:inline> if (!window.SomeGlobal) { window.SomeGlobal = {}; }</script>
モジュールスクリプトは、ブラウザがロード済みのモジュールを追跡しているため、常に1回だけ実行されます。これらのスクリプトでは再実行の心配は必要ありません。
ライフサイクルイベント
セクションタイトル: ライフサイクルイベント<ClientRouter />
ルーターは、ナビゲーション中に document
上で複数のイベントを発生させます。これらのイベントは、ナビゲーションのライフサイクルにフックし、新しいページのローディングインジケーターの表示や、デフォルト動作の上書き、ナビゲーション完了時の状態復元などに利用できます。
ナビゲーションプロセスは、準備フェーズ(新しいコンテンツの読み込み)、DOMスワップフェーズ(古いページの内容が新しいページの内容に置き換えられる)、完了フェーズ(スクリプトの実行やローディング完了の通知、クリーンアップなど)で構成されます。
AstroのView Transition APIのライフサイクルイベントは、以下の順で発生します。
before-
イベントはこれから実行されるアクションに介入・変更でき、after-
イベントはそのフェーズが完了したことを通知します。
一部のアクションはどのイベントでも実行できますが、ローディングスピナーの表示や、スワップ前のアニメーションペアの上書きなど、特定のイベントでのみ最適に実行できるタスクもあります。
astro:before-preparation
セクションタイトル: astro:before-preparation
追加:
astro@3.6.0
ナビゲーションが開始された直後(例:リンククリック直後)、コンテンツの読み込み前に発生するイベントです。
このイベントは以下の用途で使えます。
- ローディング開始前にスピナーを表示するなどの処理。
- 外部URLではなくテンプレートで定義したコンテンツを読み込むなど、ローディング処理の変更。
- カスタムアニメーションのためにナビゲーションの
direction
(通常はforward
またはbackward
)を変更。
以下は、astro:before-preparation
イベントでスピナーを表示し、ローディング完了後に停止する例です。loader
コールバックを使うことで非同期処理が可能です。
<script is:inline> document.addEventListener("astro:before-preparation", (event) => { const originalLoader = event.loader; event.loader = async function () { const { startSpinner } = await import("./spinner.js"); const stop = startSpinner(); await originalLoader(); stop(); }; });</script>
astro:after-preparation
セクションタイトル: astro:after-preparation
追加:
astro@3.6.0
新しいページのコンテンツが読み込まれパースされた後、ビュー遷移フェーズの直前に発生するイベントです。
以下のように、astro:before-preparation
でローディングインジケーターを表示し、astro:after-preparation
で非表示にできます。
<script is:inline> document.addEventListener("astro:before-preparation", () => { document.querySelector("#loading").classList.add("show"); }); document.addEventListener("astro:after-preparation", () => { document.querySelector("#loading").classList.remove("show"); });</script>
上記は、すべての処理が同期的に実行できる場合のシンプルな例です。loader
コールバックを使う必要はありません。
astro:before-swap
セクションタイトル: astro:before-swap
追加:
astro@3.6.0
準備フェーズで生成された新しいドキュメントが、現在のドキュメントに置き換わる直前に発生するイベントです。このイベントはビュー遷移中に発生し、ユーザーはまだ古いページのスナップショットを見ています。
このイベントでは、スワップ前に変更を加えることができます。event.newDocument
プロパティは、これから表示される新しいドキュメントを表します。以下は、localStorage
のダークモード設定を新しいページにも適用する例です。
<script is:inline> function setDarkMode(document) { let theme = localStorage.darkMode ? "dark" : "light"; document.documentElement.dataset.theme = theme; }
setDarkMode(document);
document.addEventListener("astro:before-swap", (event) => { // 新しいドキュメントにもテーマを適用 setDarkMode(event.newDocument); });</script>
astro:before-swap
イベントでは、スワップの実装自体も変更できます。デフォルトのスワップ実装は、headの内容の差分適用、__persistent__要素の移動、body全体の置き換えを行います。
このタイミングで、独自のスワップの実装(例:既存ドキュメント全体の差分適用)を定義することも可能です。
<script is:inline> document.addEventListener("astro:before-swap", (event) => { event.swap = () => { diff(document, event.newDocument); }; });</script>
カスタムスワップ関数の作成
セクションタイトル: カスタムスワップ関数の作成
追加:
astro@4.15.0
astro:transitions/client
モジュールのswapFunctions
オブジェクトは、ドキュメント属性やページ要素、スクリプト実行など、スワップ関連のタスクを処理する5つのユーティリティ関数を提供します。これらを使って独自のスワップ実装を定義できます。
以下は、Astroの組み込みスワップ実装を再現する例です。
<script> import { swapFunctions } from "astro:transitions/client";
// `window.document`を`doc`で置き換え function mySwap(doc: Document) { swapFunctions.deselectScripts(doc); swapFunctions.swapRootAttributes(doc); swapFunctions.swapHeadElements(doc); const restoreFocusFunction = swapFunctions.saveFocus(); swapFunctions.swapBodyElement(doc.body, document.body); restoreFocusFunction(); }
event.swap = () => mySwap(event.newDocument);<script>
カスタムスワップ実装はこのテンプレートをベースに、必要に応じて個別のステップを追加・置換できます。
astro:after-swap
セクションタイトル: astro:after-swap旧ページが新ページに置き換わった直後に発生するイベントです。document
オブジェクトにイベントリスナーを登録して検知することで、新しいページのDOM要素が描画され、スクリプトが実行される前にアクションを実行できます。
遷移元ページでこのイベントを監視することで、新しいページに引き継ぐ必要のあるDOM上の状態を渡したり復元したりできます。
このタイミングは、例えばダークモード用のクラス名(<html class="dark-mode">
)を追加するのに安全な最後のポイントです(より早いイベントで行っても構いません)。
astro:after-swap
イベントは、ブラウザの履歴が更新され、スクロール位置が復元された直後に発生します。
そのため、履歴ナビゲーション時のデフォルトのスクロール復元を上書きする用途にも使えます。
document.addEventListener("astro:after-swap", () => window.scrollTo({ left: 0, top: 0, behavior: "instant" }),);
あるいは、ダークモードのサポートを実装している場合、このイベントを使用してページロード間で状態を復元できます。
<script> const setDarkMode = () => { if (localStorage.darkMode) { document.documentElement.dataset.dark = ''; } }; // 初回のナビゲーションで実行される setDarkMode(); // ビュートランジションのナビゲーションで実行される document.addEventListener('astro:after-swap', setDarkMode);</script>
astro:page-load
セクションタイトル: astro:page-load新しいページが表示され、ブロッキングスタイルやスクリプトの読み込みが完了した後に発生するイベントです。document
オブジェクトでイベントを監視できます。
<ClientRouter />
コンポーネントは、プリレンダリングされたページの初回ナビゲーション時と、その後のすべてのナビゲーション(進む・戻る含む)でこのイベントを発生させます。
このイベントを使って、ナビゲーションごとに毎回実行したいコード(例:イベントリスナーの再設定など)を記述できます。
<script> document.addEventListener("astro:page-load", () => { // 初回ページロードとすべてのナビゲーション後に実行されます。 setupStuff(); // 例:イベントリスナーの追加 });</script>
アクセシビリティ
セクションタイトル: アクセシビリティクライアントサイドルーティングやページ遷移アニメーションの有効化にはアクセシビリティ上の課題も伴いますが、AstroはView Transitionsを利用するサイトがデフォルトでアクセシブルになるよう配慮しています。
ルーティングアナウンス
セクションタイトル: ルーティングアナウンス
追加:
astro@3.2.0
<ClientRouter />
コンポーネントは、クライアントサイドルーティング時のページナビゲーションにルーティングアナウンサーを含みます。
アクセシビリティ機能は、ナビゲーション後に新しいページタイトルをアナウンスすることで、ページが変わったことを利用者に伝えます。サーバーサイドルーティング(フルページリフレッシュ)ではこれが自動で行われますが、クライアントサイドルーティングでは<ClientRouter />
がこの役割を担います。
ルーティングアナウンスのため、コンポーネントは新しいページにaria-live="assertive"
属性を持つ要素を追加します。これによりアクセシビリティツールが以下の優先順位にしたがって、アナウンスします。
<title>
が存在すればその内容- 最初に見つかった
<h1>
- ページの
pathname
アクセシビリティ向上のため、各ページには必ず<title>
を含めるようにしてください。
prefers-reduced-motion
セクションタイトル: prefers-reduced-motionAstroの<ClientRouter />
コンポーネントは、prefer-reduced-motion
設定が検出された場合、フォールバックアニメーションを含むすべてのビュートランジションアニメーションを無効化するCSSメディアクエリを含みます。代わりに、ブラウザはアニメーションなしでDOM要素を単純にスワップします。