From 96e603b9a9c1a3c4c8d2a59a6212d8ea7e434cd8 Mon Sep 17 00:00:00 2001 From: Joe Dolson Date: Tue, 14 Jul 2026 15:50:12 -0500 Subject: [PATCH 01/19] Add support for tooltips for metabox controls This PR wraps the existing buttons in markup for existing styles and scripts, but doesn't change the function signature to support adding all of the custom markup. It may be desirable to add a modified path to inject this markup into existing patterns. This could be done similar to `wp_get_admin_notice()`, which has a bunch of parsers for attributes, but I think this isn't appropriate here. Open to ideas, but I was thinking of something that accepted existing button markup, used the HTML API to inject the additional button markup, then accepted a string for tooltip text. I don't think this is needed for toggletips, but for existing markup needing tooltips, it may be helpful. See https://core.trac.wordpress.org/ticket/50921 --- src/wp-admin/css/wp-tooltip.css | 1 - src/wp-admin/includes/template.php | 34 +++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/wp-admin/css/wp-tooltip.css b/src/wp-admin/css/wp-tooltip.css index 04ac5cd131ea9..c7c937d7b45e1 100644 --- a/src/wp-admin/css/wp-tooltip.css +++ b/src/wp-admin/css/wp-tooltip.css @@ -3,7 +3,6 @@ .wp-tooltip { display: inline-flex; align-items: center; - vertical-align: middle; } /* Toggle and close buttons. */ diff --git a/src/wp-admin/includes/template.php b/src/wp-admin/includes/template.php index 70b8249ed9c55..d8d017e50154a 100644 --- a/src/wp-admin/includes/template.php +++ b/src/wp-admin/includes/template.php @@ -1338,6 +1338,8 @@ function do_meta_boxes( $screen, $context, $data_object ) { $i = 0; if ( isset( $wp_meta_boxes[ $page ][ $context ] ) ) { + wp_enqueue_script( 'wp-tooltip' ); + wp_enqueue_style( 'wp-tooltip' ); foreach ( array( 'high', 'sorted', 'core', 'default', 'low' ) as $priority ) { if ( isset( $wp_meta_boxes[ $page ][ $context ][ $priority ] ) ) { foreach ( (array) $wp_meta_boxes[ $page ][ $context ][ $priority ] as $box ) { @@ -1396,41 +1398,53 @@ function do_meta_boxes( $screen, $context, $data_object ) { } echo '
'; - - echo ''; - echo ''; + echo '
'; - echo ''; - echo ''; + echo ''; - echo ''; + echo ''; + echo '' . $show_hide_text . ''; + echo ''; + echo ''; echo ''; } From d877331cc9ca62156ee784d18710b203fcd3ff44 Mon Sep 17 00:00:00 2001 From: Joe Dolson Date: Tue, 14 Jul 2026 15:53:08 -0500 Subject: [PATCH 02/19] Update template.php --- src/wp-admin/includes/template.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/template.php b/src/wp-admin/includes/template.php index d8d017e50154a..304285056cef2 100644 --- a/src/wp-admin/includes/template.php +++ b/src/wp-admin/includes/template.php @@ -1398,7 +1398,7 @@ function do_meta_boxes( $screen, $context, $data_object ) { } echo '
'; - + echo '
'; echo ''; echo ' - ' . sprintf( - /* translators: %s: Meta box title. */ - __( 'Move %s box up' ), - $widget_title - ) . ''; + ' . $move_up_text . ''; echo '
'; + $move_down_text = sprintf( + /* translators: %s: Meta box title. */ + __( 'Move %s box down' ), + $widget_title + ); echo '
'; echo ''; echo ' - ' . sprintf( - /* translators: %s: Meta box title. */ - __( 'Move %s box down' ), - $widget_title - ) . ''; + ' . $move_down_text . ''; echo '
'; $show_hide_text = sprintf( From 679511c19da1dee572f6a6a8048a4c547bce2591 Mon Sep 17 00:00:00 2001 From: Joe Dolson Date: Wed, 15 Jul 2026 16:34:04 -0500 Subject: [PATCH 04/19] Add HTML API parsing of button markup. Uses the HTML API to process passed markup and insert into required pattern. Allows passing existing buttons without needing to add support for custom attributes. Adds tests to verify markup changes. --- src/wp-admin/includes/template.php | 58 ++++++++++---------- src/wp-includes/general-template.php | 35 ++++++++---- tests/phpunit/tests/general/wpGetTooltip.php | 18 ++++++ 3 files changed, 73 insertions(+), 38 deletions(-) diff --git a/src/wp-admin/includes/template.php b/src/wp-admin/includes/template.php index afc0c3b8858f2..8e3f52259b367 100644 --- a/src/wp-admin/includes/template.php +++ b/src/wp-admin/includes/template.php @@ -1399,48 +1399,50 @@ function do_meta_boxes( $screen, $context, $data_object ) { echo '
'; - $move_up_text = sprintf( + $move_up_text = sprintf( /* translators: %s: Meta box title. */ __( 'Move %s box up' ), $widget_title ); - echo '
'; - echo ''; - echo ' - ' . $move_up_text . ''; - echo '
'; + $move_up_button = ''; + $move_up_args = array( + 'id' => $box['id'] . '-handle-order-higher-description', + 'button' => $move_up_button, + ); + echo wp_get_tooltip( $move_up_text, $move_up_args ); - $move_down_text = sprintf( + $move_down_text = sprintf( /* translators: %s: Meta box title. */ __( 'Move %s box down' ), $widget_title ); - echo '
'; - echo ''; - echo ' - ' . $move_down_text . ''; - echo '
'; + $move_down_button = ''; + $move_down_args = array( + 'id' => $box['id'] . '-handle-order-lower-description', + 'button' => $move_down_button, + ); + echo wp_get_tooltip( $move_down_text, $move_down_args ); - $show_hide_text = sprintf( + $show_hide_text = sprintf( /* translators: %s: Hidden accessibility text. Meta box title. */ __( 'Show or hide panel: %s' ), $widget_title ); - echo '
'; - echo ''; - echo ''; - echo '' . $show_hide_text . ''; - echo ''; - echo '
'; + $show_hide_button = ''; + $show_hide_args = array( + 'id' => $box['id'] . '-handlediv', + 'button' => $show_hide_button, + ); + echo wp_get_tooltip( $show_hide_text, $show_hide_args ); echo '
'; } diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index 36f3db3beba10..5476d9e41efa9 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -446,6 +446,8 @@ function wp_get_toggletip( $content, $args ) { * * @type string $id Unique ID for the popover element. Default is a * generated unique ID. + * @type string $button Existing `button` markup. Used instead of generated button. + * Default empty. * @type string $label Accessible label for the toggle button. * Default 'Help', matching the default icon. * Ignored for tooltips. @@ -469,6 +471,7 @@ function wp_get_tooltip_helper( $content, $args = array() ) { $defaults = array( 'id' => '', + 'button' => '', 'label' => __( 'Help' ), 'close_label' => __( 'Close' ), 'icon' => 'dashicons-editor-help', @@ -488,15 +491,29 @@ function wp_get_tooltip_helper( $content, $args = array() ) { $icon = '' !== $args['icon'] ? ' ' . $args['icon'] : ''; + $button = ''; + if ( ! empty( $args['button'] ) ) { + $processor = new WP_HTML_Tag_Processor( $args['button'] ); + if ( $processor->next_tag( 'button' ) ) { + $processor->set_attribute( 'popovertarget', '%2$s' ); + $processor->add_class( 'wp-tooltip__toggle' ); + if ( 'tooltip' !== $args['type'] ) { + $processor->set_attribute( 'aria-haspopup', 'dialog' ); + } + + $button = $processor->get_updated_html(); + } + } + if ( 'tooltip' === $args['type'] ) { // Tooltips are only used to visually display labels. $label = wp_strip_all_tags( $content, true ); $markup = sprintf( - '
' . - '' . - '' . + '
+ ' . $button . ' + ' . '%5$s' . '' . '
', @@ -508,11 +525,9 @@ function wp_get_tooltip_helper( $content, $args = array() ) { ); } else { $markup = sprintf( - '
' . - '' . - '' . + '
+ ' . $button . ' + ' . '%5$s' . '', + ) + ); + + $this->assertStringContainsString( 'aria-expanded="false"', $html, 'String contains attribute not supported in function.' ); + $this->assertStringContainsString( 'popovertarget', $html, 'String contains required attribute added by function.' ); + $this->assertStringNotContainsString( 'dashicons', $html, 'String does not contain content of default button.' ); + } + /** * Tests that the accessible labels are output and escaped in attributes. * From 69bf5ff8a918436cc5d37ad3a6ad0ad3d123ecbd Mon Sep 17 00:00:00 2001 From: Joe Dolson Date: Wed, 15 Jul 2026 16:47:12 -0500 Subject: [PATCH 05/19] Add styles to reduce highlighting of tooltips when on disabled buttons Provides a similar queue as the grayed out button. Non-sighted users will be notified that this button is disabled, sighted users have to get that from context: grayed out design & inference from the position of the box. I think that should be sufficient. --- src/wp-admin/css/wp-tooltip.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/wp-admin/css/wp-tooltip.css b/src/wp-admin/css/wp-tooltip.css index c7c937d7b45e1..0da2fc1003076 100644 --- a/src/wp-admin/css/wp-tooltip.css +++ b/src/wp-admin/css/wp-tooltip.css @@ -71,6 +71,11 @@ line-height: 1.4; } +.wp-tooltip:has(button[aria-disabled="true"],button[disabled="true"]) .wp-tooltip__bubble { + background: #50575e; + color: #c3c4c7; +} + .wp-tooltip:not(.wp-is-tooltip) .wp-tooltip__text { display: block; padding-inline-end: 24px; From 87bbb5d953658535e5bb96eb705f8ed9b3cdbb28 Mon Sep 17 00:00:00 2001 From: Joe Dolson Date: Wed, 15 Jul 2026 17:00:44 -0500 Subject: [PATCH 06/19] Need to process all buttons --- src/wp-includes/general-template.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index 5476d9e41efa9..54fc7a09163d8 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -494,17 +494,17 @@ function wp_get_tooltip_helper( $content, $args = array() ) { $button = ''; - if ( ! empty( $args['button'] ) ) { - $processor = new WP_HTML_Tag_Processor( $args['button'] ); - if ( $processor->next_tag( 'button' ) ) { - $processor->set_attribute( 'popovertarget', '%2$s' ); - $processor->add_class( 'wp-tooltip__toggle' ); - if ( 'tooltip' !== $args['type'] ) { - $processor->set_attribute( 'aria-haspopup', 'dialog' ); - } - $button = $processor->get_updated_html(); + $button = ( ! empty( $args['button'] ) ) ? $args['button'] : $button; + $processor = new WP_HTML_Tag_Processor( $button ); + if ( $processor->next_tag( 'button' ) ) { + $processor->set_attribute( 'popovertarget', '%2$s' ); + $processor->add_class( 'wp-tooltip__toggle' ); + if ( 'tooltip' !== $args['type'] ) { + $processor->set_attribute( 'aria-haspopup', 'dialog' ); } + + $button = $processor->get_updated_html(); } if ( 'tooltip' === $args['type'] ) { From 1332e477b2062f29a8d0000b334d04ee0b41bc78 Mon Sep 17 00:00:00 2001 From: Joe Dolson Date: Wed, 15 Jul 2026 17:13:22 -0500 Subject: [PATCH 07/19] Using the HTML API changes the order of button attributes --- tests/phpunit/tests/general/wpGetTooltip.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/general/wpGetTooltip.php b/tests/phpunit/tests/general/wpGetTooltip.php index e2ad6cbe45e1c..87402476044e0 100644 --- a/tests/phpunit/tests/general/wpGetTooltip.php +++ b/tests/phpunit/tests/general/wpGetTooltip.php @@ -29,7 +29,7 @@ public function test_wp_get_tooltip_returns_accessible_markup() { $tooltip = wp_get_tooltip( 'Helpful text.', array( 'id' => 'my-tip' ) ); // Toggle is a button that controls the popover and describes it. - $this->assertStringContainsString( ''; + + $button = ( ! empty( $args['button'] ) ) ? $args['button'] : $button; + $processor = new WP_HTML_Tag_Processor( $button ); + if ( $processor->next_tag( 'button' ) ) { + $processor->set_attribute( 'popovertarget', '%2$s' ); + $processor->add_class( 'wp-tooltip__toggle' ); + if ( 'tooltip' !== $args['type'] ) { + $processor->set_attribute( 'aria-haspopup', 'dialog' ); + } + $button = $processor->get_updated_html(); + } /* * The markup only uses phrasing content so it is valid when nested * in a phrasing context. Sectioning content (e.g. `div`, `dialog`) will @@ -501,11 +514,9 @@ function wp_get_tooltip_helper( $content, $args = array() ) { // Tooltips are only used to visually display labels. $label = wp_strip_all_tags( $content, true ); $markup = sprintf( - '' . - '' . - '' . + ' + ' . $button . ' + ' . '%5$s' . '' . '', @@ -522,11 +533,9 @@ function wp_get_tooltip_helper( $content, $args = array() ) { * attributes reproduce the accessible name and focus handling of the native element. */ $markup = sprintf( - '' . - '' . - '' . + ' + ' . $button . ' + ' . '%5$s' . ''; $move_up_args = array( 'id' => $box['id'] . '-handle-order-higher-description', 'button' => $move_up_button, ); - echo wp_get_tooltip( $move_up_text, $move_up_args ); + echo wp_get_tooltip( __( 'Move up' ), $move_up_args ); - $move_down_text = sprintf( - /* translators: %s: Meta box title. */ - __( 'Move %s box down' ), - $widget_title - ); $move_down_button = ''; $move_down_args = array( 'id' => $box['id'] . '-handle-order-lower-description', 'button' => $move_down_button, ); - echo wp_get_tooltip( $move_down_text, $move_down_args ); + echo wp_get_tooltip( __( 'Move down' ), $move_down_args ); - $show_hide_text = sprintf( - /* translators: %s: Hidden accessibility text. Meta box title. */ - __( 'Show or hide panel: %s' ), - $widget_title - ); $show_hide_button = ''; $show_hide_args = array( 'id' => $box['id'] . '-handlediv', 'button' => $show_hide_button, ); - echo wp_get_tooltip( $show_hide_text, $show_hide_args ); + echo wp_get_tooltip( __( 'Show or hide panel' ), $show_hide_args ); echo '
'; } From 4b5bb1295d5ac3e4a6aef40bd1762ad993a2ae1c Mon Sep 17 00:00:00 2001 From: Joe Dolson Date: Sun, 19 Jul 2026 19:24:42 -0500 Subject: [PATCH 11/19] Move default values to default args array --- src/wp-includes/general-template.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index 4cab85cae8d46..a1efc48f1d2c4 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -471,8 +471,8 @@ function wp_get_tooltip_helper( $content, $args = array() ) { } $defaults = array( - 'id' => '', - 'button' => '', + 'id' => wp_unique_id( 'wp-tooltip-' ), + 'button' => '', 'label' => __( 'Help' ), 'close_label' => __( 'Close' ), 'icon' => 'dashicons-editor-help', @@ -482,18 +482,12 @@ function wp_get_tooltip_helper( $content, $args = array() ) { $args = wp_parse_args( $args, $defaults ); - $id = '' !== $args['id'] ? $args['id'] : wp_unique_id( 'wp-tooltip-' ); - $classes = ( 'tooltip' === $args['type'] ) ? 'wp-tooltip wp-is-tooltip' : 'wp-tooltip wp-is-toggletip'; if ( '' !== $args['class'] ) { $classes .= ' ' . $args['class']; } - $icon = '' !== $args['icon'] ? ' ' . $args['icon'] : ''; - $button = ''; - + $icon = '' !== $args['icon'] ? ' ' . $args['icon'] : ''; $button = ( ! empty( $args['button'] ) ) ? $args['button'] : $button; $processor = new WP_HTML_Tag_Processor( $button ); if ( $processor->next_tag( 'button' ) ) { From a31c87cca68b881c3c8e3964efd9e4609cf2d483 Mon Sep 17 00:00:00 2001 From: Joe Dolson Date: Sun, 19 Jul 2026 19:27:33 -0500 Subject: [PATCH 12/19] $id now undefined --- src/wp-includes/general-template.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index a1efc48f1d2c4..697f531c00072 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -515,7 +515,7 @@ function wp_get_tooltip_helper( $content, $args = array() ) { '' . '', esc_attr( $classes ), - esc_attr( $id ), + esc_attr( $args['id'] ), esc_attr( $label ), esc_attr( $icon ), esc_html( $content ), @@ -537,7 +537,7 @@ function wp_get_tooltip_helper( $content, $args = array() ) { '' . '', esc_attr( $classes ), - esc_attr( $id ), + esc_attr( $args['id'] ), esc_attr( $args['label'] ), esc_attr( $icon ), esc_html( $content ), From 11955b5247259820b6a1aab3f6545b3a76cd3dc8 Mon Sep 17 00:00:00 2001 From: Joe Dolson Date: Sun, 19 Jul 2026 19:28:06 -0500 Subject: [PATCH 13/19] Don't override hover/focus when disabled. Toggle tip case not covered, as a toggle tip should never be disabled. --- src/wp-admin/css/wp-tooltip.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wp-admin/css/wp-tooltip.css b/src/wp-admin/css/wp-tooltip.css index c7c937d7b45e1..cf728860240b2 100644 --- a/src/wp-admin/css/wp-tooltip.css +++ b/src/wp-admin/css/wp-tooltip.css @@ -21,14 +21,14 @@ cursor: pointer; } -.wp-tooltip .wp-tooltip__toggle:hover, -.wp-tooltip .wp-tooltip__toggle:focus, +.wp-tooltip:not(:has(button[aria-disabled="true"],button[disabled="true"])) .wp-tooltip__toggle:hover, +.wp-tooltip:not(:has(button[aria-disabled="true"],button[disabled="true"])) .wp-tooltip__toggle:focus, .wp-tooltip .wp-tooltip__close:hover, .wp-tooltip .wp-tooltip__close:focus { color: var(--wp-admin-theme-color, #3858e9); } -.wp-tooltip .wp-tooltip__toggle:focus, +.wp-tooltip:not(:has(button[aria-disabled="true"],button[disabled="true"])) .wp-tooltip__toggle:focus, .wp-tooltip .wp-tooltip__close:focus { outline: 2px solid transparent; box-shadow: 0 0 0 var(--wp-admin-border-width-focus, 1.5px) var(--wp-admin-theme-color, #3858e9); From 7fd7bdbc2120db0458947f4f463fd770f0ea83cd Mon Sep 17 00:00:00 2001 From: Joe Dolson Date: Sun, 19 Jul 2026 19:28:55 -0500 Subject: [PATCH 14/19] Update template.php --- src/wp-admin/includes/template.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/template.php b/src/wp-admin/includes/template.php index 186e77a6be9ac..4e162e7c54acc 100644 --- a/src/wp-admin/includes/template.php +++ b/src/wp-admin/includes/template.php @@ -1374,7 +1374,7 @@ function do_meta_boxes( $screen, $context, $data_object ) { ++$i; // get_hidden_meta_boxes() doesn't apply in the block editor. $hidden_class = ( ! $screen->is_block_editor() && in_array( $box['id'], $hidden, true ) ) ? ' hide-if-js' : ''; - echo '
' . "\n"; + echo '
' . "\n"; echo '
'; echo '

'; From 3b78fe9a388f46ace661b6f696f5507091aad4f6 Mon Sep 17 00:00:00 2001 From: Andrea Fercia Date: Mon, 20 Jul 2026 17:42:38 +0200 Subject: [PATCH 15/19] Add tooltips to media table view switcher links. --- src/js/_enqueues/wp/wp-tooltip.js | 3 ++- src/wp-admin/includes/class-wp-list-table.php | 19 +++++++++++++------ src/wp-includes/general-template.php | 11 +++++++++-- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/js/_enqueues/wp/wp-tooltip.js b/src/js/_enqueues/wp/wp-tooltip.js index 5d9621c0a4e83..70fe2bfe7c1b2 100644 --- a/src/js/_enqueues/wp/wp-tooltip.js +++ b/src/js/_enqueues/wp/wp-tooltip.js @@ -14,8 +14,9 @@ let openTimeout; popovers.forEach( function( popover ) { - const trigger = /** @type {HTMLButtonElement|null} */ ( popover.querySelector( 'button.wp-tooltip__toggle' ) ); + const trigger = /** @type {HTMLButtonElement|HTMLAnchorElement|null} */ ( popover.querySelector( '.wp-tooltip__toggle' ) ); const panel = /** @type {HTMLSpanElement|null} */ ( popover.querySelector( 'span.wp-tooltip__bubble' ) ); + if ( ! trigger || ! panel ) { return; } diff --git a/src/wp-admin/includes/class-wp-list-table.php b/src/wp-admin/includes/class-wp-list-table.php index d32f08a438c60..e784b35c267af 100644 --- a/src/wp-admin/includes/class-wp-list-table.php +++ b/src/wp-admin/includes/class-wp-list-table.php @@ -809,6 +809,9 @@ protected function view_switcher( $current_mode ) {
modes as $mode => $title ) { $classes = array( 'view-' . $mode ); $aria_current = ''; @@ -818,14 +821,18 @@ protected function view_switcher( $current_mode ) { $aria_current = ' aria-current="page"'; } - printf( - "" . - "%s" . - "\n", + $switcher_link = sprintf( + "\n", esc_url( remove_query_arg( 'attachment-filter', add_query_arg( 'mode', $mode ) ) ), - implode( ' ', $classes ), - $title + esc_attr( $title ), + implode( ' ', $classes ) + ); + + $switcher_link_args = array( + 'id' => "view-switch-$mode-tooltip", + 'button' => $switcher_link, ); + echo wp_get_tooltip( $title, $switcher_link_args ); } ?>
diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index 697f531c00072..81daf126e1e9b 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -487,9 +487,10 @@ function wp_get_tooltip_helper( $content, $args = array() ) { $classes .= ' ' . $args['class']; } - $icon = '' !== $args['icon'] ? ' ' . $args['icon'] : ''; - $button = ( ! empty( $args['button'] ) ) ? $args['button'] : $button; + $icon = $args['icon']; + $button = $args['button']; $processor = new WP_HTML_Tag_Processor( $button ); + if ( $processor->next_tag( 'button' ) ) { $processor->set_attribute( 'popovertarget', '%2$s' ); $processor->add_class( 'wp-tooltip__toggle' ); @@ -498,6 +499,12 @@ function wp_get_tooltip_helper( $content, $args = array() ) { } $button = $processor->get_updated_html(); } + + if ( $processor->next_tag( 'a' ) && 'tooltip' === $args['type'] ) { + $processor->add_class( 'wp-tooltip__toggle' ); + $button = $processor->get_updated_html(); + } + /* * The markup only uses phrasing content so it is valid when nested * in a phrasing context. Sectioning content (e.g. `div`, `dialog`) will From 69c4ae84efe6a100fd74cac942f63e4c52ec99a4 Mon Sep 17 00:00:00 2001 From: Joe Dolson Date: Mon, 20 Jul 2026 16:46:20 -0500 Subject: [PATCH 16/19] Only add popovertarget to toggletipds Eliminates duplicate speaking by VoiceOver --- src/wp-includes/general-template.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index 81daf126e1e9b..9bfb8a75955d5 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -492,9 +492,9 @@ function wp_get_tooltip_helper( $content, $args = array() ) { $processor = new WP_HTML_Tag_Processor( $button ); if ( $processor->next_tag( 'button' ) ) { - $processor->set_attribute( 'popovertarget', '%2$s' ); $processor->add_class( 'wp-tooltip__toggle' ); if ( 'tooltip' !== $args['type'] ) { + $processor->set_attribute( 'popovertarget', '%2$s' ); $processor->set_attribute( 'aria-haspopup', 'dialog' ); } $button = $processor->get_updated_html(); From 8f593cc2a56ad888b2e75975a32af0f79e0f431e Mon Sep 17 00:00:00 2001 From: Joe Dolson Date: Mon, 20 Jul 2026 16:48:26 -0500 Subject: [PATCH 17/19] Update tests with new expectations --- tests/phpunit/tests/general/wpGetTooltip.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/phpunit/tests/general/wpGetTooltip.php b/tests/phpunit/tests/general/wpGetTooltip.php index c1bceee4596d5..1e8746bf4068e 100644 --- a/tests/phpunit/tests/general/wpGetTooltip.php +++ b/tests/phpunit/tests/general/wpGetTooltip.php @@ -28,9 +28,10 @@ public function test_wp_get_tooltip_returns_empty_string_without_content() { public function test_wp_get_tooltip_returns_accessible_markup() { $tooltip = wp_get_tooltip( 'Helpful text.', array( 'id' => 'my-tip' ) ); - // Toggle is a button that controls the popover and describes it. - $this->assertStringContainsString( '