DEV Community

Cover image for How to Add Skip Links to Any WordPress Theme
anti Gym Club
anti Gym Club

Posted on • Originally published at antigymclub.com

How to Add Skip Links to Any WordPress Theme

Skip links are one of the most important accessibility features and one of the most overlooked.

They let keyboard users bypass repetitive navigation and jump directly to content. WCAG 2.4.1 (Level A) requires them. Yet most WordPress themes don't include them.

Here's how to add skip links to any theme, with code you can use today.

What Are Skip Links?

A skip link is a hidden anchor link that becomes visible when a keyboard user presses Tab. It typically says "Skip to content" and jumps to the main content area.

For someone using a screen reader or keyboard-only navigation, this means not having to tab through 20 menu items on every single page.

The Basic Implementation

Add this to your theme's functions.php or a custom plugin:

/**
 * Add skip link to the beginning of the page
 */
function add_skip_link() {
    // Don't show in admin
    if ( is_admin() ) {
        return;
    }

    echo '<a class="skip-link screen-reader-text" href="#main-content">Skip to content</a>';
}
add_action( 'wp_body_open', 'add_skip_link', 1 );
Enter fullscreen mode Exit fullscreen mode

The CSS (This Is Where Most Fail)

The skip link must be:

  • Invisible by default (but NOT display: none screen readers skip those)
  • Visible on focus
  • High contrast
  • Properly positioned
.skip-link {
    /* Visually hidden but accessible */
    clip: rect(1px, 1px, 1px, 1px);
    clip-path: inset(50%);
    height: 1px;
    width: 1px;
    margin: -1px;
    overflow: hidden;
    padding: 0;
    position: absolute;
    word-wrap: normal !important;
}

.skip-link:focus {
    /* Visible on focus */
    background-color: #000;
    color: #fff;
    clip: auto !important;
    clip-path: none;
    display: inline-block;
    font-size: 16px;
    font-weight: 600;
    height: auto;
    width: auto;
    margin: 0;
    overflow: visible;
    padding: 15px;
    position: fixed;
    top: 0;
    left: 0;
    z-index: 999999;
    text-decoration: none;
    outline: 2px solid #fff;
    outline-offset: 2px;
}
Enter fullscreen mode Exit fullscreen mode

Enqueue it properly:

function enqueue_skip_link_styles() {
    wp_enqueue_style(
        'skip-link-style',
        get_template_directory_uri() . '/css/skip-link.css',
        array(),
        '1.0.0'
    );
}
add_action( 'wp_enqueue_scripts', 'enqueue_skip_link_styles' );
Enter fullscreen mode Exit fullscreen mode

The Target Element Problem

Your skip link points to #main-content. But does that ID exist in your theme?

Check common IDs by theme:

  • Twenty Twenty-Four: main
  • Astra: content or primary
  • GeneratePress: content
  • Kadence: main
  • Custom themes: Check your single.php or index.php

If your theme doesn't have an ID on the main content wrapper, add one:

// In your theme's template files, find the main content div and add an ID
<main id="main-content" class="site-main">
Enter fullscreen mode Exit fullscreen mode

Or use JavaScript as a fallback:

document.addEventListener('DOMContentLoaded', function() {
    // Find common main content elements
    const selectors = ['main', '[role="main"]', '#content', '#primary', '.site-main'];

    for (const selector of selectors) {
        const element = document.querySelector(selector);
        if (element && !element.id) {
            element.id = 'main-content';
            break;
        }
    }
});
Enter fullscreen mode Exit fullscreen mode

Handling Themes Without wp_body_open

Older themes might not call wp_body_open(). Use a fallback:

function add_skip_link_with_fallback() {
    if ( is_admin() ) {
        return;
    }

    // Check if wp_body_open was already called
    if ( did_action( 'wp_body_open' ) ) {
        return; // Already rendered via wp_body_open
    }

    // Inject via JavaScript
    $html = '<a class="skip-link screen-reader-text" href="#main-content">Skip to content</a>';
    ?>
    <script>
    (function() {
        var skipLink = <?php echo wp_json_encode( $html ); ?>;
        var div = document.createElement('div');
        div.innerHTML = skipLink;
        document.body.insertBefore(div.firstChild, document.body.firstChild);
    })();
    </script>
    <?php
}
add_action( 'wp_footer', 'add_skip_link_with_fallback', 1 );
Enter fullscreen mode Exit fullscreen mode

Adding Multiple Skip Links

Some sites benefit from multiple targets:

function add_multiple_skip_links() {
    if ( is_admin() ) {
        return;
    }
    ?>
    <nav class="skip-links" aria-label="Skip links">
        <a class="skip-link" href="#main-content">Skip to content</a>
        <a class="skip-link" href="#site-navigation">Skip to navigation</a>
        <a class="skip-link" href="#footer">Skip to footer</a>
    </nav>
    <?php
}
add_action( 'wp_body_open', 'add_multiple_skip_links', 1 );
Enter fullscreen mode Exit fullscreen mode

Testing Your Implementation

  1. Reload your site
  2. Press Tab immediately
  3. The skip link should appear
  4. Press Enter
  5. Focus should move to your main content

Test with actual assistive technology if possible. NVDA (Windows) and VoiceOver (Mac) are free.

Common Mistakes

  • Using display: none Screen readers skip it entirely
  • Wrong target ID Link goes nowhere
  • Low contrast colors Fails WCAG
  • Missing focus styles Users can't see the link
  • z-index too low Hidden behind sticky headers

The Easy Way

If you don't want to maintain custom code, I built Skip Links Generator Pro that handles all edge cases including themes without wp_body_open, automatic ID detection, and customizable styling.

But the code above will work for most themes. The important thing is to have skip links at all.


Resources:

Top comments (0)