多數人以為,網頁要做出電影般的換頁動畫,網站架構就得先整個打掉重練,改成用 JavaScript 框架接管路由的單頁應用(SPA)。其實不必這麼大費周章,瀏覽器本身就內建了這個功能,叫做 View Transitions API(畫面轉場 API),而且完全不用寫一行 JavaScript。
只要在樣式表加上幾行 CSS 宣告,一般的多頁式網站(MPA)點連結換頁時,畫面就會自動跑交叉淡出淡入,不再是舊文件卸載到新文件載入完成之間,硬生生泛白或泛黑的那段空檔。這對台灣大量還在用 WordPress 這類多頁架構的網站是個好消息,不必砍掉重練成 SPA,也不用裝一支笨重的動畫外掛,就能拿到接近原生 App 的順暢切換體感。

換頁瞬間泛白,其實是瀏覽器換頁的原生留白
點一個連結換頁,畫面閃過一下空白甚至純黑,再跳出新頁面,這不是網站設計出了問題,是瀏覽器處理換頁這件事時本來就會有的空檔。瀏覽器收到導覽請求後,會先把目前這份文件整個卸載,包括它的 DOM 樹、樣式與 JavaScript 狀態全部清空,接著才去抓取新的 HTML、重新解析、重新繪製畫面。舊文件被丟掉、新文件還沒解析完成的這段期間,瀏覽器沒有任何補間畫面可以顯示,只能露出瀏覽器背景色,通常就是那片白。
過去要避開這段空白,幾乎只有一條路,把整個網站改造成單頁應用。SPA 的做法是讓瀏覽器只完整載入一次頁面骨架,之後每次換頁其實都是用 JavaScript 動態抽換畫面裡的一小塊 DOM,瀏覽器從頭到尾沒有真正卸載過文件,自然也就沒有那段空白可言。轉場動畫就是在這次抽換前後,用框架自己控制淡出淡入或滑動效果。這也是網站要做順暢轉場,一定要上 SPA 框架這個說法的由來,過去確實只有這條路能走。
不過瀏覽器這幾年已經把這件事內建進來了,一般的多頁式網站不用改架構、不用裝框架,也能拿到同等順暢轉場的原生做法。
View Transitions API 是什麼?瀏覽器內建的轉場快照機制
View Transitions API(畫面轉場 API)是由全球資訊網協會與網頁孵化社群小組(W3C/WICG)共同推動、目前由主流瀏覽器原生實作的網頁平台功能。運作邏輯很直觀,每次換頁或換狀態的那一瞬間,瀏覽器會自動拍下「舊畫面」與「新畫面」兩張快照,再用普通的 CSS Animation 在這兩張快照之間跑轉場,預設效果是淡出淡入。整個過程不需要安裝任何 JavaScript 框架,也不必把網站改成單頁應用,原本是純 HTML 的多頁式網站照樣能用。
這個 API 其實分成兩個層級,而且目前的成熟度不一樣。一個是給單頁應用用的「同文件轉場」,另一個是給多頁式網站用的「跨文件轉場」,兩者的觸發方式與支援度都不同。跨文件轉場才是一般 WordPress 這類多頁式網站真正用得到的那一半。
同文件轉場與跨文件轉場,多頁網站要用的是後者
同文件轉場(same-document view transition)是設計給單頁應用用的,網頁的 JavaScript 呼叫 document.startViewTransition(),在同一份文件裡手動更新 DOM,例如切換某個區塊的內容或換一個分頁,瀏覽器接手拍照與補動畫,開發者只需要負責更新 DOM 這一步。這條路徑一直都要寫 JavaScript,跟框架時代的做法差異不大,只是省掉了自己接管動畫的力氣。
跨文件轉場(cross-document view transition)不需要呼叫任何 API,使用者點一個普通的 <a> 連結,瀏覽器導覽到另一個 HTML 檔案,只要兩邊都宣告了對應的 CSS,轉場就會自動觸發,完全不必寫任何 JavaScript。這代表現有的 WordPress、靜態網站、傳統伺服器渲染的網站,不需要改一行後端邏輯,光靠 CSS 就能拿到轉場效果。這也是為什麼跨文件轉場比同文件轉場更值得一般網站關注,它幾乎是零成本的升級。

啟用跨文件轉場只需要一段 CSS 宣告
要打開跨文件轉場,做法比想像中單純,在前後兩個頁面都會載入到的樣式表裡,加上一段 @view-transition 規則。
@view-transition {
navigation: auto;
}Code language: CSS (css)
只要這段 CSS 生效,使用者點連結從一個頁面換到另一個頁面時,瀏覽器就會自動跑一次交叉淡出淡入,不需要再寫任何 JavaScript 去監聽換頁事件。對大部分網站來說,這段 CSS 放進全站共用的主要樣式表就夠了,因為多數 CMS 或框架產出的頁面本來就會共用同一份 CSS。
有一個容易被忽略的條件,出發頁和到達頁都要宣告這段 CSS,轉場才會生效。只有出發頁有宣告、到達頁沒有,或反過來,瀏覽器會判定條件不成立,直接跳過轉場,換頁功能照常運作,只是少了動畫。這個特性也可以反過來利用,例如刻意讓登入頁、付款頁不宣告這段 CSS,讓使用者在這些敏感頁面之間切換時維持乾淨俐落的直接跳轉,不套用轉場效果。
另外要留意,navigation: auto 只對同源、且使用者主動觸發的導覽生效。導覽到別的網域的連結、表單送出後的 POST 提交、或是用 JavaScript 寫的 location.href = ... 這種程式化導覽,都不會觸發轉場。
舊版的 meta 標籤寫法已經廢止不再生效
網路上還流傳著一種更早期的寫法,在 <head> 放一段 meta 標籤來宣告啟用跨文件轉場。
<meta name="view-transition" content="same-origin">Code language: HTML, XML (xml)
這個寫法已經被廢止。Chrome 從 126 版開始,改成前一節介紹的 @view-transition CSS at-rule 寫法,舊的 meta 標籤不再被辨識。麻煩的是,廢止之後照抄這段舊教學並不會跳出任何錯誤訊息或警告,瀏覽器只是安靜地不執行轉場,畫面換頁跟平常一樣,什麼事都沒發生。對第一次接觸這個功能的人來說,這種沒有錯誤訊息的失效最容易讓人卡在這一步,以為是自己 CSS 寫錯或瀏覽器不支援,查了半天才發現是教學本身過時。判斷現在該用哪個寫法,最保險的做法是直接核對規格頁面本身,而不是隨便找一篇教學文照抄。
轉場的底層原理是拍照、換頁、再交叉動畫
要理解怎麼自訂轉場效果,得先弄清楚瀏覽器背後實際做了什麼。整個流程可以拆成 4 個步驟:第一步,瀏覽器在畫面即將被換掉前,先拍下舊頁面目前呈現的視覺快照;第二步,卸載舊文件、載入新文件,這段期間畫面的渲染會被暫時抑制,使用者看不到中間過程;第三步,新文件準備好要顯示時,瀏覽器再拍下新頁面的視覺快照;第四步,用一般的 CSS Animation 讓這兩張快照交叉播放,動畫跑完才恢復正常渲染,新頁面正式接手畫面。

這兩張快照分別對應兩個 CSS 偽元素,::view-transition-old(root) 代表舊畫面的快照,::view-transition-new(root) 代表新畫面的快照。瀏覽器預設幫這兩個偽元素套上一組淡出淡入的動畫,舊快照淡出、新快照淡入,疊在一起就是最常見的交叉淡化效果。這組預設動畫可以直接用 CSS 覆寫,換成滑動、縮放或其他效果都行,寫法就是重新指定這兩個偽元素的 animation-name。
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 0.4s;
}
@keyframes slide-to-left {
to {
transform: translateX(-30px);
opacity: 0;
}
}
@keyframes slide-from-right {
from {
transform: translateX(30px);
opacity: 0;
}
}
::view-transition-old(root) {
animation-name: slide-to-left;
}
::view-transition-new(root) {
animation-name: slide-from-right;
}Code language: CSS (css)
上面這段把預設的淡出淡入換成舊頁面往左滑出、新頁面從右滑入的效果,概念上跟原生 App 常見的左右切換頁面很像,而且全程只靠 CSS 完成,不必寫一行 JavaScript 去控制位移或計時。
讓特定元素跨頁對應的 view-transition-name 屬性
前面講的是整頁一起淡出淡入,但很多情境其實只想讓某個元素在兩個頁面之間變形過渡,例如清單頁的縮圖放大變成詳情頁的大圖,或導覽列裡目前作用中的項目跟著移動,而不是整頁畫面一起閃一下。做法是給兩個頁面上概念相同的元素設定相同的 view-transition-name,瀏覽器就會自動幫這個元素做位置與尺寸的補間動畫,業界通常稱作 morph(形變)。
.thumbnail {
view-transition-name: article-cover;
}
.article-cover-image {
view-transition-name: article-cover;
}Code language: CSS (css)
以文章清單頁過渡到文章詳情頁為例,只要清單頁的縮圖和詳情頁的主圖都設定了同一個 view-transition-name,讀者點進文章時看到的就不是兩頁各自淡出淡入,而是縮圖本身長大變成主圖,視覺上的連續感明顯更強。
這個屬性有一個限制要記住,同一個轉場過程中,每個名稱在同一時間只能配對一個元素,如果兩個以上的元素同時使用了同一個 view-transition-name,整個轉場會直接失效,退回成普通換頁,連預設的淡出淡入都不會有。跨文件的情境還有另一層複雜,兩個頁面的 DOM 結構通常不完全一樣,清單頁的縮圖跟詳情頁的主圖,標籤、class、所在位置多半都不同,要不要對應、對應哪幾個元素,需要逐一盤點兩邊頁面的結構後再決定,不是每個元素都適合套用。
圖片快照預設會被拉伸,需要靠 object-fit 校正
套用 view-transition-name 做圖片形變時,有一個常見的坑值得專門提醒。轉場快照的本質是一張攤平的點陣圖,如果同一張圖片在舊頁面與新頁面的尺寸比例不一樣,例如清單頁是正方形縮圖、詳情頁是寬螢幕主圖,瀏覽器預設會把舊的那張快照直接拉伸塞進新的框裡,結果圖片看起來被硬拉變形,人臉、Logo 這類有明確形狀的畫面尤其明顯。
修法是替對應的偽元素加上 object-fit: cover,讓快照維持原本的長寬比例、裁切掉多餘的部分,而不是整張硬拉伸。
::view-transition-old(article-cover),
::view-transition-new(article-cover) {
object-fit: cover;
}Code language: CSS (css)
加上這段之後,轉場過程中圖片會維持比例做局部裁切,看起來就像正常的相片縮放,而不是被壓扁或拉長的破圖。凡是用 view-transition-name 對應圖片元素,而且兩個頁面的顯示比例可能不同,這段 object-fit 幾乎是必加的保險。
瀏覽器支援度呈現同文件成熟、跨文件仍缺一角
目前,View Transitions API 的兩個層級,支援度差了一大截。同文件轉場已經在 2025 年 10 月跨過 Baseline「Newly available」(近期可用)的門檻,代表 Chrome、Edge、Safari 與 Firefox 目前的最新版本全部支援,是四大瀏覽器引擎都到齊的狀態。
跨文件轉場則還停留在 Baseline「Limited availability」(有限可用),意思是還有主流瀏覽器沒跟上。目前 Chrome 與 Edge 從 126 版起支援,Safari 從 18.2 版起支援,但 Firefox 的穩定版目前仍未實作這個 at-rule。比較特別的是,Firefox 對同文件轉場其實支援得不晚,缺的只是跨文件這一半,並不是整個 API 都不支援。

面對這種一半成熟、一半還沒到齊的狀態,正確的心態是把跨文件轉場當成漸進增強(progressive enhancement)來用,而不是等全部瀏覽器到齊才敢上線。沒有實作這個 at-rule 的瀏覽器,使用者換頁時就是回到原本沒有轉場動畫的樣子,換頁功能本身完全不受影響,不會白屏、不會報錯,也不需要為了這個功能寫任何 polyfill 或去阻擋不支援的瀏覽器存取。
上線前該留意的 3 個實務眉角
demo 展示時看起來很順的轉場效果,搬進正式環境常常會冒出幾個細節問題。以下 3 件事,建議在導入前就先弄清楚,而不是等使用者回報異常才回頭查。
4 秒逾時,頁面載入太慢轉場會靜悄悄消失
跨文件轉場有一個硬性的逾時機制,如果導覽開始之後,新頁面在 4 秒內沒有進入瀏覽器判定為可渲染的狀態,轉場就會直接被取消,頁面照常換過去,但不會跳出任何錯誤訊息或提示。這個計時是從導覽動作發起的那一刻開始算,不是從新頁面的 HTML 開始抵達才起算,所以整個換頁流程,包含伺服器回應、資源下載、瀏覽器解析,都算在這 4 秒之內。
造成逾時最常見的兩個原因是伺服器回應太慢(TTFB 過長),以及會擋住渲染的資源,例如大尺寸的首屏圖片,或是設定成 font-display: block 的字型,在字型下載完成前整個文字都不顯示。從另一個角度看,這個逾時反而是件好事,它變相督促把首屏載入速度處理好,轉場悄悄消失,某種程度上就是網站效能不夠好的一個訊號,值得回頭檢查。
只有同源、使用者主動點擊的導覽才會觸發
navigation: auto 這個值刻意設計得保守,只有同源導覽,而且是使用者實際點擊連結、或按瀏覽器的上一頁/下一頁按鈕時,才會觸發轉場。導覽到別的網域的連結、用 JavaScript 寫的 location.href = ... 這類程式化導覽、還有表單的 POST 提交,都不會套用轉場效果,即使兩個頁面都宣告了 @view-transition 也一樣。
這個限制對導入這個功能的人來說其實是好消息,不必額外寫程式碼去排除某些不該有動畫的操作。例如送出付款表單、送出登入表單這類動作,天生就不會被轉場效果影響,不會有使用者按下付款按鈕、畫面卻先跑一段淡出淡入才跳轉這種讓人分心的體驗。
使用者開啟減少動態效果偏好時要跟著關閉轉場
部分使用者會在作業系統或瀏覽器設定裡,主動開啟減少動態效果(prefers-reduced-motion: reduce)這個偏好設定。對這群使用者來說,畫面轉場動畫不只是不喜歡而已,某些前庭功能敏感的人甚至會因為畫面滑動、縮放的視覺刺激,引發暈眩或噁心等生理不適,這不是體驗上的小事,是無障礙設計必須注意的一環。
做法很簡單,把啟用轉場的那段 CSS 包進 @media (prefers-reduced-motion: no-preference) 條件裡,只在使用者沒有表達偏好的情況下才啟用轉場。
@media (prefers-reduced-motion: no-preference) {
@view-transition {
navigation: auto;
}
}Code language: CSS (css)
這樣寫完全不需要額外寫 JavaScript 去判斷使用者的系統設定,CSS 媒體查詢本身就會自動處理,對開啟減少動態效果的使用者來說,換頁就會維持成最單純直接的跳轉,沒有任何動畫效果。
轉場進行中,用 :active-view-transition 隱藏容易穿幫的元素
除了控制轉場動畫本身,還多了一個專門處理轉場進行中這段期間的 CSS 偽類,:active-view-transition。它只比對根元素(:root),而且只在畫面正處於轉場動畫進行中的這段期間才成立,轉場一結束就自動失效,不需要另外寫程式碼去解除。這個偽類已在 2025 年 10 月跨過 Baseline「Newly available」的門檻,屬於相對新的功能。
典型用途是隱藏轉場過程中容易穿幫的固定元素,像是固定在畫面上的工具列、提示框,或是鍵盤操作留下的 focus ring。這類元素本身不屬於舊畫面或新畫面快照的一部分,但轉場動畫是拿整個畫面去拍照交叉淡出淡入,固定元素疊在上面常常會看起來像殘影或位置跑掉,視覺上很不自然。過去要處理這個問題,只能靠 JavaScript 在轉場的回呼函式裡手動切換某個 class,轉場開始加上、轉場結束拿掉,現在有了這個偽類,直接用宣告式的 CSS 就能寫完。
:root:active-view-transition .toolbar {
visibility: hidden;
}Code language: CSS (css)
如果需要用程式邏輯判斷目前是否有轉場正在進行,JavaScript 這一側對應的是 Document.activeViewTransition,讀取這個屬性會拿到目前的轉場物件,沒有轉場進行中時則是 null,這個寫法通常用在需要更精細控制的情境,一般的隱藏穿幫元素用途,CSS 偽類就已經夠用。
多頁式 WordPress 網站導入這個功能的兩條路,官方外掛與兩行 CSS
回到這篇一開始提出的問題,多頁式的 WordPress 網站要用上這個功能,完全不需要外掛層級的動畫特效套件,也不必把網站改造成單頁應用。實際能走的路有兩條。
第一條是自己動手,把前面幾節整理出的 @view-transition { navigation: auto; } 直接加進佈景主題的全站樣式表,例如 style.css。因為 WordPress 各種頁面模板,文章頁、列表頁、單頁,本來就共用同一份樣式表,這段 CSS 等於一次宣告就涵蓋了全站每個頁面,不需要逐頁另外設定。
第二條是用官方外掛。WordPress 效能團隊(Performance Team)本身維護了一支名為「View Transitions」的官方外掛,安裝並啟用後,不需要自己寫任何 CSS,「設定」底下的「閱讀」畫面就有開關與動畫時間的設定選項。這支外掛的說明頁明白寫著,它是為了未來可能被納入 WordPress 核心而做的功能實驗,鼓勵使用者回饋意見,裝上它不會帶來額外的安全疑慮。

不管走哪一條路,結果都一樣,沒有支援這個 at-rule 的瀏覽器,使用者看到的就是原本沒有轉場動畫的普通換頁,不會出現任何異常或錯誤畫面,換頁功能本身完全不受影響。
跨文件轉場其實是成本很低的漸進增強,兩三行 CSS,就能讓多頁式網站的換頁動作,從硬邦邦的空白閃過,變成有連續感的轉場。它不會取代單頁應用該解決的複雜前端狀態管理,但對絕大多數靠伺服器渲染、逐頁載入的網站來說,這已經足夠把換頁體感拉到接近原生 App 的水準。等 Firefox 補上這一塊、跨文件轉場真正進到隨處可用的階段,現在先寫好的這段 CSS 也不需要再改一行,提早準備好的網站,到時候只是多了更多使用者能看到這份順暢。
