Validating and making accessible a wordpress site

Install Accessibility by UserWay

You can download the plugin here.

Nu Html Checker

First, run the Nu Html Checker for your site. You should have no errors there.

Elementor

Full example 1

				
					add_action( 'elementor/frontend/the_content', function( $content ) {
// element pack carousel widget
$content = str_replace(" bdt-grid>", " >", $content);
$content = str_replace(" bdt-margin>", " >", $content);
$content = str_replace("migration_allowed=\"1\"", "", $content);
$content = str_replace("migrated=\"0\"", "", $content);
$content = str_replace("<i ", "<span ", $content);
$content = str_replace("</i>", "</span>", $content);
$content = str_replace(" bdt-modal>", " >", $content);
$content = str_replace(" bdt-height-viewport>", " >", $content);
$content = str_replace(" bdt-close>", " >", $content);
$content = str_replace("bdt-toggle=\"\"", "", $content);
return $content;
} );

add_action( 'elementor/widget/render_content', function( $content, $widget ) {
//echo $widget->get_name();
// woocommerce mini cart
if ( 'woocommerce-menu-cart' === $widget->get_name() ) {
$content = str_replace("class=\"elementor-menu-cart__container", "role=\"navigation\" class=\"elementor-menu-cart__container", $content);
$content = str_replace("class=\"elementor-menu-cart__main\" aria-expanded=\"false\"", "class=\"elementor-menu-cart__main\"", $content);
$content = str_replace("id=\"elementor-menu-cart__toggle_button\"", "", $content);
//var_dump($content);
}

// ajax search
if ( 'wp-widget-ajaxsearchprowidget' === $widget->get_name() ) {
$content = str_replace("id=\"x-mark-icon\"", "", $content);
}

// icon
if ( 'icon' === $widget->get_name() ) {
$content = str_replace("<i ", "<span style=\"display:none;\">dummy</span><i ", $content);
}

// jet-mega-menu
if ( 'jet-mega-menu' === $widget->get_name() ) {
$content = str_replace("<i ", "<span style=\"display:none;\">dummy</span><i ", $content);
}

// slides
if ( 'slides' === $widget->get_name() ) {
$content = str_replace("</a>", "<span style=\"display:none;\">dummy</span></a>", $content);
}

// bdt-mailchimp
if ( 'bdt-mailchimp' === $widget->get_name() ) {
	//Clean the line breaks and leave just a space
	$content = preg_replace("/[ \t]+/", " ", preg_replace("/\s*$^\s*/m", "\n", $content));
    $content = preg_replace("/[\n\r]/", "", $content);
    $content = str_replace("</div> <div class=\"bdt-newsletter-btn", "</span><span class=\"bdt-newsletter-btn", $content);
	$content = str_replace("</div> </div> </button>", "</span></span></button>", $content);
	$content = str_replace("<div class=\"bdt-newsletter-btn", "<span class=\"bdt-newsletter-btn", $content);
}

if ( 'wd_testimonials' === $widget->get_name() || 'wd_infobox' === $widget->get_name()) {
		$content = str_replace("<img ", "<img alt=/"/" ", $content);
	}
if ( 'wd_blog' === $widget->get_name() ) {
	$content = str_replace("<h3 ", "<div ", $content);
	$content = str_replace("</h3>", "</div>", $content);
}
	
if ( 'wd_text_block' === $widget->get_name() ) {
	$style = "style='font-size: 18px; color:#242424; margin-bottom:20px; font-weight:600;'";
	$content = str_replace("<h4 ", "<div " . $style, $content);
	$content = str_replace("<h4>", "<div ".$style.">", $content);
	$content = str_replace("</h4>", "</div>", $content);
}
if ( 'wd_infobox' === $widget->get_name() ) {
	$content = str_replace("<h4", "<div" , $content);
	$content = str_replace("</h4>", "</div>", $content);
}
if ( 'sidebar' === $widget->get_name() ) {
	$content = str_replace("<h5", "<div" , $content); print_r('sidebar');
	//$content = str_replace("<h4>", "<div>", $content);
	$content = str_replace("</h5>", "</div>", $content);
}

return $content;
}, 10, 2 );
				
			

Full example 2

				
					function callback($content) {
	//Missing alternative text
	$content = preg_replace('/(<img(?!.*?alt=([\'"]).*?\2)[^>]*)(\/>)/', '$1 alt="site-alt" $3', $content);
	
	//Empty link
	$content = preg_replace('/(<a(?!.*?title=([\'"]).*?\2)[^>]*)(>)/', '$1 title="empty-link" $3', $content);
	
	//Empty button 
	$content = preg_replace('/(<button(?!.*?title=([\'"]).*?\2)[^>]*)(>)/', '$1 title="empty-link" $3', $content);
	
	//Missing form label
	$content = preg_replace('/(<input(?!.*?title=([\'"]).*?\2)[^>]*)(\/>)/', '$1 title="title" $3', $content);
	$content = preg_replace('/(<textarea(?!.*?title=([\'"]).*?\2)[^>]*)(>)/', '$1 title="title" $3', $content);
	
	//Other Fixes
	$content = str_replace(" ptpx", "px", $content);
	
	//Fix inline styles
	$doc = new DOMDocument();
	$doc->loadHTML($content);//Turn the $content string into a DOM document
	$els = $doc->getElementsByTagName("style"); //Find the elements matching our tag name ("style" in this example)

	foreach($els as $el) {
		if($el->getAttribute("id") && $el->getAttribute("id")=="rs-plugin-settings-inline-css"){
			$css = $el->textContent;
			$style_node = "<style id='rs-plugin-settings-inline-css' type='text/css'>" . $css . "</style>";
			$content = str_replace($style_node, "", $content);
			$content = str_replace("</head>", $style_node . "</head>", $content);
		}
		else{
			$css = $el->textContent;
			if(str_starts_with($css, "/*! elementor")){
				$style_node = "<style>" . $css . "</style>";
				$content = str_replace($style_node, "", $content);
				$content = str_replace("</head>", $style_node . "</head>", $content);
			}
		}
	}

	//Fix Contrast
	$contrast_css = "<style>
	/* your style here */
	</style>";
	$content = str_replace("</head>", $contrast_css . "</head>", $content);
	
	return  $content;
}

function buffer_start() { ob_start("callback"); }

function buffer_end() { ob_end_flush(); }

add_action('wp_head', 'buffer_start');
add_action('wp_footer', 'buffer_end', 9999999);
				
			
				
					// remove font-display: swap
function start_wp_head_buffer() {
    ob_start();
}

add_action('wp_head', 'start_wp_head_buffer', 0);

function end_wp_head_buffer() {
    $in = ob_get_clean();
    // remove font-display:swap;
    $in = str_replace('font-display:swap;', '', $in);
    echo $in; // output the result unless you want to remove it
}

add_action('wp_head', 'end_wp_head_buffer', PHP_INT_MAX);
				
			

If you want to replace <i> with <span> to pass accessibility, add this code:

				
					function callback($content) {
	// modify content here, and then return the updated code
	$content = str_replace("<i ", "<span ", $content);
	$content = str_replace("</i>", "</span>", $content);
	
	$content = str_replace("<a class=\"next", "<a title=\"next\" class=\"next", $content);
	
	$content = str_replace("<select name=\"orderby\"", "<select title=\"orderby\" name=\"orderby\"", $content);
	
	$content = str_replace("<select name=\"count\"", "<select title=\"count\" name=\"count\"", $content);
	
	
	$content = str_replace("<a href=\"\" class=\"bdt-navigation-prev", "<a title=\"bdt-navigation-prev\" href=\"\" class=\"bdt-navigation-prev", $content);
	$content = str_replace("<a href=\"\" class=\"bdt-navigation-next", "<a title=\"bdt-navigation-next\" href=\"\" class=\"bdt-navigation-next", $content);

    //Pattern to match only the content of h3
    $pattern = "|(?<=<h3 class=\"wd-entities-title\">)(.*?)(?=<\/h3>)|";
    //Pattern to match the whole h3 including the content
    $pattern = "/<h3 class=\"wd-entities-title\">(.*?)<\/h3>/";
    
    //$content = preg_replace($pattern, "<h4 class=\"wd-entities-title\">$1</h4>",$content);
    $content = preg_replace("/<h5 class=\"widget-title\">(.*?)<\/h5>/", "<div class=\"widget-title\">$1</div>",$content);
    
    ////////////////////////////////////////////////////
	// Move inline style to head
	////////////////////////////////////////////////////
	//Parent Div class
	$class = "premium-carousel-template";
	
  	$doc = new DOMDocument();
    $doc->loadHTML($content);//Turn the $content string into a DOM document
    $els = $doc->getElementsByTagName("style"); //Find the elements matching our tag name ("style" in this example)

    foreach($els as $el) {
		//Get parent element
		$parentNode = $el->parentNode;
        //for each parent element, get the class, and if it matches return it's contents
        $classAttr = $parentNode->getAttribute("class");
        if(preg_match('#\b'.$class.'\b#',$classAttr) > 0) {
			$css = $el->textContent;
			$style_node = "<style>" . $css . "</style>";
			$content = str_replace($style_node, "", $content);
			$content = str_replace("</head>", $style_node . "</head>", $content);
		}
    }
	
	return $content;
}

function buffer_start() { ob_start("callback"); }

function buffer_end() { ob_end_flush(); }

add_action('wp_head', 'buffer_start');
add_action('wp_footer', 'buffer_end');
				
			

Elementor Hello

Remove skip link

Add alt to images

				
					function callback($content) {
    $content = str_replace('alt=""', '', $content);
     $content = preg_replace("/<img (.*?)\/>/", "<img $1>",$content);
    $content = preg_replace('/(<img(?!.*?alt=([\'"]).*?\2)[^>]*)(>)/', '$1 alt="site-alt" $3', $content);

    return  $content;
}

function buffer_start() { ob_start("callback"); }

function buffer_end() { ob_end_flush(); }

add_action('wp_head', 'buffer_start');
add_action('wp_footer', 'buffer_end');
				
			

Empty link error

				
					add_action( 'elementor/widget/render_content', function( $content, $widget ) {
if ( 'post-info' === $widget->get_name() ) { 
	$content = preg_replace("/itemprop=\"(.*?)\"/", "",$content);
}

if ( 'gallery' === $widget->get_name() ) { 
	$content = preg_replace("/<div (.*?) alt=\"\" >/", "<div $1 >",$content);
	
	$content = str_replace("<a class=\"e-gallery-item", "<a title=\"gallery-item\" class=\"e-gallery-item", $content);
}
				
			

Install a GDPR cookie plugin

Install CookieYes | GDPR Cookie Consent & Compliance Notice (CCPA Ready) by WebToffee

Open the Settings > Customize Cookie bar and convert div to span.

Also click Privacy Overview menu button and remove title if you have one (to remove the headings error).

If you use a slick slider, and you get a aria-describedby error in wave, make sure that you show the bullets in slider (only bullets, not arrows). If this does not work, use a similar code to below.

 

				
					<script>
jQuery( document ).ready(function() {
	jQuery('.ht-products').each(function(){
		jQuery(this).on('init', function(event, slick){
			remove_aria_describedby(jQuery(this));
		});

		jQuery(this).on('afterChange', function(event, slick, currentSlide){
			remove_aria_describedby(jQuery(this));
		});

		jQuery(this).on('beforeChange', function(event, slick, currentSlide){
			remove_aria_describedby(jQuery(this));
		});
		
	});
	
	function remove_aria_describedby(el){
		console.log(el);
		jQuery('.ht-product').each(function () {
				//jQuery(this).removeAttr('aria-describedby');
				if (jQuery(this).attr('aria-describedby') != undefined) { // ignore extra/cloned slides
						jQuery(this).attr('id', jQuery(this).attr('aria-describedby'));
				}
		});
	}
});
</script>
				
			
				
					

function callback($content) {
	// modify content here, and then return the updated code
	$content = str_replace("<i ", "<span ", $content);
	$content = str_replace("</i>", "</span>", $content);

	$content = str_replace("<a class=\"next", "<a title=\"next\" class=\"next", $content);

	$content = str_replace("<select name=\"orderby\"", "<select title=\"orderby\" name=\"orderby\"", $content);

	$content = str_replace("<select name=\"count\"", "<select title=\"count\" name=\"count\"", $content);

	
	
	$content = str_replace("<input type=\"text\" name=\"your-name\"", "<input title=\"your-name\" aria-label=\"NAME\" type=\"text\" name=\"your-name\"", $content);
	$content = str_replace("<input type=\"email\" aria-label=\"EMAIL\" name=\"your-email\"", "<input title=\"your-email\" type=\"email\" name=\"your-email\"", $content);
	$content = str_replace("<input type=\"tel\" name=\"tel-767\"", "<input title=\"tel-767\" type=\"tel\" name=\"tel-767\"", $content);
	$content = str_replace("<input type=\"text\" name=\"text-1\"", "<input title=\"text-1\" type=\"text\" name=\"text-1\"", $content);


	$content = str_replace("<textarea name=\"your-message\"", "<textarea title=\"your-message\" name=\"your-message\"", $content);
	$content = str_replace("<textarea name=\"g-recaptcha-response\"", "<textarea title=\"g-recaptcha-response\" name=\"g-recaptcha-response\"", $content);
	
	$content = str_replace("<a href=\"javascript:void(0);\"", "<a title=\"javascript-void\" href=\"javascript:void(0);\"", $content);
	$content = str_replace("<a href=\"#\"", "<a title=\"#\" href=\"#\"", $content);
	$content = str_replace("<a rel=\"noopener", "<a title=\"noopener\" rel=\"noopener", $content);
	$content = str_replace("<a rel=\"nofollow", "<a title=\"nofollow\" rel=\"nofollow", $content);
	
	
	$content = str_replace("<svg version=\"1.1\" id=\"Layer_1\"", "<svg version=\"1.1\"", $content);
	
	

    //Pattern to match only the content of h3
    $pattern = "|(?<=<h3 class=\"wd-entities-title\">)(.*?)(?=<\/h3>)|";
    //Pattern to match the whole h3 including the content
    $pattern = "/<h3 class=\"wd-entities-title\">(.*?)<\/h3>/";

    //$content = preg_replace($pattern, "<h4 class=\"wd-entities-title\">$1</h4>",$content);
    //$content = preg_replace("/<h5 class=\"widget-title\">(.*?)<\/h5>/", "<div class=\"widget-title\">$1</div>",$content);
	
	
	$content = preg_replace($pattern, "<div class=\"wd-entities-title\">$1</div>",$content);
	
	$content = preg_replace("/<h3 class=\"title slider-title\">(.*?)<\/h3>/", "<div class=\"title slider-title\">$1</div>",$content);
	$content = preg_replace("/<h5 class=\"widget-title\">(.*?)<\/h5>/", "<div class=\"widget-title\">$1</div>",$content);
	$content = preg_replace("/<h3 class=\"widget-title\">(.*?)<\/h3>/", "<div class=\"widget-title\">$1</div>",$content);
	
	
	$content = preg_replace("/<h5 class=\"wd-entities-title\">(.*?)<\/h5>/", "<div class=\"wd-entities-title\">$1</div>",$content);

	//$content = preg_replace("/<h3 class=\"wd-entities-title title post-title\">(.*?)<\/h3>/", "<div class=\"wd-entities-title title post-title\">$1</div>",$content);
	return $content;
}

function buffer_start() { ob_start("callback"); }

function buffer_end() { ob_end_flush(); }

add_action('wp_head', 'buffer_start');
add_action('wp_footer', 'buffer_end');
				
			

If you use revolution slider, open wp-content\plugins\revslider\public\revslider-front.class.php and comment this line in 150:

				
					$custom_css = (trim($custom_css) == '') ? '#rs-demo-id {}' : $custom_css;


				
			

Alternatively, instead of changing the plugin, you can use the following code:

				
					 ////////////////////////////////////////////////////
    // Move inline style to head Revolution Slider
    ////////////////////////////////////////////////////
      $doc = new DOMDocument();
    $doc->loadHTML($content);//Turn the $content string into a DOM document
    $els = $doc->getElementsByTagName("style"); //Find the elements matching our tag name ("style" in this example)

    foreach($els as $el) {
        if($el->getAttribute("id") && $el->getAttribute("id")=="rs-plugin-settings-inline-css"){
            $css = $el->textContent;
            $style_node = "<style id='rs-plugin-settings-inline-css' type='text/css'>" . $css . "</style>";
            $content = str_replace($style_node, "", $content);
            $content = str_replace("</head>", $style_node . "</head>", $content);
        }

    }
				
			

If you use Contact form 7 recaptcha:

				
					add_filter('wpcf7_contact_form_properties',  'my_wpcf7_form_elements2', 10, 2 );

function my_wpcf7_form_elements2($properties, $cf7) {
    $properties['form'] = str_replace("[recaptcha]", "", $properties['form']);  
    return $properties;
};
				
			

If you use elementor form recaptcha:

				
					<script>
jQuery(window).on('load', function () {
    let textarea = document.getElementById("g-recaptcha-response");
    textarea.setAttribute("aria-hidden", "true");
    textarea.setAttribute("aria-label", "do not use");
    textarea.setAttribute("aria-readonly", "true");
});
</script>
				
			

If you use porto theme and you have errors in css, go THEME OPTIONS -> SKIN -> CSTOM CSS and check there. Also, inline style errors may be from blocks from template builer. You must remake them with elementor.

Alt images

Make sure that all your images have alt text.

To do so easily for mutliple images at the same time, you can use the Bulk edit image alt tag, caption & description – WordPress Media Library Helper by Codexin plugin.

Cloudfare

If you have cloudfare on, make sure that you deactivate Rocket loader.

To turn off Rocket Loader for your whole site, click off in the Performance tab of your Cloudflare Dashboard.

Acchecker

Go to acchecker site and run it for your site. Make sure no errors exist.

Wave checker

Add in chrome wave checker and see that no errors exist.