settings = AccessIQ_Settings::instance();
add_action( 'wp_head', [ $this, 'inject_fixes' ], 1 );
// Skip link must be the first focusable element: wp_body_open (WP 5.2+), footer as fallback.
add_action( 'wp_body_open', [ $this, 'inject_skip_link' ], 1 );
add_action( 'wp_footer', [ $this, 'inject_skip_link_fallback' ], 1 );
add_action( 'wp_footer', [ $this, 'inject_skip_target' ], 2 );
add_filter( 'language_attributes', [ $this, 'fix_lang_attribute' ] );
add_filter( 'the_content', [ $this, 'fix_decorative_images' ] );
}
/* ── 1. Skip Link ──────────────────────────────────────────── */
/**
* Output skip-to-content link as first element in footer (priority 1).
* Visually hidden until focused via CSS; accessible via keyboard tab.
*/
public function inject_skip_link() {
if ( ! $this->is_enabled( 'skip_link' ) || $this->skip_link_done ) {
return;
}
$this->skip_link_done = true;
echo ''
. esc_html__( 'Skip to content', 'accessiq' )
. '';
}
/**
* Themes without wp_body_open(): print in footer so the link still exists.
*/
public function inject_skip_link_fallback() {
$this->inject_skip_link();
}
/**
* Ensure #content exists. If the theme has no element with that id, point the link at
* /[role=main] (giving it id=content) instead of leaving a dead anchor.
*/
public function inject_skip_target() {
if ( ! $this->is_enabled( 'skip_link' ) ) {
return;
}
wp_print_inline_script_tag(
"(function(){var l=document.getElementById('accessiq-skip-link');if(!l||document.getElementById('content')){return;}"
. "var m=document.querySelector('main,[role=\"main\"],#primary,#main');if(!m){return;}"
. "if(!m.id){m.id='content';}l.setAttribute('href','#'+m.id);"
. "if(!m.hasAttribute('tabindex')){m.setAttribute('tabindex','-1');}})();"
);
}
/* ── 2 & 5. CSS injection ──────────────────────────────────── */
/**
* Inject CSS fixes via wp_add_inline_style (WP.org compliant, no raw