-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Fix Image block lightbox missing alt attribute and improve accessibility #55010
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
6f19386
Move lightbox open button after the image.
afercia 649b4a0
Fix getting the lightbox image alt attribute.
afercia c9bedb0
Improve docblocks.
afercia b8fa8f2
Do not render empty role attribute.
afercia eadf211
Remove unnecessary aria-hidden attribute.
afercia 1e6ea11
Set aria-modal attribute dynamically.
afercia d568e5a
More meaningful and simpler modal dialog aria-label.
afercia 6a8801f
Increase Close button target size.
afercia cbbfabd
Add enlarged image base64 encoded placeholder.
afercia b2b56cb
Better check for alt attribute as a string.
afercia 7f9996b
Update changelog.
afercia b887286
Move changelog entry to the block library changelog.
afercia 43e6be8
Set lightbox dialog aria-label dynamically.
afercia cea0f32
Hide background scrim container from assistive technology.
afercia ae939eb
Remove obsolete code
artemiomorales File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,10 +9,11 @@ | |
* Renders the `core/image` block on the server, | ||
* adding a data-id attribute to the element if core/gallery has added on pre-render. | ||
* | ||
* @param array $attributes The block attributes. | ||
* @param string $content The block content. | ||
* @param WP_Block $block The block object. | ||
* @return string Returns the block content with the data-id attribute added. | ||
* @param array $attributes The block attributes. | ||
* @param string $content The block content. | ||
* @param WP_Block $block The block object. | ||
* | ||
* @return string The block content with the data-id attribute added. | ||
*/ | ||
function render_block_core_image( $attributes, $content, $block ) { | ||
|
||
|
@@ -76,12 +77,13 @@ function render_block_core_image( $attributes, $content, $block ) { | |
} | ||
|
||
/** | ||
* Add the lightboxEnabled flag to the block data. | ||
* Adds the lightboxEnabled flag to the block data. | ||
* | ||
* This is used to determine whether the lightbox should be rendered or not. | ||
* | ||
* @param array $block Block data. | ||
* @return array Filtered block data. | ||
* @param array $block Block data. | ||
* | ||
* @return array Filtered block data. | ||
*/ | ||
function block_core_image_get_lightbox_settings( $block ) { | ||
// Get the lightbox setting from the block attributes. | ||
|
@@ -113,43 +115,44 @@ function block_core_image_get_lightbox_settings( $block ) { | |
} | ||
|
||
/** | ||
* Add the directives and layout needed for the lightbox behavior. | ||
* Adds the directives and layout needed for the lightbox behavior. | ||
* | ||
* @param string $block_content Rendered block content. | ||
* @param array $block Block object. | ||
* | ||
* @param string $block_content Rendered block content. | ||
* @param array $block Block object. | ||
* @return string Filtered block content. | ||
* @return string Filtered block content. | ||
*/ | ||
function block_core_image_render_lightbox( $block_content, $block ) { | ||
$processor = new WP_HTML_Tag_Processor( $block_content ); | ||
|
||
$aria_label = __( 'Enlarge image' ); | ||
|
||
$processor->next_tag( 'img' ); | ||
$alt_attribute = $processor->get_attribute( 'alt' ); | ||
|
||
if ( null !== $alt_attribute ) { | ||
// An empty alt attribute `alt=""` is valid for decorative images. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see above comment about |
||
if ( is_string( $alt_attribute ) ) { | ||
$alt_attribute = trim( $alt_attribute ); | ||
} | ||
|
||
// It only makes sense to append the alt text to the button aria-label when the alt text is non-empty. | ||
if ( $alt_attribute ) { | ||
/* translators: %s: Image alt text. */ | ||
$aria_label = sprintf( __( 'Enlarge image: %s' ), $alt_attribute ); | ||
} | ||
$content = $processor->get_updated_html(); | ||
|
||
// Currently, we are only enabling the zoom animation. | ||
$lightbox_animation = 'zoom'; | ||
|
||
// We want to store the src in the context so we can set it dynamically when the lightbox is opened. | ||
$z = new WP_HTML_Tag_Processor( $content ); | ||
$z->next_tag( 'img' ); | ||
|
||
// Note: We want to store the `src` in the context so we | ||
// can set it dynamically when the lightbox is opened. | ||
if ( isset( $block['attrs']['id'] ) ) { | ||
$img_uploaded_src = wp_get_attachment_url( $block['attrs']['id'] ); | ||
$img_metadata = wp_get_attachment_metadata( $block['attrs']['id'] ); | ||
$img_width = $img_metadata['width']; | ||
$img_height = $img_metadata['height']; | ||
} else { | ||
$img_uploaded_src = $z->get_attribute( 'src' ); | ||
$img_uploaded_src = $processor->get_attribute( 'src' ); | ||
$img_width = 'none'; | ||
$img_height = 'none'; | ||
} | ||
|
@@ -160,7 +163,7 @@ function block_core_image_render_lightbox( $block_content, $block ) { | |
$scale_attr = false; | ||
} | ||
|
||
$w = new WP_HTML_Tag_Processor( $content ); | ||
$w = new WP_HTML_Tag_Processor( $block_content ); | ||
$w->next_tag( 'figure' ); | ||
$w->add_class( 'wp-lightbox-container' ); | ||
$w->set_attribute( 'data-wp-interactive', true ); | ||
|
@@ -180,15 +183,17 @@ function block_core_image_render_lightbox( $block_content, $block ) { | |
"imageCurrentSrc": "", | ||
"targetWidth": "%s", | ||
"targetHeight": "%s", | ||
"scaleAttr": "%s" | ||
"scaleAttr": "%s", | ||
"dialogLabel": "%s" | ||
} | ||
} | ||
}', | ||
$lightbox_animation, | ||
$img_uploaded_src, | ||
$img_width, | ||
$img_height, | ||
$scale_attr | ||
$scale_attr, | ||
__( 'Enlarged image' ) | ||
) | ||
); | ||
$w->next_tag( 'img' ); | ||
|
@@ -200,27 +205,28 @@ function block_core_image_render_lightbox( $block_content, $block ) { | |
// Wrap the image in the body content with a button. | ||
$img = null; | ||
preg_match( '/<img[^>]+>/', $body_content, $img ); | ||
$button = | ||
'<button | ||
type="button" | ||
aria-haspopup="dialog" | ||
aria-label="' . esc_attr( $aria_label ) . '" | ||
data-wp-on--click="actions.core.image.showLightbox" | ||
data-wp-style--width="context.core.image.imageButtonWidth" | ||
data-wp-style--height="context.core.image.imageButtonHeight" | ||
data-wp-style--left="context.core.image.imageButtonLeft" | ||
data-wp-style--top="context.core.image.imageButtonTop" | ||
> | ||
</button>' | ||
. $img[0]; | ||
|
||
$button = | ||
$img[0] | ||
. '<button | ||
type="button" | ||
aria-haspopup="dialog" | ||
aria-label="' . esc_attr( $aria_label ) . '" | ||
data-wp-on--click="actions.core.image.showLightbox" | ||
data-wp-style--width="context.core.image.imageButtonWidth" | ||
data-wp-style--height="context.core.image.imageButtonHeight" | ||
data-wp-style--left="context.core.image.imageButtonLeft" | ||
data-wp-style--top="context.core.image.imageButtonTop" | ||
></button>'; | ||
|
||
$body_content = preg_replace( '/<img[^>]+>/', $button, $body_content ); | ||
|
||
// We need both a responsive image and an enlarged image to animate | ||
// the zoom seamlessly on slow internet connections; the responsive | ||
// image is a copy of the one in the body, which animates immediately | ||
// as the lightbox is opened, while the enlarged one is a full-sized | ||
// version that will likely still be loading as the animation begins. | ||
$m = new WP_HTML_Tag_Processor( $content ); | ||
$m = new WP_HTML_Tag_Processor( $block_content ); | ||
$m->next_tag( 'figure' ); | ||
$m->add_class( 'responsive-image' ); | ||
$m->next_tag( 'img' ); | ||
|
@@ -236,7 +242,7 @@ function block_core_image_render_lightbox( $block_content, $block ) { | |
$m->set_attribute( 'data-wp-style--object-fit', 'selectors.core.image.lightboxObjectFit' ); | ||
$initial_image_content = $m->get_updated_html(); | ||
|
||
$q = new WP_HTML_Tag_Processor( $content ); | ||
$q = new WP_HTML_Tag_Processor( $block_content ); | ||
$q->next_tag( 'figure' ); | ||
$q->add_class( 'enlarged-image' ); | ||
$q->next_tag( 'img' ); | ||
|
@@ -268,20 +274,16 @@ function block_core_image_render_lightbox( $block_content, $block ) { | |
} | ||
|
||
$close_button_icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="15" height="15" aria-hidden="true" focusable="false"><path d="M13 11.8l6.1-6.3-1-1-6.1 6.2-6.1-6.2-1 1 6.1 6.3-6.5 6.7 1 1 6.5-6.6 6.5 6.6 1-1z"></path></svg>'; | ||
$dialog_label = $alt_attribute ? esc_attr( $alt_attribute ) : esc_attr__( 'Image' ); | ||
$close_button_label = esc_attr__( 'Close' ); | ||
|
||
$lightbox_html = <<<HTML | ||
<div data-wp-body="" class="wp-lightbox-overlay $lightbox_animation" | ||
data-wp-bind--role="selectors.core.image.roleAttribute" | ||
aria-label="$dialog_label" | ||
data-wp-bind--aria-label="selectors.core.image.dialogLabel" | ||
data-wp-class--initialized="context.core.image.initialized" | ||
data-wp-class--active="context.core.image.lightboxEnabled" | ||
data-wp-class--hideAnimationEnabled="context.core.image.hideAnimationEnabled" | ||
data-wp-bind--aria-hidden="!context.core.image.lightboxEnabled" | ||
aria-hidden="true" | ||
data-wp-bind--aria-modal="context.core.image.lightboxEnabled" | ||
aria-modal="false" | ||
data-wp-bind--aria-modal="selectors.core.image.ariaModal" | ||
data-wp-effect="effects.core.image.initLightbox" | ||
data-wp-on--keydown="actions.core.image.handleKeydown" | ||
data-wp-on--touchstart="actions.core.image.handleTouchStart" | ||
|
@@ -294,19 +296,21 @@ function block_core_image_render_lightbox( $block_content, $block ) { | |
</button> | ||
<div class="lightbox-image-container">$initial_image_content</div> | ||
<div class="lightbox-image-container">$enlarged_image_content</div> | ||
<div class="scrim" style="background-color: $background_color"></div> | ||
<div class="scrim" style="background-color: $background_color" aria-hidden="true"></div> | ||
</div> | ||
HTML; | ||
|
||
return str_replace( '</figure>', $lightbox_html . '</figure>', $body_content ); | ||
} | ||
|
||
/** | ||
* Ensure that the view script has the `wp-interactivity` dependency. | ||
* Ensures that the view script has the `wp-interactivity` dependency. | ||
* | ||
* @since 6.4.0 | ||
* | ||
* @global WP_Scripts $wp_scripts | ||
* | ||
* @return void | ||
*/ | ||
function block_core_image_ensure_interactivity_dependency() { | ||
global $wp_scripts; | ||
|
@@ -322,6 +326,8 @@ function block_core_image_ensure_interactivity_dependency() { | |
|
||
/** | ||
* Registers the `core/image` block on server. | ||
* | ||
* @return void | ||
*/ | ||
function register_block_core_image() { | ||
register_block_type_from_metadata( | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
excellent comment, and excellent fix above. one small note:
null
indicates that thealt
attribute is missing, but an empty value would be''
or if it's only present as a name likesrc="…" alt loading=eager
then the value would betrue
this might be more appropriate, as it ensures that that we're dealing with an
alt=something
situationThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a way, the check is a bit redundant in the first place. As far as I know, WordPress enforces at least an empty
alt=""
attribute everywhere. I'd agree to check for string anyways, just in case.