-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Insert block template skip link via HTML API, minify CSS, remove JS. #10676
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
Insert block template skip link via HTML API, minify CSS, remove JS. #10676
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
westonruter
left a comment
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.
Great to see this!
Co-authored-by: Weston Ruter <westonruter@gmail.com>
Co-authored-by: Weston Ruter <westonruter@gmail.com>
Co-authored-by: Weston Ruter <westonruter@gmail.com>
Co-authored-by: Weston Ruter <westonruter@gmail.com>
Co-authored-by: Weston Ruter <westonruter@gmail.com>
…into refact/insert-skip-link-via-html
This reverts commit b1afb70.
Co-authored-by: Dennis Snell <dmsnell@git.wordpress.org>
Fixed in 61711e2. The problem is that some other test in the test suite is not cleaning up after itself. When the test runs in isolation, it's not a block template. But some other test is leaving the block template status in place. Previously the code was already ignoring |
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.
Pull request overview
This pull request refactors the block template skip link implementation to move it from client-side JavaScript injection to server-side HTML processing using WordPress's HTML API. The skip link is now inserted during template rendering rather than dynamically added via JavaScript on page load.
Key changes:
- Server-side skip link insertion via HTML API replaces JavaScript DOM manipulation
- New CSS file created with skip link styles (to be minified by build process)
- JavaScript removed from
wp_enqueue_block_template_skip_link()function
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/wp-includes/block-template.php | Adds _block_template_add_skip_link() function to insert skip links server-side and updates get_the_block_template_html() to call it |
| src/wp-includes/theme-templates.php | Removes JavaScript insertion code and updates function to only enqueue CSS styles |
| src/wp-includes/script-loader.php | Registers new CSS file for skip link styles with minification support |
| src/wp-includes/css/wp-block-template-skip-link.css | New CSS file containing skip link styles (previously inline) |
| tests/phpunit/tests/block-template.php | Adds tests for skip link insertion behavior in template HTML |
| tests/phpunit/tests/block-template-utils.php | Adds comprehensive tests for various skip link edge cases |
| tests/phpunit/tests/template.php | Updates ignored styles list to include new skip link CSS handle |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
dmsnell
left a comment
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.
Looks nice to me from the HTML API perspective. Thanks @rutviksavsani and @westonruter for this work; should be a nice uplift for the initial page render!
@westonruter I‘ll leave the next step to you. It seems like things are fine now with the tests, but since I didn’t focus much on those I don’t want to eagerly merge anything.
|
I cURL'ed the Sample Page before and after the change and here's the diff (after running both the before and after through --- before.html 2026-01-10 20:32:07
+++ after.html 2026-01-10 20:32:16
@@ -2478,7 +2478,8 @@
width: auto;
z-index: 100000;
}
- /*# sourceURL=wp-block-template-skip-link-inline-css */
+
+ /*# sourceURL=/wp-includes/css/wp-block-template-skip-link.css */
</style>
<style id="twentytwentyfive-style-inline-css">
/*
@@ -2628,6 +2629,12 @@
<body
class="wp-singular page-template-default page page-id-2 wp-embed-responsive wp-theme-twentytwentyfive"
>
+ <a
+ class="skip-link screen-reader-text"
+ id="wp-skip-link"
+ href="#wp--skip-link--target"
+ >Skip to content</a
+ >
<div class="wp-site-blocks">
<header class="wp-block-template-part">
<div
@@ -2765,6 +2772,7 @@
</header>
<main
+ id="wp--skip-link--target"
class="wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained"
style="margin-top: var(--wp--preset--spacing--60)"
>
@@ -3013,49 +3021,6 @@
fetchpriority="low"
data-wp-router-options='{"loadOnClientNavigation":true}'
></script>
- <script id="wp-block-template-skip-link-js-after">
- (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 = "Skip to content";
-
- // Inject the skip link.
- sibling.parentElement.insertBefore(skipLink, sibling);
- })();
-
- //# sourceURL=wp-block-template-skip-link-js-after
- </script>
<script id="wp-emoji-settings" type="application/json">
{
"baseUrl": "https://s.w.org/images/core/emoji/17.0.2/72x72/",I just realized another benefit to doing this: the skip link works even when JavaScript is turned off. |
|
I just noticed that RTL versions of the CSS files are being generated. Here are all the versions now after a build:
Previously there was no RTL variation in the CSS. So maybe this was a bug. But it doesn't seem the RTL version is currently getting registered so it is being unused. We need to double check that. |
|
OK, with 357ea8a I now get the skip link positioned on the right as expected, but only when <link rel='stylesheet' id='wp-block-template-skip-link-rtl-css' href='http://localhost:8000/wp-includes/css/wp-block-template-skip-link-rtl.min.css?ver=7.0-alpha-61215-src' media='all' />But when <style id="wp-block-template-skip-link-inline-css">
.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;
}
/*# sourceURL=/wp-includes/css/wp-block-template-skip-link.css */
</style>Note the erroneous It turns out this is a known issue which doesn't have to be fixed in this PR: Core-61625. It's already something on my radar, so I'll follow up on that now that I've seen another example of how to reproduce it. At least it's fixed when stylesheet inlining is disabled. |
|
The diff above in #10676 (comment) was with --- /tmp/before.html 2026-01-10 21:41:27
+++ /tmp/after.html 2026-01-10 21:41:37
@@ -97,35 +97,9 @@
/*# sourceURL=core-block-supports-inline-css */
</style>
<style id="wp-block-template-skip-link-inline-css">
-
- .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;
- }
-/*# sourceURL=wp-block-template-skip-link-inline-css */
+/*! 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}
+/*# sourceURL=/wp-includes/css/wp-block-template-skip-link.min.css */
</style>
<style id="twentytwentyfive-style-inline-css">
a{text-decoration-thickness:1px!important;text-underline-offset:.1em}:where(.wp-site-blocks :focus){outline-width:2px;outline-style:solid}.wp-block-navigation .wp-block-navigation-submenu .wp-block-navigation-item:not(:last-child){margin-bottom:3px}.wp-block-navigation .wp-block-navigation-item .wp-block-navigation-item__content{outline-offset:4px}.wp-block-navigation .wp-block-navigation-item ul.wp-block-navigation__submenu-container .wp-block-navigation-item__content{outline-offset:0}blockquote,caption,figcaption,h1,h2,h3,h4,h5,h6,p{text-wrap:pretty}.more-link{display:block}:where(pre){overflow-x:auto}
@@ -147,7 +121,7 @@
<body class="wp-singular page-template-default page page-id-2 wp-embed-responsive wp-theme-twentytwentyfive">
-<div class="wp-site-blocks"><header class="wp-block-template-part">
+<a class="skip-link screen-reader-text" id="wp-skip-link" href="#wp--skip-link--target">Skip to content</a><div class="wp-site-blocks"><header class="wp-block-template-part">
<div class="wp-block-group alignfull is-layout-flow wp-block-group-is-layout-flow">
<div class="wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained">
@@ -198,7 +172,7 @@
</header>
-<main class="wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained" style="margin-top:var(--wp--preset--spacing--60)">
+<main id="wp--skip-link--target" class="wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained" style="margin-top:var(--wp--preset--spacing--60)">
<div class="wp-block-group alignfull has-global-padding is-layout-constrained wp-block-group-is-layout-constrained" style="padding-top:var(--wp--preset--spacing--60);padding-bottom:var(--wp--preset--spacing--60)">
@@ -298,49 +272,6 @@
{"prefetch":[{"source":"document","where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":["/wp-*.php","/wp-admin/*","/wp-content/uploads/*","/wp-content/*","/wp-content/plugins/*","/wp-content/themes/twentytwentyfive/*","/*\\?(.+)"]}},{"not":{"selector_matches":"a[rel~=\"nofollow\"]"}},{"not":{"selector_matches":".no-prefetch, .no-prefetch a"}}]},"eagerness":"conservative"}]}
</script>
<script type="module" src="http://localhost:8000/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js?ver=7437ed5c45ee57daf02c" id="@wordpress/block-library/navigation/view-js-module" fetchpriority="low" data-wp-router-options="{"loadOnClientNavigation":true}"></script>
-<script id="wp-block-template-skip-link-js-after">
- ( 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 = 'Skip to content';
-
- // Inject the skip link.
- sibling.parentElement.insertBefore( skipLink, sibling );
- }() );
-
-//# sourceURL=wp-block-template-skip-link-js-after
-</script>
<script id="wp-emoji-settings" type="application/json">
{"baseUrl":"https://s.w.org/images/core/emoji/17.0.2/72x72/","ext":".png","svgUrl":"https://s.w.org/images/core/emoji/17.0.2/svg/","svgExt":".svg","source":{"concatemoji":"http://localhost:8000/wp-includes/js/wp-emoji-release.min.js?ver=7.0-alpha-61215-src"}}
</script> |
…nk was unhooked from wp_enqueue_scripts
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.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
… 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 #10676 Follow-up to [56932], [51003]. Props rutviksavsani, westonruter, dmsnell, whiteshadow01, Slieptsov. See #59505, #53176. Fixes #64361. git-svn-id: https://develop.svn.wordpress.org/trunk@61469 602fd350-edb4-49c9-b593-d223f7449a82
… 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 WordPress/wordpress-develop#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
… 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 WordPress/wordpress-develop#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: https://core.svn.wordpress.org/trunk@60781 1a063a9b-81f0-0310-95a4-ce76da25c4cd


Move the block template skip link from client-side injection to server-side HTML processing using the HTML API, while keeping the existing accessibility behaviour and minifying the CSS.
What this changes
Adds
_block_template_skip_link_markup()to process the block template HTML:<main>has an id (addswp--skip-link--targetif missing).<a id="wp-skip-link" class="skip-link screen-reader-text">before.wp-site-blocks.<main>or when a skip link already exists.Updates
get_the_block_template_html()to run the rendered template through the new helper.Refactors
wp_enqueue_block_template_skip_link()to:Preserves backward compatibility gating via
the_block_template_skip_linkand block-templates theme support.Add tests for the new function as well.
Ticket
Trac ticket: https://core.trac.wordpress.org/ticket/64361
This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.