#64361 closed enhancement (fixed)
Leverage HTML API to implement block template skip link
| Reported by: |
|
Owned by: |
|
|---|---|---|---|
| Milestone: | 7.0 | Priority: | normal |
| Severity: | normal | Version: | 5.8 |
| Component: | Themes | Keywords: | has-patch has-unit-tests |
| Focuses: | accessibility, javascript, performance | Cc: |
Description (last modified by )
When the_block_template_skip_link() (now wp_enqueue_block_template_skip_link()) was introduced in #53176 there was no HTML API which necessitated JavaScript to add the skip link. This can now be revisited to leverage WP_HTML_Tag_Processor to add the skip link.
The inline JavaScript can then be eliminated.
The CSS should also be minified.
Original description:
Can skip link CSS/JS be minified?
Change History (25)
#1
@
2 months ago
- Component changed from General to Themes
- Description modified (diff)
- Focuses javascript performance added
- Milestone changed from Awaiting Review to 7.0
- Summary changed from Skip Link Minification to Leverage HTML API to implement block template skip link
- Version set to 5.8
#5
@
2 months ago
The HTML API doesn't allow inserting elements into the DOM, only modifying the existing ones. How do we plan on inserting a new anchor tag as we are doing it in the javascript right now using the HTML API?
#6
@
2 months ago
@whiteshadow01 It does, but not via the public interface. You can see an example of this in wp_hoist_late_printed_styles(). See these lines.
This ticket was mentioned in PR #10676 on WordPress/wordpress-develop by @rutviksavsani.
7 weeks ago
#8
- Keywords has-patch has-unit-tests added; needs-patch removed
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:- Ensures the first
<main>has an id (addswp--skip-link--targetif missing). - Inserts a single
<a id="wp-skip-link" class="skip-link screen-reader-text">before.wp-site-blocks. - Skips insertion when there is no
<main>or when a skip link already exists.
- Ensures the first
- Updates
get_the_block_template_html()to run the rendered template through the new helper. - Refactors
wp_enqueue_block_template_skip_link()to:- Remove the old JS-based DOM injection.
- Minify the skip-link CSS as an inline style.
- 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
@westonruter commented on PR #10676:
6 weeks ago
#10
@rutviksavsani Could you address that great feedback from Dennis?
@rutviksavsani commented on PR #10676:
6 weeks ago
#11
@westonruter @dmsnell I have implemented the suggestions with best of my understandings, Let me know if I missed anything.
@rutviksavsani commented on PR #10676:
6 weeks ago
#12
@dmsnell I have added the changes and looks good to me with what we wanted to achieve. Let me know if it looks good to you.
@westonruter commented on PR #10676:
6 weeks ago
#13
I'm confused why the Tests_Template::test_wp_hoist_late_printed_styles are failing. They pass for my locally.
I'm also seeing a problem on the frontend with how the sourceURL comment is being constructed for the new style. I see:
/*# sourceURL=http://wp-includes/css/wp-block-template-skip-link.css */
@rutviksavsani commented on PR #10676:
6 weeks ago
#14
I'm confused why the
Tests_Template::test_wp_hoist_late_printed_stylesare failing. They pass for my locally.
I'm also seeing a problem on the frontend with how the
sourceURLcomment is being constructed for the new style. I see:
/*# sourceURL=http://wp-includes/css/wp-block-template-skip-link.css */
Look's okay to me on local as well for the sourceURL. Tests failure I am not completely sure of on what it can be but it's failing on other PRs on the repo as well.
@westonruter commented on PR #10676:
6 weeks ago
#15
Tests failure I am not completely sure of on what it can be but it's failing on other PRs on the repo as well.
I'm not seeing test failures for the latest commit in trunk: https://github.com/WordPress/wordpress-develop/actions/runs/20841211620/job/59875830100
No test failures in this PR either: https://github.com/WordPress/wordpress-develop/pull/10694
Something is wrong with that Tests_Template::test_wp_hoist_late_printed_styles test. I'll have to debug it tomorrow.
@westonruter commented on PR #10676:
6 weeks ago
#16
Look's okay to me on local as well for the sourceURL.
I fixed this at least in 350b57c.
This now matches the existing code better:
I now see:
/*# sourceURL=/wp-includes/css/wp-block-template-skip-link.css */
@westonruter commented on PR #10676:
5 weeks ago
#17
Something is wrong with that
Tests_Template::test_wp_hoist_late_printed_stylestest. I'll have to debug it tomorrow.
Fixed in 61711e2716742d44a86c5b26477cad4d74edac83. 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 wp-block-template-skip-link-inline-css for this reason. It was failing for wp-block-template-skip-link-css because now since the stylesheet is actually in the filesystem, it can be conditionally _not_ inlined, as is being done in the tests.
@westonruter commented on PR #10676:
5 weeks ago
#19
I cURL'ed the Sample Page before and after the change and here's the diff:
-
.html
old new 2478 2478 width: auto; 2479 2479 z-index: 100000; 2480 2480 } 2481 /*# sourceURL=wp-block-template-skip-link-inline-css */ 2481 2482 /*# sourceURL=/wp-includes/css/wp-block-template-skip-link.css */ 2482 2483 </style> 2483 2484 <style id="twentytwentyfive-style-inline-css"> 2484 2485 /* … … 2628 2629 <body 2629 2630 class="wp-singular page-template-default page page-id-2 wp-embed-responsive wp-theme-twentytwentyfive" 2630 2631 > 2632 <a 2633 class="skip-link screen-reader-text" 2634 id="wp-skip-link" 2635 href="#wp--skip-link--target" 2636 >Skip to content</a 2637 > 2631 2638 <div class="wp-site-blocks"> 2632 2639 <header class="wp-block-template-part"> 2633 2640 <div … … 2765 2772 </header> 2766 2773 2767 2774 <main 2775 id="wp--skip-link--target" 2768 2776 class="wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained" 2769 2777 style="margin-top: var(--wp--preset--spacing--60)" 2770 2778 > … … 3013 3021 fetchpriority="low" 3014 3022 data-wp-router-options='{"loadOnClientNavigation":true}' 3015 3023 ></script> 3016 <script id="wp-block-template-skip-link-js-after">3017 (function () {3018 var skipLinkTarget = document.querySelector("main"),3019 sibling,3020 skipLinkTargetID,3021 skipLink;3022 3023 // Early exit if a skip-link target can't be located.3024 if (!skipLinkTarget) {3025 return;3026 }3027 3028 /*3029 * Get the site wrapper.3030 * The skip-link will be injected in the beginning of it.3031 */3032 sibling = document.querySelector(".wp-site-blocks");3033 3034 // Early exit if the root element was not found.3035 if (!sibling) {3036 return;3037 }3038 3039 // Get the skip-link target's ID, and generate one if it doesn't exist.3040 skipLinkTargetID = skipLinkTarget.id;3041 if (!skipLinkTargetID) {3042 skipLinkTargetID = "wp--skip-link--target";3043 skipLinkTarget.id = skipLinkTargetID;3044 }3045 3046 // Create the skip link.3047 skipLink = document.createElement("a");3048 skipLink.classList.add("skip-link", "screen-reader-text");3049 skipLink.id = "wp-skip-link";3050 skipLink.href = "#" + skipLinkTargetID;3051 skipLink.innerText = "Skip to content";3052 3053 // Inject the skip link.3054 sibling.parentElement.insertBefore(skipLink, sibling);3055 })();3056 3057 //# sourceURL=wp-block-template-skip-link-js-after3058 </script>3059 3024 <script id="wp-emoji-settings" type="application/json"> 3060 3025 { 3061 3026 "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.
@westonruter commented on PR #10676:
5 weeks ago
#20
I just noticed that RTL versions of the CSS files are being generated. Here are all the versions now after a build:
src/wp-includes/css/wp-block-template-skip-link-rtl.csssrc/wp-includes/css/wp-block-template-skip-link-rtl.min.csssrc/wp-includes/css/wp-block-template-skip-link.csssrc/wp-includes/css/wp-block-template-skip-link.min.css
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.
@westonruter commented on PR #10676:
5 weeks ago
#21
@westonruter commented on PR #10676:
5 weeks ago
#22
OK, with 357ea8ac2177aa34c4b436b2d47ac7132303975a I now get the skip link positioned on the right as expected, but only when styles_inline_size_limit is zero. I then get this stylesheet in the HTML:
<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 SCRIPT_DEBUG is off and styles_inline_size_limit is at the default of 40K, I see:
<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 left: 5px. It should have right: 5px; which I can see in wp-block-template-skip-link-rtl.css.
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.
@westonruter commented on PR #10676:
5 weeks ago
#23
The diff above in https://github.com/WordPress/wordpress-develop/pull/10676#issuecomment-3733991892 was with prettier applied. Here's the diff with SCRIPT_DEBUG off and no prettier formatting. Notice how the CSS is now minified:
-
.html
old new 97 97 /*# sourceURL=core-block-supports-inline-css */ 98 98 </style> 99 99 <style id="wp-block-template-skip-link-inline-css"> 100 101 .skip-link.screen-reader-text { 102 border: 0; 103 clip-path: inset(50%); 104 height: 1px; 105 margin: -1px; 106 overflow: hidden; 107 padding: 0; 108 position: absolute !important; 109 width: 1px; 110 word-wrap: normal !important; 111 } 112 113 .skip-link.screen-reader-text:focus { 114 background-color: #eee; 115 clip-path: none; 116 color: #444; 117 display: block; 118 font-size: 1em; 119 height: auto; 120 left: 5px; 121 line-height: normal; 122 padding: 15px 23px 14px; 123 text-decoration: none; 124 top: 5px; 125 width: auto; 126 z-index: 100000; 127 } 128 /*# sourceURL=wp-block-template-skip-link-inline-css */ 100 /*! This file is auto-generated */ 101 .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} 102 /*# sourceURL=/wp-includes/css/wp-block-template-skip-link.min.css */ 129 103 </style> 130 104 <style id="twentytwentyfive-style-inline-css"> 131 105 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 121 148 122 <body class="wp-singular page-template-default page page-id-2 wp-embed-responsive wp-theme-twentytwentyfive"> 149 123 150 < div class="wp-site-blocks"><header class="wp-block-template-part">124 <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"> 151 125 <div class="wp-block-group alignfull is-layout-flow wp-block-group-is-layout-flow"> 152 126 153 127 <div class="wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained"> … … 198 172 </header> 199 173 200 174 201 <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)">175 <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)"> 202 176 203 177 <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)"> 204 178 … … 298 272 {"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"}]} 299 273 </script> 300 274 <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> 301 <script id="wp-block-template-skip-link-js-after">302 ( function() {303 var skipLinkTarget = document.querySelector( 'main' ),304 sibling,305 skipLinkTargetID,306 skipLink;307 308 // Early exit if a skip-link target can't be located.309 if ( ! skipLinkTarget ) {310 return;311 }312 313 /*314 * Get the site wrapper.315 * The skip-link will be injected in the beginning of it.316 */317 sibling = document.querySelector( '.wp-site-blocks' );318 319 // Early exit if the root element was not found.320 if ( ! sibling ) {321 return;322 }323 324 // Get the skip-link target's ID, and generate one if it doesn't exist.325 skipLinkTargetID = skipLinkTarget.id;326 if ( ! skipLinkTargetID ) {327 skipLinkTargetID = 'wp--skip-link--target';328 skipLinkTarget.id = skipLinkTargetID;329 }330 331 // Create the skip link.332 skipLink = document.createElement( 'a' );333 skipLink.classList.add( 'skip-link', 'screen-reader-text' );334 skipLink.id = 'wp-skip-link';335 skipLink.href = '#' + skipLinkTargetID;336 skipLink.innerText = 'Skip to content';337 338 // Inject the skip link.339 sibling.parentElement.insertBefore( skipLink, sibling );340 }() );341 342 //# sourceURL=wp-block-template-skip-link-js-after343 </script>344 275 <script id="wp-emoji-settings" type="application/json"> 345 276 {"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"}} 346 277 </script>
@rutviksavsani commented on PR #10676:
5 weeks ago
#25
Thanks @dmsnell for the final review and Thanks @westonruter for taking it over the line with all the necessary checks.
What's more is that I don't think this needs to use JavaScript at all anymore. The HTML API can be used to make the markup modifications. This could be done in
get_the_block_template_html().This was first introduced in r51003 (#53176) and was revisited in r56932 (#59505).