From 8d83177b0f1c786915e12c049e84ff8642b81681 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 20 May 2026 15:34:55 -0700 Subject: [PATCH 1/3] Refine the admin menu view transition animation. Clip each top-level menu item's view transition snapshot to its animating group box. By default a captured snapshot keeps its full intrinsic height, so a newly expanded submenu painted at full height from the first frame and overlapped the menu items below while they were still sliding into place. Setting `overflow: clip` on the image pair makes the submenu wipe open, and closed in reverse, in step with those items. Also name the collapse button so it slides into place with the menu items rather than cross-fading via the page root, and add comments explaining each rule. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/wp-admin/css/view-transitions.css | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/wp-admin/css/view-transitions.css b/src/wp-admin/css/view-transitions.css index 538a90b502f9e..619a74be8c660 100644 --- a/src/wp-admin/css/view-transitions.css +++ b/src/wp-admin/css/view-transitions.css @@ -3,7 +3,40 @@ navigation: auto; } + /* + * Give each top-level menu item its own view transition name — its id, + * such as "menu-posts" — so each one is captured as an independent group + * and animates from its old position and size to its new ones across a + * navigation. This produces the accordion effect: the previously open + * section collapses as the newly current one expands, instead of the + * whole menu simply cross-fading. attr() falls back to "none" for any + * item whose id is missing or is not a valid custom identifier. The + * shared wp-menu-top class lets the image-pair rule below address every + * menu-top group at once. + */ #adminmenu > .menu-top { view-transition-name: attr(id type(), none); + view-transition-class: wp-menu-top; + } + + /* + * Name the collapse button too, so it slides into place with the menu + * items instead of cross-fading as part of the page root. + */ + #collapse-menu { + view-transition-name: collapse-menu; + } + + /* + * Clip each menu-top's snapshot to its animating group box. By default a + * captured snapshot keeps its full intrinsic height, so the new (expanded) + * submenu paints at full height from the first frame and overlaps the + * items below while they are still sliding down. Clipping the image pair + * to the group — whose height animates from the link row to the full + * expanded height — makes the submenu wipe open, and closed in reverse, + * in step with those items. + */ + ::view-transition-image-pair(.wp-menu-top) { + overflow: clip; } } From 867f8e39dec328710e790e73ee2e82802719540e Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 20 May 2026 17:17:56 -0700 Subject: [PATCH 2/3] Render-block admin pages so View Transitions capture a complete page. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A cross-document view transition snapshots the incoming page at its first render, which can occur before the body has finished parsing. When that happens the admin menu is only partially built in the snapshot, so the menu-top elements have no height delta and the section accordion silently degrades to a plain cross-fade. Print a render-blocking `` pointed at the end of the body, so the first render — and therefore the transition's snapshot — waits until the page is fully parsed. The media query scopes the small first-render delay to when view transitions run. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/wp-includes/default-filters.php | 1 + src/wp-includes/view-transitions.php | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index a4ecf677e2af1..0f1110bfee892 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -603,6 +603,7 @@ add_action( 'admin_enqueue_scripts', 'wp_common_block_scripts_and_styles' ); add_action( 'admin_enqueue_scripts', 'wp_enqueue_command_palette_assets' ); add_action( 'admin_enqueue_scripts', 'wp_enqueue_view_transitions_admin_css' ); +add_action( 'admin_head', 'wp_print_view_transitions_render_blocking_link' ); add_action( 'enqueue_block_assets', 'wp_enqueue_classic_theme_styles' ); add_action( 'enqueue_block_assets', 'wp_enqueue_registered_block_scripts_and_styles' ); add_action( 'enqueue_block_assets', 'enqueue_block_styles_assets', 30 ); diff --git a/src/wp-includes/view-transitions.php b/src/wp-includes/view-transitions.php index 2d2ad61e0b431..e6d9bdf075b71 100644 --- a/src/wp-includes/view-transitions.php +++ b/src/wp-includes/view-transitions.php @@ -28,3 +28,24 @@ function wp_get_view_transitions_admin_css(): string { $path = ABSPATH . "wp-admin/css/view-transitions{$affix}.css"; return file_get_contents( $path ); } + +/** + * Prints a render-blocking expectation so View Transitions capture a fully parsed admin page. + * + * A cross-document view transition snapshots the incoming page at its first render, which can + * happen before the `` has finished parsing. The snapshot would then capture a partially + * built page — for example an incomplete admin menu — and the transition animates to or from + * the wrong state. + * + * The `expect` link blocks the first render until an element near the end of the body is + * parsed, so the snapshot is always taken of a complete page. The media query limits the + * (small) first-render delay to when view transitions actually run, matching the + * `@view-transition` rule in view-transitions.css. + * + * @since 7.0.1 + * + * @link https://html.spec.whatwg.org/multipage/links.html#link-type-expect + */ +function wp_print_view_transitions_render_blocking_link(): void { + echo '' . "\n"; +} From b5abf8de2b11c8fceb725e813d51cb43516cd2bf Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 20 May 2026 17:24:38 -0700 Subject: [PATCH 3/3] Address PHPStan error: Function wp_get_view_transitions_admin_css() should return string but returns string|false. --- src/wp-includes/view-transitions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/view-transitions.php b/src/wp-includes/view-transitions.php index e6d9bdf075b71..784482dd48d36 100644 --- a/src/wp-includes/view-transitions.php +++ b/src/wp-includes/view-transitions.php @@ -26,7 +26,7 @@ function wp_enqueue_view_transitions_admin_css(): void { function wp_get_view_transitions_admin_css(): string { $affix = SCRIPT_DEBUG ? '' : '.min'; $path = ABSPATH . "wp-admin/css/view-transitions{$affix}.css"; - return file_get_contents( $path ); + return (string) file_get_contents( $path ); } /**