Why accessibility matters in WordPress theme development
When we build WordPress themes, we're often focused on aesthetics and performance, but we're leaving behind roughly 15% of the global population who experience some form of disability. Accessibility in WordPress themes isn't a nice-to-have feature or a compliance checkbox—it's a fundamental part of professional theme development. By following the guidance in this accessibility in WordPress themes: a step-by-step guide, we can create sites everyone can use, regardless of their abilities or the tools they rely on.
The good news is that accessible themes benefit everyone. Better keyboard navigation helps power users. Clear semantic structure improves SEO. Proper contrast ratios reduce eye strain. These practices make your themes faster, more maintainable, and more valuable to clients. Let's walk through the concrete steps we take when building accessible WordPress themes.
Semantic HTML and landmarks
The foundation of accessible WordPress themes is semantic HTML. When we use semantic elements like <header>, <nav>, <main>, <article>, and <footer>, we're giving assistive technologies a clear roadmap of our page structure. Screen readers parse these landmarks to let users jump to specific content sections without wading through the entire page.
In a typical WordPress theme, your header template might look like this:
<header class="site-header" role="banner">
<div class="site-branding">
<?php bloginfo( 'name' ); ?>
</div>
<nav class="main-navigation" aria-label="Primary Menu">
<?php
wp_nav_menu( array(
'theme_location' => 'primary',
'fallback_cb' => 'wp_page_menu',
'depth' => 2,
) );
?>
</nav>
</header>Notice the <nav> element with aria-label="Primary Menu". When a page has multiple navigation sections, the aria-label differentiates them for screen reader users. The role="banner" on the header ensures it's recognized as the site header, not just a generic section.
For your main content area, always wrap post content in a <main> element:
<main id="main" class="site-main">
<?php
while ( have_posts() ) :
the_post();
get_template_part( 'template-parts/content', get_post_type() );
endwhile;
?>
</main>The id="main" lets screen readers skip directly to content using skip links. A skip link is a hidden anchor that appears when focused via keyboard, allowing users to bypass repetitive navigation:
<a href="#main" class="skip-link screen-reader-text">
Skip to content
</a>Style the skip link to be invisible but visible on focus:
.skip-link {
position: absolute;
top: -9999px;
left: -9999px;
}
.skip-link:focus {
position: fixed;
top: 0;
left: 0;
z-index: 999;
padding: 1em;
background: #000;
color: #fff;
}Use heading hierarchy correctly. Start with <h1> for the page title, then use <h2>, <h3>, etc. in order. Never skip heading levels (jumping from <h1> to <h3>). Screen reader users rely on this hierarchy to navigate content structure.
Keyboard navigation and focus styles
Many users navigate websites entirely with keyboards—either by choice or necessity. We need to ensure every interactive element is reachable and operable via keyboard. This means buttons, links, form inputs, and custom interactive components must be focusable in logical order.
WordPress automatically handles focus order for native HTML elements, but custom components need attention. If we build a custom dropdown menu, we need to implement keyboard handlers:
<div class="dropdown-menu">
<button class="dropdown-toggle" aria-haspopup="true" aria-expanded="false">
Menu
</button>
<ul class="dropdown-items" role="menu">
<li><a href="/about" role="menuitem">About</a></li>
<li><a href="/contact" role="menuitem">Contact</a></li>
</ul>
</div>The JavaScript to handle this:
document.querySelector('.dropdown-toggle').addEventListener('click', function() {
const isExpanded = this.getAttribute('aria-expanded') === 'true';
this.setAttribute('aria-expanded', !isExpanded);
document.querySelector('.dropdown-items').style.display = isExpanded ? 'none' : 'block';
});
document.querySelectorAll('.dropdown-items a').forEach(link => {
link.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
document.querySelector('.dropdown-toggle').setAttribute('aria-expanded', 'false');
document.querySelector('.dropdown-items').style.display = 'none';
}
});
});Always provide visible focus styles. The browser's default blue outline isn't pretty, but removing it without replacing it breaks accessibility. Use a custom outline that meets the WCAG AA contrast ratio of 3:1:
a:focus,
button:focus,
input:focus {
outline: 2px solid #0066cc;
outline-offset: 2px;
}Use outline-offset to give the focus ring space from the element so it doesn't look cramped. For form fields, consider combining outline with a background color change or border adjustment for extra clarity.
Alt text for images
Alt text is critical for screen reader users and anyone who can't load images. In WordPress, every image needs descriptive alt text that conveys the content or purpose of the image.
When rendering featured images in a template:
<?php
if ( has_post_thumbnail() ) {
the_post_thumbnail( 'medium', array(
'alt' => get_the_title(),
) );
}
?>However, using the post title as alt text is a bare minimum. Better practice is to store meaningful alt text separately. When outputting images in custom blocks or components, always include alt text:
<img src="<?php echo esc_url( $image_url ); ?>"
alt="<?php echo esc_attr( $alt_text ); ?>"
width="400"
height="300" />For decorative images that don't convey content—like background patterns or spacers—use empty alt text: alt="". This tells screen readers to skip the image entirely, which is the correct behavior for purely decorative content.
When building custom image galleries or carousels, add role and ARIA attributes:
<div class="carousel" role="region" aria-label="Featured images">
<div class="carousel-item">
<img src="image-1.jpg" alt="Product view from front" />
</div>
<div class="carousel-item">
<img src="image-2.jpg" alt="Product view from side" />
</div>
</div>The region role with aria-label helps screen reader users understand this is a carousel with multiple images, not separate unrelated images.
Test with screen readers
We can't know if our accessibility work is effective without testing with actual screen readers. The good news is that testing tools are free and widely available.
NVDA (NonVisual Desktop Access) is free and runs on Windows. Download it from nvaccess.org. After installation, press Ctrl+Alt+N to start it. Navigate your WordPress site using arrow keys and Tab. Listen to how content is announced. If a screen reader can't find a main landmark or skips heading levels, you'll hear it immediately.
JAWS is the industry standard but costs money. Many development teams have licenses. If available to you, it's worth testing with because real users often use JAWS.
Safari with VoiceOver is built into macOS and iOS. Press Cmd+F5 to enable VoiceOver. Navigate using arrow keys. VoiceOver is excellent for testing how navigation and form elements behave.
When testing, focus on these specific scenarios:
- Can you navigate the entire page using only Tab and arrow keys?
- Are all headings announced with their correct level?
- Do all buttons and links have descriptive labels that make sense without visual context?
- Are form labels properly associated with their inputs?
- Does focus movement follow a logical order?
- Are there any unlabeled interactive elements?
Automated testing with tools like Axe DevTools or WAVE can catch many issues, but they can't replace manual testing. Run Axe DevTools in your WordPress staging environment to catch low-hanging fruit, then spend time with a screen reader to catch issues automation misses.
Common mistakes
We see recurring patterns in WordPress themes that break accessibility:
- Empty buttons and links: A button with only an icon and no text fails for screen readers. Add a
aria-labelor include text viasr-onlyclass if you want to hide it visually. - Images as text: Never put text inside image files (like logos with taglines). Screen readers can't read text in images. Use actual text and style it with CSS.
- Removing outline styles: We see
* { outline: none; }in stylesheets more often than we'd like. If you need custom focus styles, add them—don't remove them entirely. - Color alone to convey meaning: Red text for errors is fine, but pair it with an icon or text label. Color-blind users won't see the distinction.
- Autoplay video or audio: Unexpected sounds are jarring and confusing for screen reader users. Let users control playback.
- Poor contrast ratios: Gray text on white backgrounds fails WCAG AA. Use a contrast checker tool—aim for 4.5:1 for body text.
Key takeaways
- Build with semantic HTML landmarks (
<header>,<nav>,<main>,<footer>) and proper heading hierarchy from the start. - Ensure keyboard navigation works flawlessly—Tab through your entire site without a mouse, and provide visible focus styles.
- Write meaningful alt text for all images; use empty alt for purely decorative images.
- Test with real screen readers like NVDA or VoiceOver. Automated tools catch syntax errors but miss real-world usability issues.
- Use ARIA labels and roles to enhance semantic meaning when native HTML isn't sufficient for custom components.
- Prioritize contrast ratios and readable fonts—accessibility benefits all users, not just those with disabilities.






Comments
Loading comments…