Ensuring Skip Links Across WordPress Themes
0 reputation · 11 Sept 2022, 02:26 UTC
0 reputation · 11 Sept 2022, 02:26 UTC
We want to guarantee that every page served by a WordPress site provides a functional skip link so that keyboard and screen‑reader users can bypass repetitive navigation and jump directly to the main content.
Twenty Twenty‑Three ships with a <a href="#content">Skip to content</a> element, whereas earlier core themes omit it. Custom Gutenberg blocks can generate non‑semantic markup, and the block toolbar sometimes traps focus on alignment controls. WordPress core does not enforce WCAG compliance for color contrast or skip links, leaving enforcement to theme developers or plugins.
32775 reputation · 11 Sept 2022, 09:06 UTC
To guarantee a functional skip link across any WordPress theme without modifying core theme files, the most reliable strategy is to use a must-use (MU) plugin that injects the link via the wp_body_open hook and ensures a target ID exists on the main content container.
wp-content/mu-plugins/accessibility-skip-link.php. MU plugins are automatically activated and cannot be disabled by site administrators in the dashboard.wp_body_open hook to place the link immediately after the opening <body> tag.#content, #main, #primary), the plugin should inject a small piece of JavaScript to ensure the primary content area has a consistent ID if the theme lacks one.<?php
add_action('wp_body_open', function() {
echo '<a class="skip-link screen-reader-text" href="#main-content">Skip to content</a>';
});
add_action('wp_footer', function() {
?>
<script>
(function() {
const targets = ['main', 'primary', 'content'];
let found = false;
targets.forEach(id => {
if (document.getElementById(id)) {
document.getElementById(id).id = 'main-content';
found = true;
}
});
if (!found) {
const main = document.querySelector('main') || document.querySelector('.site-main');
if (main) main.id = 'main-content';
}
})();
</script>
?>
Confirmed Facts: The wp_body_open action was introduced in WordPress 5.2 to provide a standardized location for injecting scripts and elements at the start of the body. Using this hook avoids the need to edit header.php in individual themes.
Likely Behavior: While most modern themes include a <main> element, older themes may rely on <div> wrappers. The JavaScript fallback provided above attempts to map common theme IDs to a single target (#main-content) to prevent broken links across different theme architectures.
Tab immediately. The "Skip to content" link should appear visually.Enter on the link; the browser focus must move to the main content area, bypassing the navigation menu.main-content exists to avoid accessibility failures.Are you using a Block Theme (FSE) or a Classic Theme? Block themes handle template parts differently, and if you are using a Full Site Editing theme, this injection can be handled directly via the Site Editor's HTML patterns without a plugin.
Use comments to ask for clarification. Post a solution as an answer.
4,880 reputation · 11 Sept 2022, 06:35 UTC
While using wp_body_open is the standard approach for WordPress 5.2+, it is important to note that this hook is not mandatory for theme developers. If a legacy or custom theme lacks the wp_body_open() function call in its header.php, the MU plugin approach mentioned above will fail silently.
To ensure the skip link is present regardless of theme compliance, you can verify the hook's availability or implement a JavaScript fallback. However, be cautious with JS injection: if the script runs too late, the skip link may not be the first element in the accessibility tree, potentially violating WCAG requirements for keyboard navigation order.
header.php for the wp_body_open() call.wp_footer script can prepend the link to the body, though this may cause a visible layout shift.