One of the web technologies I’m currently most excited about is the View Transitions API. Latest chromium based browsers have shipped support and it can be used right now as a progressively enhanced feature on your site.
One of the use cases for the API is to animate DOM changes within the current page but what I’ve been most eagerly waiting for has been Cross-document view transitions for multi-page applications. To my dismay that article was a bit heavy on JavaScript – my original understanding was this API could be entirely controlled with CSS. Well it turns out that is true, no JavaScript required.
Browsing the Frontend Masters Boost blog I saw the effect I was dreaming of in action. I immediately opened devtools to check how it was being done and smiled when finding JavaScript was not involved.
The implementation is simple assuming you can associate content across pages via unique identifier e.g. in the case of WordPress a post_id
. The trick is two elements, one on the current page and another on the next page, need to share a transition name. That transition name can not appear more than once per page otherwise the browser does not know which element to transition. Assigning the transition name is done with an inline style:
The left side is a screenshot of my homepage, article titles and images share the same view-transition-name
as the title and image on the detail page shown to the right.
To complete the implementation add the following global CSS rule:
@media (prefers-reduced-motion: no-preference) {
@view-transition {
navigation: auto;
}
}
In the case of WordPress you can skip a stylesheet and add this in your theme’s theme.json
file:
{
"styles": {
"css": "@media (prefers-reduced-motion: no-preference) { @view-transition { navigation: auto; } }"
}
}
As for those inline view transition names, throw the following into your functions.php:
add_filter('render_block', __NAMESPACE__ . '\add_view_transition_names', 10, 2);
function add_view_transition_names($block_content, $block)
{
global $post;
if (!is_a($post, 'WP_POST')) return $block_content;
$block_name = $block['blockName'];
$blocks = [
'core/post-title',
'core/post-featured-image'
];
foreach ($blocks as $key => $value) {
if ($value != $block_name) continue;
$post_id = $post->ID;
$clean_block_name = sanitize_title($block_name);
$block_content = new \WP_HTML_Tag_Processor($block_content);
$block_content->next_tag();
$block_content->set_attribute('style', "view-transition-name: $clean_block_name-$post_id;");
$block_content->get_updated_html();
}
return $block_content;
}