WP Snippets #33 Accessibility

Add a skip link to a classic theme

Keyboard users press Tab to move through a page, and on most sites the first twenty or so stops are header links and menu items. A skip link is a hidden link at the very top that appears on the first Tab press and jumps straight to the main content.

Block themes get one automatically (WordPress has added it since 5.8, pointing at the template’s <main> element). Classic themes don’t, unless the theme author built one in.

Before adding anything, open your site, click in the address bar, and press Tab once. If a “Skip to content” link shows up in a corner, your theme already has one and you can stop here. Themes built on Underscores usually do.

1. Print the link with wp_body_open

add_action( 'wp_body_open', function () {
    echo '<a class="skip-link screen-reader-text" href="#primary">' . esc_html__( 'Skip to content', 'hsr' ) . '</a>';
}, 5 );

wp_body_open fires right after the opening <body> tag, which is exactly where a skip link needs to be so it’s the first thing Tab reaches. The priority of 5 puts it ahead of anything else hooked there.

The href has to match the id of your main content wrapper. I used #primary because a lot of classic themes use it, but check your theme’s index.php or page.php for the real one (#content and #main are common too).

Warning: wp_body_open only works if the theme calls it.

The function was added in WordPress 5.2, and themes older than that often skip it. If your link never appears, open header.php and look for wp_body_open(); right after the <body> tag.

If it’s missing, add it there. For a third-party theme, do this in a child theme’s copy of header.php, or the next theme update will wipe it.

2. Hide it until it gets focus

.screen-reader-text {
    border: 0;
    clip-path: inset(50%);
    height: 1px;
    margin: -1px;
    overflow: hidden;
    padding: 0;
    position: absolute;
    width: 1px;
    word-wrap: normal !important;
}

.skip-link.screen-reader-text:focus {
    background: #fff;
    clip-path: none;
    color: #1e1e1e;
    display: block;
    font-weight: 600;
    height: auto;
    left: 8px;
    margin: 0;
    padding: 12px 20px;
    text-decoration: none;
    top: 8px;
    width: auto;
    z-index: 100000;
}

The first rule is the standard screen-reader-text pattern WordPress core uses in the admin. It hides the link visually but keeps it readable by screen readers, which display: none would not.

The second rule brings it back when it receives keyboard focus. The z-index of 100000 keeps it above the admin bar when you’re logged in (the admin bar sits at 99999).

3. Test it with the keyboard

Reload the page, click in the address bar, and press Tab. The link should appear at the top left.

Press Enter, then Tab again. Focus should land on the first link inside your main content, not back in the header menu.

If it jumps back to the header, the id in the href doesn’t match anything on the page. View source and search for it.

That’s all for this one.

Try it: Tab through your homepage without the skip link first and count how many presses it takes to reach the first link in your content. Then add the snippet and count again. Tell me the before number, I’m guessing some of you will cross 30.

Also published on LinkedIn ↗ (opens in a new tab)

WP Snippets

Get the next issue.

Short, practical WordPress notes with code you can use. Follow the newsletter on LinkedIn or browse earlier issues.