Web accessibility is often treated as an afterthought, but a few targeted improvements can make a significant difference for screen-reader users and assistive technology in general. This article covers two concrete areas: image role attributes and accessible table markup.
Problem: A WordPress site fails basic accessibility checks — images lack descriptive alt text, decorative images are announced by screen readers, and data tables have no headers or captions.
Solution: Add descriptive alt attributes to meaningful images via the media library or ACF alt fields. Set alt="" and role="presentation" on purely decorative images so screen readers skip them. For data tables, add a <caption> and use scope attributes on all <th> elements.
Images — adding role="presentation" for decorative images. Decorative images (icons, dividers, backgrounds rendered as <img> tags) should be marked so that screen readers skip them. If you use wp_get_attachment_image(), hook into wp_get_attachment_image_attributes:
<?php
add_filter( 'wp_get_attachment_image_attributes', 'add_presentation_role_to_images' );
function add_presentation_role_to_images( $attr ) {
$attr['role'] = 'presentation';
return $attr;
}
Apply this filter conditionally (e.g. only for a specific image size or post type) if you need role="img" with a meaningful alt on content images.
Tables — adding scope attributes for better screen-reader navigation. Screen readers use scope="col" and scope="row" to associate data cells with their headers. The filter below adds these attributes automatically to post and page content:
<?php
add_filter( 'the_content', 'improve_table_accessibility' );
function improve_table_accessibility( $content ) {
$post_type = get_post_type();
if ( $post_type !== 'post' && basename( get_page_template() ) !== 'page.php' ) {
return $content;
}
// Add scope="col" to column header cells
$content = str_replace( '</th><th>', '</th><th scope="col">', $content );
// Convert the first <td> in each row to a row-header <th scope="row">
$content = preg_replace( '/<tr><td>(.*?)<\/td>/', '<tr><th scope="row">$1</th>', $content );
return $content;
}
Bonus — responsive table wrapper. Wrap all tables in a scrollable container and apply Bootstrap classes automatically, while also cleaning up stray spaces in class attributes that the W3C validator flags:
<?php
// Extend the improve_table_accessibility() function above with:
$content = str_replace( '<table', '<div class="table-wrap"><table', $content );
$content = str_replace( '</table>', '</table></div>', $content );
$content = str_replace( '<table class="', '<table class="table ', $content );
// Remove leading/trailing spaces from class attribute values (W3C validator warning)
$content = preg_replace( '/class=["']\s*(?P<class>[^"'<>]+?)\s*["']/i', 'class="$1"', $content );
NOTE: The preg_replace that promotes the first <td> to a row header assumes a simple table structure where the leftmost cell is always a header. If your tables have a different layout — for example, no row headers — skip that line to avoid incorrectly wrapping data cells in <th> elements.