Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add view transitions theme support in abstracted way with sensible defaults #8370

Draft
wants to merge 20 commits into
base: trunk
Choose a base branch
from

Conversation

felixarntz
Copy link
Member

@felixarntz felixarntz commented Feb 20, 2025

This PR explores adding cross-document view transitions to WordPress Core. These enable smooth transitions between URL navigations and thus can improve user experience.

Learn more about cross-document view transitions

For browser support, see https://caniuse.com/mdn-css_at-rules_view-transition

High-level approach

  • In principle, how view transitions should be applied heavily depends on the layout and markup of each site. WordPress themes are rather unpredictable in that regard - for the most part.
  • This PR focuses on identifying aspects where themes have things in common. Specifically, a smooth transition between certain elements of a post (e.g. navigating between the singular post URL and the same post within a blog or archive URL) seems achievable.
    • For classic themes, the post is almost always wrapped in an article.post element or, sometimes on the singular post URL it's not wrapped specifically, but this we can detect via body.single selector.
    • For block themes, it's equally straightforward, as there a post is almost always wrapped in a .wp-block-post.post element.
    • We can identify between which post the navigation happens by comparing the URL that is being navigated to or from with the specific .post element on the page that contains an a link pointing to that same URL. That way we can achieve a nice effect where relevant post elements (in this PR so far the post title and featured image) shift to their location within the other URL.
  • For block themes, this is overall by far the most straightforward, since their markup is far more predictable, so it should be reasonable to enable view transitions for block themes by default.

What's supported?

API

  • This PR opts in every block theme and very classic default theme to using cross-document view transitions.
  • Any theme can opt in by calling add_theme_support( 'view-transitions' ).
  • Optionally, themes that opt in can provide configuration arguments to fine-tune the default behavior by providing an array of arguments in the call: See https://github.com/WordPress/wordpress-develop/pull/8370/files#diff-b9f1810ad43acfa11ba58a9f21eb0ee3a8063c80aa155de9b210323252534716R2938 for the supported arguments.
  • For now, none of the opted in themes provides custom arguments. This is so that we can first focus on testing and validating the most suitable default behavior before getting into fine-tuning.

Transition behavior

  • Overall, the entire content on any URL will fade over to the next one. This is the very basic foundation of view transitions and, while maybe quite nice, not very useful on its own. It's the more specific transitions that make it visually appealing (see below).
  • When navigating between an archive view URL and a single post URL within that archive, the following elements will morph between the two URLs (e.g. transitioning between their locations and transforming in size accordingly):
    • the overall page header
    • the overall page content
    • the specific post title being navigated to/from
    • the specific post thumbnail / featured image (if present) being navigated to/from
    • the specific post content (if present) being navigated to/from
  • If pretty permalinks are enabled:
    • When navigating between different pages of the same archive view, the overall page content will slide to the left if moving to a higher page, or to the right if moving to a lower page, while the rest of the page remains in place.
    • When navigating between different pages of a multi-page post (rarely used, can be achieved via "Page Break" block), the specific post content will slide to the left if moving to a higher page, or to the right if moving to a lower page, while the rest of the page remains in place.
    • When navigating from a post to the previous or next post, the overall page content will slide to the left if moving to a newer post, or to the right if moving to an older post, while the rest of the page remains in place. This only works if the permalinks are configured to include the date (e.g. /%year%/%monthnum%/%day%/%postname%/).

Testing

  1. Use a compatible browser (see https://caniuse.com/mdn-css_at-rules_view-transition).
  2. Simply apply the PR to your codebase, or use WordPress Playground.
  3. Create a few posts with non-empty titles and any content, most of them with a featured image. 3-4 posts should be enough for testing.
  4. Try using any of the WordPress default themes, navigating between blog/archive views and specific posts on them to see the view transitions in effect.

Next steps

I tested all default themes with how they behave, and it works for almost all of them:

  • 2025: Works out of the box.
  • 2024: Works out of the box.
  • 2023: Works out of the box.
  • 2022: Works out of the box.
  • 2021: Works out of the box.
  • 2020: Works out of the box.
  • 2019: Requires custom configuration due to special handling of full-screen featured images.
  • 2017: Requires custom configuration due to special handling of full-screen featured images.
  • 2016: Works out of the box.
  • 2015: Works out of the box.
  • 2014: Works out of the box.
  • 2013: Works out of the box.
  • 2012: Works out of the box.
  • 2011: Works as well as it can, because featured images aren't even displayed in blog or archive views.

Obviously, more at-scale in research and testing is needed, to see how other themes (especially classic themes) handle those areas in terms of markup and whether they would also work out of the box or would require customizations. But this is a start :)

Trac ticket:


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

Copy link

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • The Plugin and Theme Directories cannot be accessed within Playground.
  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

const isMainSlide = transitionType === 'forwards' || transitionType === 'backwards';
let foundMainElement = false;
return [
...Object.entries( config.globalTransitionNames || {} ).map( ( [ selector, name ] ) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some inline comments above each of the return elements would be nice, took a bit to understand what the code was doing exactly

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added docs in 220d1b8

];
};

const setTemporaryViewTransitionNames = async ( entries, vtPromise ) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function also clears the transition names after the transitions complete, right? inline doc would be nice here as well :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added docs for all the functions in 220d1b8

@adamsilverstein
Copy link
Member

❤️ Amazing work @felixarntz 🎉 I love how you managed to create such a simple way for themes to opt in to View Transitions, and haw you handled the complex bits automatically (especially figuring out if the transition is moving "forwards" or "backwards"). Also, I completely forgot about page breaks, neat that it already supports them.

Overall this looks really good, my main feedback would to request a bit more inline documentation, especially for the obtuse JavaScript bits. It might also be nice to capture how these transitions look across a subset of core themes and include brief screencasts of them in action, or maybe some Playground blueprints that were preset to use them including sample content.

This also looks like something we might be able to leverage eventually for the Query Loop Block, eg navigating from a post list loop to a single post. Have you already considered how that could work?

Copy link
Member

@westonruter westonruter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested with Twenty Twenty-One and it felt magical.

Screen.recording.2025-03-03.12.57.25.webm

Comment on lines 87 to 88
let oldPageMatches = oldPathname.match( /\/page\/(\d+)\/?$/ );
let newPageMatches = newPathname.match( /\/page\/(\d+)\/?$/ );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically the /page/ can be overridden by setting it to something else in WP_Rewrite::$pagination_base. So ideally this would be exported from PHP as part of the config.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related, I was thinking last week about whether we should even use the URLs for this or rely on classes on body. Almost all relevant WordPress query parameters (including those for pagination) have body classes we could use. That would make the implementation more flexible and also work on sites without pretty permalinks.

Part of the JS in this PR already used body classes, so maybe we should use that approach more holistically?

@felixarntz
Copy link
Member Author

@adamsilverstein

Overall this looks really good, my main feedback would to request a bit more inline documentation, especially for the obtuse JavaScript bits.

Fair point, I'll add more soon. As I'm still experimenting at this point, I didn't want to invest too much time documenting just yet, but eventually there should definitely be better docs.

It might also be nice to capture how these transitions look across a subset of core themes and include brief screencasts of them in action, or maybe some Playground blueprints that were preset to use them including sample content.

Great idea! Let's work on this once the PR is in a place where we think the feature set is worth moving forward with.


window.wp = window.wp || {};
window.wp.viewTransitions = {};
window.wp.viewTransitions.init = ( config ) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually config should have types added to it, along with the relevant type annotations throughout this file. But maybe doesn't make sense yet while still in prototype phase.

Copy link
Member Author

@felixarntz felixarntz Mar 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you mean that? Types as in TypeScript?

I agree it's too early now, FWIW implementing this as a Gutenberg package may be better from a technical perspective alone, since writing JavaScript in Core is subject to ancient linting requirements (GHA currently fails because of basic things like using const in this file) 🙃

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Types as in JSDoc comments.

Copy link
Member

@westonruter westonruter Mar 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not TypeScript, but JSDoc (with maybe TypeScript in the JSDoc). For example, like we do in the PL plugin:
https://github.com/WordPress/performance/blob/trunk/plugins/performance-lab/includes/admin/plugin-activate-ajax.js
https://github.com/WordPress/performance/blob/1a7f906f96a3c6635e8a1cfdb8e36ee1ff6aa75e/plugins/optimization-detective/detect.js#L318-L339

(GHA currently fails because of basic things like using const in this file) 🙃

That used to be the case, but that was changed in Core-58645. So most of the JSHint complaints are actual issues:

   src/js/_enqueues/wp/view-transitions.js
      8 |    if ( ! window.navigation || ! 'CSSViewTransitionRule' in window ) {
                                         ^ Confusing use of '!'.
     25 |                ? Object.entries( config.postTransitionNames || {} ).map( ( [ selector, name ] ) => {
                         ^ Misleading line break before '?'; readers may interpret this as an expression boundary.
     34 |    const setTemporaryViewTransitionNames = async ( entries, vtPromise ) => {
                                                     ^ 'async functions' is only available in ES8 (use 'esversion: 8').
     44 |        for ( const [ element, _ ] of entries ) {
                                        ^ '_' is defined but never used.

The third issue is due to:

"esversion": 6,

We should update that to 8 now. Maybe apply that change here while waiting for it to be fixed in core. I've opened Core-63077 to address this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Committed to trunk in r59963.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added docs for this function (and others) in 220d1b8.

@westonruter
Copy link
Member

While I'm not a designer, I think it's unfortunate that the index template in Twenty Twenty-Five puts the post title after the featured image:

Screen.recording.2025-03-12.14.49.49.webm

Whereas on the singular template, the index template has the post title before the featured image. If the index template were made consistent, this would make the transition better IMO:

Screen.recording.2025-03-12.14.50.42.webm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants