Skip to content

Add support for chronological and pagination transitions #2318

Open

Description

@westonruter

This was split out from #1997 as a polishing issue to graduate from non-experimental.

Activity

  1. moved this to Not Started/Backlog 📆 in WP Performance Ongoingon Jan 2, 2026
  2. westonruter commented on Jan 6, 2026

    @westonruter
    MemberAuthor

    Let's also make sure there is a view transition between paginated list table navigations in the admin. cc @mukeshpanchal27

  3. moved this from Not Started/Backlog 📆 to In Progress 🚧 in WP Performance Ongoingon Jan 6, 2026
  4. b1ink0 commented on Sep 4, 2026

    @b1ink0
    Contributor

    Adding the issues that needs to be addressed before CSS only @navigation based view transitions can be shipped:

    1. Interactivity API Modifies the Navigation Context

    Directional transitions become intermittent on pages containing an interactive block.

    WordPress 7.1 runs the following logic from wp-includes/js/dist/script-modules/interactivity/index.min.js during module evaluation (Gutenberg source: packages/interactivity/src/index.ts):

    window.history.replaceState(
    	{ ...window.history.state, wpInteractivityId: Qe },
    	""
    );
    
    window.addEventListener( "popstate", ( e ) => {
    	e.state !== null &&
    		e.state?.wpInteractivityId !== Qe &&
    		window.location.reload();
    } );

    history.replaceState() registers a same-document navigation. Once it runs, from: and to: both resolve to the current URL, so an @navigation condition comparing the two no longer matches.

    Whether this happens before or after pagereveal is a race, which makes the issue intermittent.

    This is not specific to CSS route matching. Anything using navigation.activation.from, including the JavaScript direction detection this PR replaces, sees the same modified navigation context.

    Temporary Workaround is to delay history.replaceState() calls until after pagereveal, then flushes them, with a load fallback.

    Possible upstream solutions include:

    • Writing the ID only when the router itself creates a history entry.
    • Treating a missing ID as "not ours" rather than "stale".
    • Deferring the initial history tagging until after the first render opportunity.
    • Applying the behavior only when the router module is present instead of the base Interactivity API module.
    2. Speculation Rules prerender Loses the Direction

    When a prerendered document is activated, no @navigation direction condition matches. However, the nested @view-transition opt-in still survives.

    The result is a transition with an empty type set, so none of the html:active-view-transition-type(...) rules apply and the browser falls back to the default cross-fade.

    The following result was consistent across five navigations in both directions:

    Event Document Matched event.viewTransition Types Prerendered
    pageswap outgoing chronological-forwards non-null chronological-forwards no
    prerenderingchange incoming none – – yes
    pagereveal incoming none non-null empty yes

    The incoming document already reports no matching condition at prerenderingchange, so this does not appear to be a race that can be solved by reading the value later.

    Because the transition pseudo-elements belong to the incoming document, there is no page-side workaround once the incoming document has an empty type set.

    With the same pages and stylesheet but prerendering disabled:

    • pagereveal reports chronological-forwards.
    • The transition type is chronological-forwards.
    • The directional animation works.

    prefetch is unaffected because the document is not created until navigation occurs.

    Chrome recommends prerendering as a way to improve cross-document view transitions:

    For best effect of cross-page view transitions in general you need fast loading pages. For this you can consider prerendering, using the Speculation Rules API.

    See Cross-document view transitions.

    This also overlaps with WordPress itself:

    • Core has shipped speculative loading since WordPress 6.8 in wp-includes/speculative-loading.php.
    • Core defaults to prefetch with conservative eagerness, which is unaffected.
    • Sites can switch to prerender through wp_speculation_rules_configuration, WP_SPECULATIVE_LOADING_DEFAULT_MODE, the corresponding environment variable, or the Speculation Rules plugin.

    A site enabling prerender therefore loses directional transitions without any indication that the two features conflict.

    3. JavaScript Cannot Be Fully Removed Yet

    Direction detection can move to CSS, but cross-document element pairing cannot yet be made fully declarative.

    For example, when navigating from an archive to a single post, the clicked post's thumbnail and the destination hero image need the same view-transition-name.

    The server knows all possible posts, but it does not know which archive link the user clicked.

    view-transition-name: match-element does not solve this for cross-document transitions. Per MDN:

    Elements in different documents have a different identity, therefore match-element can only be used in same-document view transitions.

    The server could assign deterministic names:

    view-transition-name: plvt-post-123;

    The same name could be used for the archive item and the corresponding hero image, allowing them to pair without JavaScript.

    However, this means every named archive item becomes a transition participant and may need to be snapshotted by the browser. view-transition-class reduces CSS duplication but does not remove that snapshot cost.

    Declarative Solution in the Spec

    :navigation-source is intended to expose the element that initiated the navigation:

    @navigation (between: --home --detail) {
    	@navigation (at: --home) {
    		:nav-source img {
    			view-transition-name: image;
    		}
    	}
    
    	@navigation (at: --detail) {
    		img#hero {
    			view-transition-name: image;
    		}
    	}
    }

    It is defined in CSS Navigation Matching (css-navigation-1), and Chromium has a navigation-source-001.html test covering anchors, submit buttons, and forms. It intentionally does not match back/forward navigation because those navigations are not user-initiated.

    Until this functionality ships, cross-document element pairing still requires JavaScript or a potentially more expensive server-side naming strategy.

    Experimental PR b1ink0#2 with working CSS navigation based chronological and pagination transitions

    Screen.Recording.2026-09-05.at.12.35.37.AM.mov

    Claude Opus 5 was used for debugging, and investigation.

  5. b1ink0 commented on Sep 4, 2026

    @b1ink0
    Contributor

    Let's also make sure there is a view transition between paginated list table navigations in the admin. cc @mukeshpanchal27

    @westonruter, I tested the view transitions on paginated list tables, and they seem to be working already. Could you check the video below and confirm whether this is the behavior you were referring to, or if you meant something else?

    Screen.Recording.2026-09-05.at.12.26.16.AM.mov
  6. westonruter commented on Sep 4, 2026

    @westonruter
    MemberAuthor

    I tested the view transitions on paginated list tables, and they seem to be working already

    @b1ink0 Yes, they work in that there is a cross-fade animation. I was wondering if there should be horizontal animation from right-to-left when clicking a subsequent page, or left-to-right when navigating to a previous page, as you showed in your frontend demo in #2318 (comment).

    Maybe this would just be distracting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions