Skip to content

Fix layout shift caused by video tag in Video block lacking width and height #70293

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

Open
wants to merge 6 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ function gutenberg_reregister_core_block_types() {
'table-of-contents',
'text-columns',
'verse',
'video',
'embed',
),
'block_names' => array(
Expand Down Expand Up @@ -120,6 +119,7 @@ function gutenberg_reregister_core_block_types() {
'tag-cloud.php' => 'core/tag-cloud',
'template-part.php' => 'core/template-part',
'term-description.php' => 'core/term-description',
'video.php' => 'core/video',
),
),
__DIR__ . '/../build/edit-widgets/blocks/' => array(
Expand Down
1 change: 1 addition & 0 deletions packages/block-library/src/video/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ function VideoEdit( {
}
}, [ poster ] );

// TODO: Whether the video was obtained from the media library or was provided by URL, obtain the `videoWidth` and `videoHeight` of the video once its metadata has loaded and persist in the block attributes.
function onSelectVideo( media ) {
if ( ! media || ! media.url ) {
// In this case there was an error
Expand Down
91 changes: 91 additions & 0 deletions packages/block-library/src/video/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php
/**
* Server-side rendering of the `core/video` block.
*
* @package WordPress
*/

/**
* Renders the `core/video` block on the server to supply the width and height attributes from the attachment metadata.
*
* @since 0.0.1
*
* @phpstan-param array{ "id"?: positive-int } $attributes
*
* @param array $attributes The block attributes.
* @param string $content The block content.
* @return string The block content with the dimensions added.
*/
function render_block_core_video( array $attributes, string $content ): string {
// if the content lacks any video tag, abort.
if ( ! str_contains( $content, '<video' ) ) {
return $content;
}

// If the 'id' attribute is not populated for a video attachment, abort.
if (
! isset( $attributes['id'] ) ||
! is_int( $attributes['id'] ) ||
$attributes['id'] <= 0
) {
return $content;
}

// If the 'id' attribute wasn't for an attachment, abort.
if ( get_post_type( $attributes['id'] ) !== 'attachment' ) {
return $content;
}

// Get the width and height metadata for the video, and abort if absent or invalid.
$metadata = wp_get_attachment_metadata( $attributes['id'] );
if (
! isset( $metadata['width'], $metadata['height'] ) ||
! ( is_int( $metadata['width'] ) && is_int( $metadata['height'] ) ) ||
! ( $metadata['width'] > 0 && $metadata['height'] > 0 )
) {
return $content;
}

// Locate the VIDEO tag to add the dimensions.
$p = new WP_HTML_Tag_Processor( $content );
if ( ! $p->next_tag( array( 'tag_name' => 'VIDEO' ) ) ) {
return $content;
}

$p->set_attribute( 'width', (string) $metadata['width'] );
$p->set_attribute( 'height', (string) $metadata['height'] );

/*
* The aspect-ratio style is needed due to an issue with the CSS spec: <https://github.com/w3c/csswg-drafts/issues/7524>.
* Note that a style rule using attr() like the following cannot currently be used:
*
* .wp-block-video video[width][height] {
* aspect-ratio: attr(width type(<number>)) / attr(height type(<number>));
* }
*
* This is because this attr() is yet only implemented in Chromium: <https://caniuse.com/css3-attr>.
*/
$style = $p->get_attribute( 'style' );
if ( ! is_string( $style ) ) {
$style = '';
}
$aspect_ratio_style = sprintf( 'aspect-ratio: %d / %d;', $metadata['width'], $metadata['height'] );
$p->set_attribute( 'style', $aspect_ratio_style . $style );

return $p->get_updated_html();
}

/**
* Registers the `core/video` block on server.
*
* @since 0.0.1
*/
function register_block_core_video(): void {
register_block_type_from_metadata(
__DIR__ . '/video',
array(
'render_callback' => 'render_block_core_video',
)
);
}
add_action( 'init', 'register_block_core_video' );
1 change: 1 addition & 0 deletions packages/block-library/src/video/style.native.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

.video {
width: 100%;
height: auto;
}

.videoContainer {
Expand Down
1 change: 1 addition & 0 deletions packages/block-library/src/video/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
box-sizing: border-box;
video {
width: 100%;
height: auto;
Copy link
Member Author

@westonruter westonruter Jun 3, 2025

Choose a reason for hiding this comment

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

Without this, the aspect-ratio doesn't cause the height of the element to be the intrinsic height based on the resized width. Notice the white bars on the top and bottom of the video, causes by the default object-fit:contain styles when height:auto is absent:

Before After
Screenshot 2025-06-03 at 08 16 53 Screenshot 2025-06-03 at 08 05 51

Copy link
Member Author

Choose a reason for hiding this comment

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

Originally proposed in #37052 as a follow-up to #30092

vertical-align: middle;
}

Expand Down
Loading