Commit 87bb5634ca for wordpress.org
commit 87bb5634ca3781437a193d04d02cbfb05c35016e
Author: Weston Ruter <weston@xwp.co>
Date: Sun Jan 11 06:36:49 2026 +0000
Themes: Use `WP_HTML_Tag_Processor` to insert the block template skip link instead of JavaScript.
* The skip link now works when JavaScript is turned off.
* By removing the script, the amount of JavaScript sent to the client is reduced for a very marginal performance improvement.
* A new `wp-block-template-skip-link` stylesheet is registered, with minification and `path` data for inlining.
* The CSS for the skip link now has an RTL version generated, although it is not yet served when the styles are inlined. See #61625.
* The `wp_enqueue_block_template_skip_link()` function now exclusively enqueues the stylesheet since the script is removed.
* For backwards-compatibility, the skip link will continue to be omitted if `the_block_template_skip_link()` is unhooked from the `wp_footer` action or `wp_enqueue_block_template_skip_link()` is unhooked from `wp_enqueue_scripts`.
Developed in https://github.com/WordPress/wordpress-develop/pull/10676
Follow-up to [56932], [51003].
Props rutviksavsani, westonruter, dmsnell, whiteshadow01, Slieptsov.
See #59505, #53176.
Fixes #64361.
Built from https://develop.svn.wordpress.org/trunk@61469
git-svn-id: http://core.svn.wordpress.org/trunk@60781 1a063a9b-81f0-0310-95a4-ce76da25c4cd
diff --git a/wp-includes/block-template.php b/wp-includes/block-template.php
index df3735b238..88f9c4384a 100644
--- a/wp-includes/block-template.php
+++ b/wp-includes/block-template.php
@@ -301,7 +301,102 @@ function get_the_block_template_html() {
// Wrap block template in .wp-site-blocks to allow for specific descendant styles
// (e.g. `.wp-site-blocks > *`).
- return '<div class="wp-site-blocks">' . $content . '</div>';
+ $template_html = '<div class="wp-site-blocks">' . $content . '</div>';
+
+ // Back-compat for plugins that disable functionality by unhooking one of these actions.
+ if (
+ ! has_action( 'wp_footer', 'the_block_template_skip_link' ) ||
+ ! has_action( 'wp_enqueue_scripts', 'wp_enqueue_block_template_skip_link' )
+ ) {
+ return $template_html;
+ }
+
+ return _block_template_add_skip_link( $template_html );
+}
+
+/**
+ * Inserts the block template skip-link into the template HTML.
+ *
+ * When a `MAIN` element exists in the template, this function will ensure
+ * that the element contains an `id` attribute, and it will insert a link to
+ * that `MAIN` element before the first `DIV.wp-site-blocks` element, which
+ * is the wrapper for all blocks in a block template as constructed by
+ * {@see get_the_block_template_html()}.
+ *
+ * Example:
+ *
+ * // Input.
+ * <div class="wp-site-blocks">
+ * <nav>...</nav>
+ * <main>
+ * <h2>...
+ *
+ * // Output.
+ * <a href="#wp--skip-link--target" id="wp-skip-link" class="...">
+ * <div class="wp-site-blocks">
+ * <nav>...</nav>
+ * <main id="wp--skip-link--target">
+ * <h2>...
+ *
+ * When the `MAIN` element already contains a non-empty `id` value it will be
+ * used instead of the default skip-link id.
+ *
+ * @access private
+ * @since 7.0.0
+ *
+ * @param string $template_html Block template markup.
+ * @return string Modified markup with skip link when applicable.
+ */
+function _block_template_add_skip_link( string $template_html ): string {
+ // Anonymous subclass of WP_HTML_Tag_Processor to access protected bookmark spans.
+ $processor = new class( $template_html ) extends WP_HTML_Tag_Processor {
+ /**
+ * Inserts text before the current token.
+ *
+ * @param string $text Text to insert.
+ */
+ public function insert_before( string $text ) {
+ $this->set_bookmark( 'here' );
+ $this->lexical_updates[] = new WP_HTML_Text_Replacement( $this->bookmarks['here']->start, 0, $text );
+ }
+ };
+
+ // Find and bookmark the first DIV.wp-site-blocks.
+ if (
+ ! $processor->next_tag(
+ array(
+ 'tag_name' => 'DIV',
+ 'class_name' => 'wp-site-blocks',
+ )
+ )
+ ) {
+ return $template_html;
+ }
+ $processor->set_bookmark( 'skip_link_insertion_point' );
+
+ // Ensure the MAIN element has an ID.
+ if ( ! $processor->next_tag( 'MAIN' ) ) {
+ return $template_html;
+ }
+
+ $skip_link_target_id = $processor->get_attribute( 'id' );
+ if ( ! is_string( $skip_link_target_id ) || '' === $skip_link_target_id ) {
+ $skip_link_target_id = 'wp--skip-link--target';
+ $processor->set_attribute( 'id', $skip_link_target_id );
+ }
+
+ // Seek back to the bookmarked insertion point.
+ $processor->seek( 'skip_link_insertion_point' );
+
+ $skip_link = sprintf(
+ '<a class="skip-link screen-reader-text" id="wp-skip-link" href="%s">%s</a>',
+ esc_url( '#' . $skip_link_target_id ),
+ /* translators: Hidden accessibility text. */
+ esc_html__( 'Skip to content' )
+ );
+ $processor->insert_before( $skip_link );
+
+ return $processor->get_updated_html();
}
/**
diff --git a/wp-includes/css/dist/index.php b/wp-includes/css/dist/index.php
index c1720de876..1b98c542ab 100644
--- a/wp-includes/css/dist/index.php
+++ b/wp-includes/css/dist/index.php
@@ -42,16 +42,16 @@ return array(
'path' => 'patterns/style',
'dependencies' => array('wp-block-editor', 'wp-components'),
),
- array(
- 'handle' => 'wp-components',
- 'path' => 'components/style',
- 'dependencies' => array(),
- ),
array(
'handle' => 'wp-format-library',
'path' => 'format-library/style',
'dependencies' => array('wp-block-editor', 'wp-components'),
),
+ array(
+ 'handle' => 'wp-components',
+ 'path' => 'components/style',
+ 'dependencies' => array(),
+ ),
array(
'handle' => 'wp-media-utils',
'path' => 'media-utils/style',
@@ -67,11 +67,6 @@ return array(
'path' => 'customize-widgets/style',
'dependencies' => array('wp-block-editor', 'wp-block-library', 'wp-components', 'wp-media-utils', 'wp-preferences', 'wp-widgets'),
),
- array(
- 'handle' => 'wp-block-library',
- 'path' => 'block-library/style',
- 'dependencies' => array('wp-block-editor', 'wp-components', 'wp-patterns'),
- ),
array(
'handle' => 'wp-edit-widgets',
'path' => 'edit-widgets/style',
@@ -82,19 +77,24 @@ return array(
'path' => 'edit-post/style',
'dependencies' => array('wp-block-editor', 'wp-block-library', 'wp-commands', 'wp-components', 'wp-editor', 'wp-preferences', 'wp-widgets'),
),
+ array(
+ 'handle' => 'wp-block-library',
+ 'path' => 'block-library/style',
+ 'dependencies' => array('wp-block-editor', 'wp-components', 'wp-patterns'),
+ ),
array(
'handle' => 'wp-editor',
'path' => 'editor/style',
'dependencies' => array('wp-block-editor', 'wp-commands', 'wp-components', 'wp-media-utils', 'wp-patterns', 'wp-preferences'),
),
- array(
- 'handle' => 'wp-block-editor',
- 'path' => 'block-editor/style',
- 'dependencies' => array('wp-commands', 'wp-components', 'wp-preferences'),
- ),
array(
'handle' => 'wp-edit-site',
'path' => 'edit-site/style',
'dependencies' => array('wp-block-editor', 'wp-block-library', 'wp-commands', 'wp-components', 'wp-editor', 'wp-patterns', 'wp-preferences', 'wp-widgets'),
),
+ array(
+ 'handle' => 'wp-block-editor',
+ 'path' => 'block-editor/style',
+ 'dependencies' => array('wp-commands', 'wp-components', 'wp-preferences'),
+ ),
);
diff --git a/wp-includes/css/wp-block-template-skip-link-rtl.css b/wp-includes/css/wp-block-template-skip-link-rtl.css
new file mode 100644
index 0000000000..17a074696b
--- /dev/null
+++ b/wp-includes/css/wp-block-template-skip-link-rtl.css
@@ -0,0 +1,28 @@
+/*! This file is auto-generated */
+.skip-link.screen-reader-text {
+ border: 0;
+ clip-path: inset(50%);
+ height: 1px;
+ margin: -1px;
+ overflow: hidden;
+ padding: 0;
+ position: absolute !important;
+ width: 1px;
+ word-wrap: normal !important;
+}
+
+.skip-link.screen-reader-text:focus {
+ background-color: #eee;
+ clip-path: none;
+ color: #444;
+ display: block;
+ font-size: 1em;
+ height: auto;
+ right: 5px;
+ line-height: normal;
+ padding: 15px 23px 14px;
+ text-decoration: none;
+ top: 5px;
+ width: auto;
+ z-index: 100000;
+}
diff --git a/wp-includes/css/wp-block-template-skip-link-rtl.min.css b/wp-includes/css/wp-block-template-skip-link-rtl.min.css
new file mode 100644
index 0000000000..e7e1177b05
--- /dev/null
+++ b/wp-includes/css/wp-block-template-skip-link-rtl.min.css
@@ -0,0 +1,2 @@
+/*! This file is auto-generated */
+.skip-link.screen-reader-text{border:0;clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute!important;width:1px;word-wrap:normal!important}.skip-link.screen-reader-text:focus{background-color:#eee;clip-path:none;color:#444;display:block;font-size:1em;height:auto;right:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}
\ No newline at end of file
diff --git a/wp-includes/css/wp-block-template-skip-link.css b/wp-includes/css/wp-block-template-skip-link.css
new file mode 100644
index 0000000000..4176599ad0
--- /dev/null
+++ b/wp-includes/css/wp-block-template-skip-link.css
@@ -0,0 +1,27 @@
+.skip-link.screen-reader-text {
+ border: 0;
+ clip-path: inset(50%);
+ height: 1px;
+ margin: -1px;
+ overflow: hidden;
+ padding: 0;
+ position: absolute !important;
+ width: 1px;
+ word-wrap: normal !important;
+}
+
+.skip-link.screen-reader-text:focus {
+ background-color: #eee;
+ clip-path: none;
+ color: #444;
+ display: block;
+ font-size: 1em;
+ height: auto;
+ left: 5px;
+ line-height: normal;
+ padding: 15px 23px 14px;
+ text-decoration: none;
+ top: 5px;
+ width: auto;
+ z-index: 100000;
+}
diff --git a/wp-includes/css/wp-block-template-skip-link.min.css b/wp-includes/css/wp-block-template-skip-link.min.css
new file mode 100644
index 0000000000..0e7b5e49cd
--- /dev/null
+++ b/wp-includes/css/wp-block-template-skip-link.min.css
@@ -0,0 +1,2 @@
+/*! This file is auto-generated */
+.skip-link.screen-reader-text{border:0;clip-path:inset(50%);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute!important;width:1px;word-wrap:normal!important}.skip-link.screen-reader-text:focus{background-color:#eee;clip-path:none;color:#444;display:block;font-size:1em;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}
\ No newline at end of file
diff --git a/wp-includes/js/dist/script-modules/index.php b/wp-includes/js/dist/script-modules/index.php
index 1ebf6bd042..8497ba890e 100644
--- a/wp-includes/js/dist/script-modules/index.php
+++ b/wp-includes/js/dist/script-modules/index.php
@@ -17,6 +17,11 @@ return array(
'path' => 'interactivity-router/full-page',
'asset' => 'interactivity-router/full-page.min.asset.php',
),
+ array(
+ 'id' => '@wordpress/a11y',
+ 'path' => 'a11y/index',
+ 'asset' => 'a11y/index.min.asset.php',
+ ),
array(
'id' => '@wordpress/interactivity',
'path' => 'interactivity/index',
@@ -33,9 +38,9 @@ return array(
'asset' => 'latex-to-mathml/loader.min.asset.php',
),
array(
- 'id' => '@wordpress/a11y',
- 'path' => 'a11y/index',
- 'asset' => 'a11y/index.min.asset.php',
+ 'id' => '@wordpress/core-abilities',
+ 'path' => 'core-abilities/index',
+ 'asset' => 'core-abilities/index.min.asset.php',
),
array(
'id' => '@wordpress/abilities',
@@ -52,11 +57,6 @@ return array(
'path' => 'edit-site-init/index',
'asset' => 'edit-site-init/index.min.asset.php',
),
- array(
- 'id' => '@wordpress/core-abilities',
- 'path' => 'core-abilities/index',
- 'asset' => 'core-abilities/index.min.asset.php',
- ),
array(
'id' => '@wordpress/lazy-editor',
'path' => 'lazy-editor/index',
diff --git a/wp-includes/script-loader.php b/wp-includes/script-loader.php
index 2d1f49ff95..2946f19656 100644
--- a/wp-includes/script-loader.php
+++ b/wp-includes/script-loader.php
@@ -1605,6 +1605,9 @@ function wp_default_styles( $styles ) {
$styles->add( 'wp-pointer', "/wp-includes/css/wp-pointer$suffix.css", array( 'dashicons' ) );
$styles->add( 'customize-preview', "/wp-includes/css/customize-preview$suffix.css", array( 'dashicons' ) );
$styles->add( 'wp-empty-template-alert', "/wp-includes/css/wp-empty-template-alert$suffix.css" );
+ $skip_link_style_path = WPINC . "/css/wp-block-template-skip-link$suffix.css";
+ $styles->add( 'wp-block-template-skip-link', "/$skip_link_style_path" );
+ $styles->add_data( 'wp-block-template-skip-link', 'path', ABSPATH . $skip_link_style_path );
// External libraries and friends.
$styles->add( 'imgareaselect', '/wp-includes/js/imgareaselect/imgareaselect.css', array(), '0.9.8' );
@@ -1800,6 +1803,7 @@ function wp_default_styles( $styles ) {
'media-views',
'wp-pointer',
'wp-jquery-ui-dialog',
+ 'wp-block-template-skip-link',
// Package styles.
'wp-reset-editor-styles',
'wp-editor-classic-layout-styles',
diff --git a/wp-includes/theme-templates.php b/wp-includes/theme-templates.php
index eed0fb9b2b..301820f78f 100644
--- a/wp-includes/theme-templates.php
+++ b/wp-includes/theme-templates.php
@@ -99,10 +99,11 @@ function wp_filter_wp_template_unique_post_slug( $override_slug, $slug, $post_id
}
/**
- * Enqueues the skip-link script & styles.
+ * Enqueues the skip-link styles.
*
* @access private
* @since 6.4.0
+ * @since 7.0.0 A script is no longer printed in favor of being added via {@see _block_template_add_skip_link()}.
*
* @global string $_wp_current_template_content
*/
@@ -125,96 +126,7 @@ function wp_enqueue_block_template_skip_link() {
return;
}
- $skip_link_styles = '
- .skip-link.screen-reader-text {
- border: 0;
- clip-path: inset(50%);
- height: 1px;
- margin: -1px;
- overflow: hidden;
- padding: 0;
- position: absolute !important;
- width: 1px;
- word-wrap: normal !important;
- }
-
- .skip-link.screen-reader-text:focus {
- background-color: #eee;
- clip-path: none;
- color: #444;
- display: block;
- font-size: 1em;
- height: auto;
- left: 5px;
- line-height: normal;
- padding: 15px 23px 14px;
- text-decoration: none;
- top: 5px;
- width: auto;
- z-index: 100000;
- }';
-
- $handle = 'wp-block-template-skip-link';
-
- /**
- * Print the skip-link styles.
- */
- wp_register_style( $handle, false );
- wp_add_inline_style( $handle, $skip_link_styles );
- wp_enqueue_style( $handle );
-
- /**
- * Enqueue the skip-link script.
- */
- ob_start();
- ?>
- <script>
- ( function() {
- var skipLinkTarget = document.querySelector( 'main' ),
- sibling,
- skipLinkTargetID,
- skipLink;
-
- // Early exit if a skip-link target can't be located.
- if ( ! skipLinkTarget ) {
- return;
- }
-
- /*
- * Get the site wrapper.
- * The skip-link will be injected in the beginning of it.
- */
- sibling = document.querySelector( '.wp-site-blocks' );
-
- // Early exit if the root element was not found.
- if ( ! sibling ) {
- return;
- }
-
- // Get the skip-link target's ID, and generate one if it doesn't exist.
- skipLinkTargetID = skipLinkTarget.id;
- if ( ! skipLinkTargetID ) {
- skipLinkTargetID = 'wp--skip-link--target';
- skipLinkTarget.id = skipLinkTargetID;
- }
-
- // Create the skip link.
- skipLink = document.createElement( 'a' );
- skipLink.classList.add( 'skip-link', 'screen-reader-text' );
- skipLink.id = 'wp-skip-link';
- skipLink.href = '#' + skipLinkTargetID;
- skipLink.innerText = '<?php /* translators: Hidden accessibility text. Do not use HTML entities ( , etc.). */ esc_html_e( 'Skip to content' ); ?>';
-
- // Inject the skip link.
- sibling.parentElement.insertBefore( skipLink, sibling );
- }() );
- </script>
- <?php
- $skip_link_script = wp_remove_surrounding_empty_script_tags( ob_get_clean() );
- $script_handle = 'wp-block-template-skip-link';
- wp_register_script( $script_handle, false, array(), false, array( 'in_footer' => true ) );
- wp_add_inline_script( $script_handle, $skip_link_script );
- wp_enqueue_script( $script_handle );
+ wp_enqueue_style( 'wp-block-template-skip-link' );
}
/**
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 2d03a6b1ca..6fb6079e7f 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
-$wp_version = '7.0-alpha-61468';
+$wp_version = '7.0-alpha-61469';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.