class-frontend-checker.php
499 lines
| 1 | <?php |
| 2 | if ( ! defined( 'ABSPATH' ) ) exit; |
| 3 | |
| 4 | class ARIAAT_Frontend_Checker { |
| 5 | |
| 6 | public function __construct() { |
| 7 | add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ] ); |
| 8 | add_action( 'wp_footer', [ $this, 'render_checker_panel' ] ); |
| 9 | add_action( 'wp_ajax_ariaat_apply_auto_fixes', [ $this, 'ajax_apply_auto_fixes' ] ); |
| 10 | } |
| 11 | |
| 12 | public function enqueue_assets() { |
| 13 | |
| 14 | $settings = get_option( 'ariaat_general_settings', [] ); |
| 15 | |
| 16 | $show_checker = apply_filters( |
| 17 | 'ariaat_show_frontend_checker', |
| 18 | ! empty( $settings['enable_frontend_checker'] ) && current_user_can( 'administrator' ) |
| 19 | ); |
| 20 | |
| 21 | if ( $show_checker ) { |
| 22 | |
| 23 | $v = ARIAATVERSION; |
| 24 | //$v = time(); |
| 25 | |
| 26 | wp_enqueue_style( |
| 27 | 'ariaat-checker-css', |
| 28 | ARIAATURL . 'assets/css/ariaat-frontend-checker.css', |
| 29 | false, |
| 30 | $v |
| 31 | ); |
| 32 | |
| 33 | wp_enqueue_script( |
| 34 | 'ariaat-checker-js', |
| 35 | ARIAATURL . 'assets/js/ariaat-frontend-checker.js', |
| 36 | [ 'jquery' ], |
| 37 | $v, |
| 38 | true |
| 39 | ); |
| 40 | |
| 41 | wp_localize_script( |
| 42 | 'ariaat-checker-js', |
| 43 | 'ARIAAT_ErrorMap', |
| 44 | $this->get_error_definitions() |
| 45 | ); |
| 46 | |
| 47 | wp_localize_script( |
| 48 | 'ariaat-checker-js', |
| 49 | 'ARIAAT_AutoFixes', |
| 50 | $this->get_free_auto_fix_definitions() |
| 51 | ); |
| 52 | |
| 53 | wp_localize_script( |
| 54 | 'ariaat-checker-js', |
| 55 | 'ARIAAT_CheckerAjax', |
| 56 | [ |
| 57 | 'ajaxurl' => admin_url( 'admin-ajax.php' ), |
| 58 | 'nonce' => wp_create_nonce( 'ariaat_apply_auto_fixes' ), |
| 59 | ] |
| 60 | ); |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | public function render_checker_panel() { |
| 65 | |
| 66 | $settings = get_option( 'ariaat_general_settings', [] ); |
| 67 | |
| 68 | $show_checker = apply_filters( |
| 69 | 'ariaat_show_frontend_checker', |
| 70 | ! empty( $settings['enable_frontend_checker'] ) && current_user_can( 'administrator' ) |
| 71 | ); |
| 72 | |
| 73 | if ( $show_checker ) { |
| 74 | ?> |
| 75 | <div id="ariaat-checker-panel" class="ariaat-ignore"> |
| 76 | <div id="ariaat-checker-header"> |
| 77 | <div id="ariaat-draggable"> |
| 78 | <svg fill="#ffffff" width="20" height="20" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> |
| 79 | <title>draggable</title> |
| 80 | <rect x="10" y="6" width="4" height="4"/> |
| 81 | <rect x="18" y="6" width="4" height="4"/> |
| 82 | <rect x="10" y="14" width="4" height="4"/> |
| 83 | <rect x="18" y="14" width="4" height="4"/> |
| 84 | <rect x="10" y="22" width="4" height="4"/> |
| 85 | <rect x="18" y="22" width="4" height="4"/> |
| 86 | </svg> |
| 87 | Accessibility Checker |
| 88 | </div> |
| 89 | |
| 90 | <div class="ariaat-checker-actions"> |
| 91 | <button id="run-ariaat-scan" type="button">Scan Page</button> |
| 92 | </div> |
| 93 | </div> |
| 94 | |
| 95 | <div id="ariaat-checker-overview"></div> |
| 96 | <div id="ariaat-checker-results"></div> |
| 97 | </div> |
| 98 | <?php |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | private function get_free_auto_fix_definitions() : array { |
| 103 | $definitions = $this->get_error_definitions(); |
| 104 | $fixable = []; |
| 105 | |
| 106 | foreach ( $definitions as $group => $items ) { |
| 107 | foreach ( $items as $code => $item ) { |
| 108 | if ( |
| 109 | isset( $item['fix_type'], $item['fix_scope'], $item['fix_pro'] ) && |
| 110 | 'auto' === $item['fix_type'] && |
| 111 | 'safe' === $item['fix_scope'] && |
| 112 | false === $item['fix_pro'] |
| 113 | ) { |
| 114 | $fixable[ $group . '.' . $code ] = $item; |
| 115 | } |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | return $fixable; |
| 120 | } |
| 121 | |
| 122 | public function ajax_apply_auto_fixes() : void { |
| 123 | if ( ! current_user_can( 'administrator' ) ) { |
| 124 | wp_send_json_error( [ 'message' => 'Permission denied.' ], 403 ); |
| 125 | } |
| 126 | |
| 127 | check_ajax_referer( 'ariaat_apply_auto_fixes', 'nonce' ); |
| 128 | |
| 129 | $setting_keys = isset( $_POST['setting_keys'] ) ? (array) $_POST['setting_keys'] : []; |
| 130 | $setting_keys = array_map( 'sanitize_key', $setting_keys ); |
| 131 | $setting_keys = array_filter( array_unique( $setting_keys ) ); |
| 132 | |
| 133 | if ( empty( $setting_keys ) ) { |
| 134 | wp_send_json_error( [ 'message' => 'No settings provided.' ], 400 ); |
| 135 | } |
| 136 | |
| 137 | $allowed_keys = [ |
| 138 | 'focus_outline', |
| 139 | 'fix_tabindex', |
| 140 | 'make_viewport_scalable', |
| 141 | ]; |
| 142 | |
| 143 | $setting_keys = array_values( array_intersect( $setting_keys, $allowed_keys ) ); |
| 144 | |
| 145 | if ( empty( $setting_keys ) ) { |
| 146 | wp_send_json_error( [ 'message' => 'No valid settings provided.' ], 400 ); |
| 147 | } |
| 148 | |
| 149 | $settings = get_option( 'ariaat_general_settings', [] ); |
| 150 | |
| 151 | if ( ! is_array( $settings ) ) { |
| 152 | $settings = []; |
| 153 | } |
| 154 | |
| 155 | foreach ( $setting_keys as $setting_key ) { |
| 156 | $settings[ $setting_key ] = '1'; |
| 157 | } |
| 158 | |
| 159 | update_option( 'ariaat_general_settings', $settings ); |
| 160 | |
| 161 | wp_send_json_success( |
| 162 | [ |
| 163 | 'message' => 'Fix settings saved.', |
| 164 | 'setting_keys' => $setting_keys, |
| 165 | ] |
| 166 | ); |
| 167 | } |
| 168 | |
| 169 | private function get_error_definitions() { |
| 170 | |
| 171 | $aria_url = admin_url('admin.php?page=ariaat&tab=aria'); |
| 172 | $roles_url = admin_url('admin.php?page=ariaat&tab=roles'); |
| 173 | $contrast_url = admin_url('admin.php?page=ariaat&tab=contrast'); |
| 174 | $general_url = admin_url('admin.php?page=ariaat&tab=general'); |
| 175 | $images_url = admin_url('admin.php?page=ariaat&tab=images'); |
| 176 | |
| 177 | $aria_link = sprintf('<a target="_blank" href="%s">ARIA settings</a>', $aria_url); |
| 178 | $roles_link = sprintf('<a target="_blank" href="%s">Roles settings</a>', $roles_url ); |
| 179 | $contrast_link = sprintf('<a target="_blank" href="%s">Contrast settings</a>', $contrast_url ); |
| 180 | $general_link = sprintf('<a target="_blank" href="%s">General settings</a>', $general_url ); |
| 181 | $images_link = sprintf('<a target="_blank" href="%s">Images settings</a>', $images_url ); |
| 182 | |
| 183 | $errors = [ |
| 184 | 'aria' => [ |
| 185 | 'missing_label' => [ |
| 186 | 'desc' => 'Missing aria-label', |
| 187 | 'long_desc' => "This can prevent screen readers from interpreting its purpose. Add a meaningful aria-label or use aria-labelledby within $aria_link.", |
| 188 | 'show' => true, |
| 189 | 'copy' => true, |
| 190 | 'fix' => $aria_url, |
| 191 | 'level' => 'A', |
| 192 | 'wcag' => '1.3.1', |
| 193 | 'wcag_label' => 'Info and Relationships', |
| 194 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships.html', |
| 195 | 'type' => 'error', |
| 196 | ], |
| 197 | 'empty_attribute' => [ |
| 198 | 'desc' => 'Empty ARIA attribute', |
| 199 | 'long_desc' => "An ARIA attribute was found with an empty value. ARIA attributes must have valid values to assist accessibility. Add a value within $aria_link.", |
| 200 | 'show' => true, |
| 201 | 'copy' => true, |
| 202 | 'fix' => $aria_url, |
| 203 | 'level' => 'A', |
| 204 | 'wcag' => '4.1.2', |
| 205 | 'wcag_label' => 'Name, Role, Value', |
| 206 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html', |
| 207 | 'type' => 'error', |
| 208 | ], |
| 209 | 'broken_reference' => [ |
| 210 | 'desc' => 'Broken ARIA reference', |
| 211 | 'long_desc' => "This element references another element via ARIA (e.g., aria-describedby), but the referenced ID was not found in the DOM. Double-check that the target ID exists and is unique.", |
| 212 | 'show' => true, |
| 213 | 'copy' => true, |
| 214 | 'level' => 'A', |
| 215 | 'wcag' => '4.1.2', |
| 216 | 'wcag_label' => 'Name, Role, Value', |
| 217 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html', |
| 218 | 'type' => 'error', |
| 219 | ], |
| 220 | 'empty_button' => [ |
| 221 | 'desc' => 'Empty button with no accessible text', |
| 222 | 'long_desc' => "Buttons should contain text or an aria-label so screen readers can convey their purpose. Add an aria-label within $aria_link.", |
| 223 | 'show' => true, |
| 224 | 'copy' => true, |
| 225 | 'fix' => $aria_url, |
| 226 | 'level' => 'A', |
| 227 | 'wcag' => '4.1.2', |
| 228 | 'wcag_label' => 'Name, Role, Value', |
| 229 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html', |
| 230 | 'type' => 'error', |
| 231 | ], |
| 232 | ], |
| 233 | 'roles' => [ |
| 234 | 'missing_role' => [ |
| 235 | 'desc' => 'Missing role or label on repeated landmark-type element', |
| 236 | 'long_desc' => "This element is one of multiple landmark-type elements on the page, such as <code><section></code> or <code><aside></code>. When multiple instances of these elements are used as landmarks, each should be given a semantic <code>role</code> (e.g. <code>role=\"region\"</code> or <code>role=\"complementary\"</code>) and a descriptive label using <code>aria-label</code> or <code>aria-labelledby</code>. This helps screen reader users distinguish between different regions of the page. Use $roles_link to fix.", |
| 237 | 'show' => true, |
| 238 | 'copy' => true, |
| 239 | 'fix' => $roles_url, |
| 240 | 'level' => 'A', |
| 241 | 'wcag' => '1.3.1', |
| 242 | 'wcag_label' => 'Info and Relationships', |
| 243 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships.html', |
| 244 | 'type' => 'warning', |
| 245 | ], |
| 246 | 'incorrect_role' => [ |
| 247 | 'desc' => 'Incorrect ARIA role', |
| 248 | 'long_desc' => "This element has a <code>role</code> that doesn’t match its expected semantic purpose. For example, <code><main role=\"banner\"></code> is incorrect. Adjust the role to match the element’s purpose using $roles_link.", |
| 249 | 'show' => true, |
| 250 | 'copy' => true, |
| 251 | 'fix' => $roles_url, |
| 252 | 'level' => 'A', |
| 253 | 'wcag' => '1.3.1', |
| 254 | 'wcag_label' => 'Info and Relationships', |
| 255 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships.html', |
| 256 | 'type' => 'warning', |
| 257 | ], |
| 258 | 'invalid_role' => [ |
| 259 | 'desc' => 'Invalid ARIA role', |
| 260 | 'long_desc' => "This element is using a role that does not exist or is misspelled (e.g., <code>role=\"navigationn\"</code>). Roles must match valid ARIA specifications. Fix or remove the invalid role using $roles_link.", |
| 261 | 'show' => true, |
| 262 | 'copy' => true, |
| 263 | 'fix' => $roles_url, |
| 264 | 'level' => 'A', |
| 265 | 'wcag' => '4.1.2', |
| 266 | 'wcag_label' => 'Name, Role, Value', |
| 267 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html', |
| 268 | 'type' => 'error', |
| 269 | ], |
| 270 | 'redundant_role' => [ |
| 271 | 'desc' => 'Redundant or conflicting ARIA role', |
| 272 | 'long_desc' => "This element is using a role that is already implied by its HTML tag (e.g., <code><nav role=\"navigation\"></code>). Avoid redundant roles unless needed for clarification or assistive technology compatibility. Update or remove via $roles_link.", |
| 273 | 'show' => true, |
| 274 | 'copy' => true, |
| 275 | 'fix' => $roles_url, |
| 276 | 'level' => 'A', |
| 277 | 'wcag' => '1.3.1', |
| 278 | 'wcag_label' => 'Info and Relationships', |
| 279 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships.html', |
| 280 | 'type' => 'warning', |
| 281 | ], |
| 282 | ], |
| 283 | 'contrast' => [ |
| 284 | 'low_contrast' => [ |
| 285 | 'desc' => 'Has low contrast', |
| 286 | 'long_desc' => "This text has insufficient color contrast against the background. Aim for 4.5:1 or higher. You can manually override text and background colors using $contrast_link.", |
| 287 | 'show' => true, |
| 288 | 'copy' => true, |
| 289 | 'fix' => $contrast_url, |
| 290 | 'level' => 'AA', |
| 291 | 'wcag' => '1.4.3', |
| 292 | 'wcag_label' => 'Contrast (Minimum)', |
| 293 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html', |
| 294 | 'type' => 'error', |
| 295 | ], |
| 296 | ], |
| 297 | 'headings' => [ |
| 298 | 'skipped_level' => [ |
| 299 | 'desc' => 'Skips heading level', |
| 300 | 'long_desc' => "Heading levels should be used in order without skipping (e.g., h2 → h3, not h2 → h4). Update your theme/template structure accordingly.", |
| 301 | 'show' => true, |
| 302 | 'level' => 'A', |
| 303 | 'wcag' => '2.4.10', |
| 304 | 'wcag_label' => 'Section Headings', |
| 305 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/section-headings.html', |
| 306 | 'type' => 'warning', |
| 307 | ], |
| 308 | ], |
| 309 | 'media' => [ |
| 310 | 'missing_alt' => [ |
| 311 | 'desc' => 'Missing alt attribute', |
| 312 | 'long_desc' => "Images must include alt text for screen readers. Use role=\"presentation\" if decorative or add alt tags in $images_link.", |
| 313 | 'show' => true, |
| 314 | 'fix' => $images_url, |
| 315 | 'level' => 'A', |
| 316 | 'wcag' => '1.1.1', |
| 317 | 'wcag_label' => 'Non-text Content', |
| 318 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html', |
| 319 | 'type' => 'error', |
| 320 | ], |
| 321 | 'low_quality_alt' => [ |
| 322 | 'desc' => 'Low-quality alternative text', |
| 323 | 'long_desc' => "This image uses alt text that contains unnecessary words like 'image', file extensions like '.jpg', or generic descriptors such as 'spacer' or 'arrow' or 'logo'. These terms do not help screen reader users. Rewrite the alt text to be accurate, unique, and meaningful, or leave it blank if the image is decorative. Learn more in the $images_link.", |
| 324 | 'show' => true, |
| 325 | 'fix' => $images_url, |
| 326 | 'level' => 'AA', |
| 327 | 'type' => 'warning', |
| 328 | 'wcag' => '1.1.1', |
| 329 | 'wcag_label' => 'Non-text Content', |
| 330 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html', |
| 331 | ], |
| 332 | 'non_text_fallback' => [ |
| 333 | 'desc' => 'Non-text element has no fallback content', |
| 334 | 'long_desc' => "This <code><object></code>, <code><embed></code>, <code><svg></code>, or <code><canvas></code> element has no fallback text. Non-text content must provide an accessible alternative for screen reader users. This issue can only be fixed by adding the fallback content directly to the item.", |
| 335 | 'show' => true, |
| 336 | 'level' => 'A', |
| 337 | 'type' => 'error', |
| 338 | 'wcag' => '1.1.1', |
| 339 | 'wcag_label' => 'Non-text Content', |
| 340 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html', |
| 341 | ], |
| 342 | |
| 343 | ], |
| 344 | 'language' => [ |
| 345 | 'missing_lang' => [ |
| 346 | 'desc' => 'Missing the lang attribute', |
| 347 | 'long_desc' => "The <html> element should have a lang attribute to help screen readers pronounce content correctly. You can define it in $general_link.", |
| 348 | 'fix' => $general_url, |
| 349 | 'level' => 'A', |
| 350 | 'wcag' => '3.1.1', |
| 351 | 'wcag_label' => 'Language of Page', |
| 352 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/language-of-page.html', |
| 353 | 'type' => 'error', |
| 354 | ], |
| 355 | ], |
| 356 | 'links' => [ |
| 357 | 'duplicate_text' => [ |
| 358 | 'desc' => 'Duplicate link text with different destinations', |
| 359 | 'long_desc' => "Ensure each link has a unique and descriptive label. Avoid multiple links that only say “Read more.” Rewrite the anchor text in your content or use aria-labels via $aria_link.", |
| 360 | 'show' => true, |
| 361 | 'copy' => true, |
| 362 | 'fix' => $aria_url, |
| 363 | 'level' => 'A', |
| 364 | 'wcag' => '2.4.4', |
| 365 | 'wcag_label' => 'Link Purpose (In Context)', |
| 366 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/link-purpose-in-context.html', |
| 367 | 'type' => 'warning', |
| 368 | ], |
| 369 | 'improper_link_use' => [ |
| 370 | 'desc' => 'Link used without href or button role', |
| 371 | 'long_desc' => "This <code><a></code> element is missing an <code>href</code> attribute or only links to <code>#</code>, but does not have <code>role=\"button\"</code>. Links are meant for navigation. If the element triggers an action (like opening a menu or toggling content), use a <code><button></code> instead, or add <code>role=\"button\"</code> along with the appropriate ARIA attributes. Fix this in the $roles_link.", |
| 372 | 'show' => true, |
| 373 | 'copy' => true, |
| 374 | 'fix' => $roles_url, |
| 375 | 'level' => 'A', |
| 376 | 'type' => 'error', |
| 377 | 'wcag' => '4.1.2', |
| 378 | 'wcag_label' => 'Name, Role, Value', |
| 379 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html', |
| 380 | ], |
| 381 | |
| 382 | ], |
| 383 | 'taborder' => [ |
| 384 | 'positive_tabindex' => [ |
| 385 | 'desc' => 'Uses a positive tabindex value', |
| 386 | 'long_desc' => "Avoid tabindex values greater than 0 as they disrupt natural tab order. Use tabindex=\"0\" or let elements follow the default DOM order. You can fix this in $general_link.", |
| 387 | 'show' => true, |
| 388 | 'fix' => $general_url, |
| 389 | 'fix_type' => 'auto', |
| 390 | 'fix_scope' => 'safe', |
| 391 | 'fix_setting_tab' => 'ariaat_general_settings', |
| 392 | 'fix_setting_key' => 'fix_tabindex', |
| 393 | 'fix_value' => '1', |
| 394 | 'fix_label' => 'Enable Fix Tab Order', |
| 395 | 'fix_pro' => false, |
| 396 | 'level' => 'A', |
| 397 | 'wcag' => '2.4.3', |
| 398 | 'wcag_label' => 'Focus Order', |
| 399 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/focus-order.html', |
| 400 | 'type' => 'warning', |
| 401 | ], |
| 402 | ], |
| 403 | 'keyboard' => [ |
| 404 | 'non_focusable' => [ |
| 405 | 'desc' => 'Not focusable via keyboard', |
| 406 | 'long_desc' => "This element can’t be accessed using a keyboard (e.g. Tab, Enter, Space). It may be missing necessary HTML attributes or have JavaScript behavior that blocks focus. In some cases, this can be resolved by enabling Fix Tab Order in $general_link", |
| 407 | 'show' => true, |
| 408 | 'fix' => $general_url, |
| 409 | 'level' => 'A', |
| 410 | 'wcag' => '2.1.1', |
| 411 | 'wcag_label' => 'Keyboard', |
| 412 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/keyboard.html', |
| 413 | 'type' => 'error', |
| 414 | ], |
| 415 | 'focus_indicator' => [ |
| 416 | 'desc' => 'No visible focus indicator', |
| 417 | 'long_desc' => "This element receives focus but has no visible outline or indicator. Ensure interactive elements show a clear visual focus state using <code>:focus</code> or <code>:focus-visible</code> with <code>outline</code> or <code>box-shadow</code>. You can fix this in $general_link using Show Focus Outline setting.", |
| 418 | 'show' => true, |
| 419 | 'fix' => $general_url, |
| 420 | 'fix_type' => 'auto', |
| 421 | 'fix_scope' => 'safe', |
| 422 | 'fix_setting_tab' => 'ariaat_general_settings', |
| 423 | 'fix_setting_key' => 'focus_outline', |
| 424 | 'fix_value' => '1', |
| 425 | 'fix_label' => 'Enable Show Focus Outline', |
| 426 | 'fix_pro' => false, |
| 427 | 'level' => 'AA', |
| 428 | 'type' => 'error', |
| 429 | 'wcag' => '2.4.7', |
| 430 | 'wcag_label' => 'Focus Visible', |
| 431 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/focus-visible.html', |
| 432 | ], |
| 433 | |
| 434 | ], |
| 435 | 'page' => [ |
| 436 | 'missing_title' => [ |
| 437 | 'desc' => 'Missing a page title', |
| 438 | 'long_desc' => "Each page should have a unique and descriptive title tag to help users and screen readers understand the content. You can add a dynamic title using WordPress’s wp_title() or SEO plugins. This is something not easily fixable with a plugin like this.", |
| 439 | 'show' => false, |
| 440 | 'level' => 'A', |
| 441 | 'wcag' => '2.4.2', |
| 442 | 'wcag_label' => 'Page Titled', |
| 443 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/page-titled.html', |
| 444 | 'type' => 'error', |
| 445 | ], |
| 446 | ], |
| 447 | 'dom' => [ |
| 448 | 'duplicate_id' => [ |
| 449 | 'desc' => 'Uses a duplicate ID', |
| 450 | 'long_desc' => "Each ID in the DOM must be unique. Duplicate IDs can break scripts and confuse screen readers. Update your HTML to ensure each ID is only used once. This is something not easily fixable with a plugin like this.", |
| 451 | 'show' => true, |
| 452 | 'level' => 'A', |
| 453 | 'wcag' => '4.1.1', |
| 454 | 'wcag_label' => 'Parsing', |
| 455 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/parsing.html', |
| 456 | 'type' => 'error', |
| 457 | ], |
| 458 | ], |
| 459 | 'viewport' => [ |
| 460 | 'not_scalable' => [ |
| 461 | 'desc' => 'Viewport not scalable', |
| 462 | 'long_desc' => "The viewport meta tag contains <code>user-scalable=no</code>, which prevents users from zooming. This can impact accessibility for users who rely on pinch-to-zoom. You can fix this in $general_link", |
| 463 | 'fix' => $general_url, |
| 464 | 'fix_type' => 'auto', |
| 465 | 'fix_scope' => 'safe', |
| 466 | 'fix_setting_tab' => 'ariaat_general_settings', |
| 467 | 'fix_setting_key' => 'make_viewport_scalable', |
| 468 | 'fix_value' => '1', |
| 469 | 'fix_label' => 'Enable Make viewport not scalable', |
| 470 | 'fix_pro' => false, |
| 471 | 'level' => 'AA', |
| 472 | 'wcag' => '1.4.4', |
| 473 | 'wcag_label' => 'Resize Text', |
| 474 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/resize-text.html', |
| 475 | 'type' => 'warning', |
| 476 | ], |
| 477 | ], |
| 478 | 'navigation' => [ |
| 479 | 'missing_skip_link' => [ |
| 480 | 'desc' => 'No “Skip to content” link found', |
| 481 | 'long_desc' => "This page does not include a <code>skip to content</code> link. These links help keyboard and screen reader users bypass repetitive navigation and go straight to the main content. It should appear near the top of the page and link to an element like <code>#main</code> or <code>#content</code>. You can enable this feature in the $general_link.", |
| 482 | 'show' => true, |
| 483 | 'fix' => $general_url, |
| 484 | 'level' => 'A', |
| 485 | 'type' => 'error', |
| 486 | 'wcag' => '2.4.1', |
| 487 | 'wcag_label' => 'Bypass Blocks', |
| 488 | 'wcag_url' => 'https://www.w3.org/WAI/WCAG21/Understanding/bypass-blocks.html', |
| 489 | ], |
| 490 | |
| 491 | ], |
| 492 | |
| 493 | ]; |
| 494 | |
| 495 | return apply_filters( 'ariaat_error_definitions', $errors ); |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | new ARIAAT_Frontend_Checker(); |