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

Explore view transitions in Twenty Fifteen for post title and featured image specifically #8131

Draft
wants to merge 7 commits into
base: trunk
Choose a base branch
from
3 changes: 3 additions & 0 deletions src/wp-content/themes/twentyfifteen/css/view-transitions.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@view-transition {
navigation: auto;
}
15 changes: 15 additions & 0 deletions src/wp-content/themes/twentyfifteen/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,21 @@ function twentyfifteen_scripts() {
'collapse' => '<span class="screen-reader-text">' . __( 'collapse child menu', 'twentyfifteen' ) . '</span>',
)
);

wp_enqueue_style(
'twentyfifteen-view-transitions',
get_template_directory_uri() . '/css/view-transitions.css',
array(),
'20250115'
);

// This script must be loaded prior to rendering, i.e. not in the footer and not deferred or async.
wp_enqueue_script(
'twentyfifteen-view-transitions',
get_template_directory_uri() . '/js/view-transitions.js',
array(),
'20250115'
);
}
add_action( 'wp_enqueue_scripts', 'twentyfifteen_scripts' );

Expand Down
87 changes: 87 additions & 0 deletions src/wp-content/themes/twentyfifteen/js/view-transitions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
if ( !! window.navigation && 'CSSViewTransitionRule' in window ) {
const setTemporaryViewTransitionNames = async ( entries, vtPromise ) => {
for ( const [ element, name ] of entries ) {
if ( ! element ) {
continue;
}
element.style.viewTransitionName = name;
}

await vtPromise;

for ( const [ element, _ ] of entries ) {
if ( ! element ) {
continue;
}
element.style.viewTransitionName = '';
}
};

window.addEventListener( 'pageswap', ( e ) => {
if ( e.viewTransition ) {
if ( document.body.classList.contains( 'single' ) ) {
const article = document.querySelectorAll( 'article.post' );
if ( article.length !== 1 ) {
return;
}

setTemporaryViewTransitionNames( [
[ article[ 0 ].querySelector( '.entry-title' ), 'post-title' ],
[ article[ 0 ].querySelector( '.post-thumbnail' ), 'post-thumbnail' ],
], e.viewTransition.finished );
} else if ( document.body.classList.contains( 'home' ) || document.body.classList.contains( 'archive' ) ) {
const articleLink = document.querySelector( 'article.post a[href="' + e.activation.entry.url + '"]' );
if ( ! articleLink ) {
return;
}
Comment on lines +33 to +36
Copy link
Member Author

Choose a reason for hiding this comment

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

@bramus I debugged the issue further and got as far as figuring out that it, for some reason, comes down to this:

  • When you click on the link to an article without a featured image from the home page, the articleLink variable here will be empty - at least this is how it shows when I add a console.log( articleLink ); below.
  • This causes the early return to trigger.

I still don't understand why that's happening, for two reasons:

  1. If I manually run this exact document.querySelector( ... ) call in the console using the URL of that post in question, I do get a result, which is the a element in the post title. That's what I would expect. But for some reason, during the view transition it returns nothing.
  2. The early return here is triggered if articleLink is empty. But that shouldn't cause the browser to infinitely get stuck on loading the page right? Or is there any "cleanup" to do if a view transition is "aborted" with an early return like this?


const article = articleLink.closest( 'article.post' );
if ( ! article ) {
return;
}

setTemporaryViewTransitionNames( [
[ article.querySelector( '.entry-title' ), 'post-title' ],
[ article.querySelector( '.post-thumbnail' ), 'post-thumbnail' ],
], e.viewTransition.finished );
}
}
} );

window.addEventListener( 'pagereveal', ( e ) => {
if ( ! window.navigation.activation.from ) {
return;
}

if ( e.viewTransition ) {
if ( document.body.classList.contains( 'single' ) ) {
const article = document.querySelectorAll( 'article.post' );
if ( article.length !== 1 ) {
return;
}

setTemporaryViewTransitionNames( [
[ article[ 0 ].querySelector( '.entry-title' ), 'post-title' ],
[ article[ 0 ].querySelector( '.post-thumbnail' ), 'post-thumbnail' ],
], e.viewTransition.ready );
} else if ( document.body.classList.contains( 'home' ) || document.body.classList.contains( 'archive' ) ) {
const articleLink = document.querySelector( 'article.post a[href="' + window.navigation.activation.from.url + '"]' );
if ( ! articleLink ) {
return;
}

const article = articleLink.closest( 'article.post' );
if ( ! article ) {
return;
}

setTemporaryViewTransitionNames( [
[ article.querySelector( '.entry-title' ), 'post-title' ],
[ article.querySelector( '.post-thumbnail' ), 'post-thumbnail' ],
], e.viewTransition.ready );
}
}
} );
} else {
window.console.warn( 'View transitions not loaded as the browser is lacking support.' );
}
Loading