diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index 7175d7a88747d..b2152fb6b8dd0 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -816,7 +816,8 @@ private static function get_viewport_breakpoint_value_in_pixels( $value ) { * the default breakpoints when no valid custom breakpoint is provided. When * only one breakpoint is valid, it remains keyed by its configured state and * uses a single max-width media query. When `tablet` is not larger than - * `mobile`, it is removed. + * `mobile`, or does not share an absolute or relative unit type with it, it + * is removed. * * @since 7.1.0 * @@ -852,7 +853,17 @@ private static function sanitize_viewport_settings( $viewport_settings ) { $sanitized = array( 'mobile' => $breakpoints['mobile']['value'] ); if ( isset( $breakpoints['tablet'] ) && $breakpoints['mobile']['px'] < $breakpoints['tablet']['px'] ) { - $sanitized['tablet'] = $breakpoints['tablet']['value']; + /* + * A media query resolves `em` and `rem` against the browser's default + * font size, so the order of a pair that mixes a relative unit with + * `px` only holds at the 16px base assumed above. + */ + $mobile_is_absolute = str_ends_with( $breakpoints['mobile']['value'], 'px' ); + $tablet_is_absolute = str_ends_with( $breakpoints['tablet']['value'], 'px' ); + + if ( $mobile_is_absolute === $tablet_is_absolute ) { + $sanitized['tablet'] = $breakpoints['tablet']['value']; + } } return $sanitized; diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index c2cda7bb158d0..5ef2d4d76e1ac 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -1248,6 +1248,49 @@ public function test_get_viewport_media_queries_omits_tablet_when_its_breakpoint ); } + /** + * @ticket 65865 + */ + public function test_get_viewport_media_queries_omits_tablet_when_breakpoints_mix_absolute_and_relative_units() { + $this->assertSame( + array( + '@mobile' => '@media (width <= 30em)', + '@desktop' => '@media (width > 30em)', + ), + WP_Theme_JSON::get_viewport_media_queries( + array( + 'mobile' => '30em', + 'tablet' => '580px', + ), + array( + 'include_desktop' => true, + ) + ) + ); + } + + /** + * @ticket 65865 + */ + public function test_get_viewport_media_queries_keeps_tablet_when_breakpoints_mix_em_and_rem() { + $this->assertSame( + array( + '@mobile' => '@media (width <= 30em)', + '@tablet' => '@media (30em < width <= 48rem)', + '@desktop' => '@media (width > 48rem)', + ), + WP_Theme_JSON::get_viewport_media_queries( + array( + 'mobile' => '30em', + 'tablet' => '48rem', + ), + array( + 'include_desktop' => true, + ) + ) + ); + } + /** * @ticket 65596 */