[ 'level' => 'A', 'title' => 'Non-text Content', 'principle' => 'Perceivable', 'description' => 'All non-text content has a text alternative.', 'checks' => [ 'img_alt', 'input_image_alt', 'decorative_images' ], ], '1.3.1' => [ 'level' => 'A', 'title' => 'Info and Relationships', 'principle' => 'Perceivable', 'description' => 'Structure conveyed through presentation is available to assistive tech.', 'checks' => [ 'form_labels', 'table_headers', 'heading_structure' ], ], '2.1.1' => [ 'level' => 'A', 'title' => 'Keyboard', 'principle' => 'Operable', 'description' => 'All functionality is available from keyboard.', 'checks' => [ 'keyboard_trap', 'onclick_keyboard' ], ], '2.4.1' => [ 'level' => 'A', 'title' => 'Bypass Blocks', 'principle' => 'Operable', 'description' => 'Skip navigation links available.', 'checks' => [ 'skip_link' ], ], '2.4.2' => [ 'level' => 'A', 'title' => 'Page Titled', 'principle' => 'Operable', 'description' => 'Pages have descriptive titles.', 'checks' => [ 'page_title' ], ], '2.4.4' => [ 'level' => 'A', 'title' => 'Link Purpose', 'principle' => 'Operable', 'description' => 'Purpose of each link can be determined from text alone.', 'checks' => [ 'link_text', 'empty_links' ], ], '3.1.1' => [ 'level' => 'A', 'title' => 'Language of Page', 'principle' => 'Understandable', 'description' => 'HTML has lang attribute.', 'checks' => [ 'html_lang' ], ], '4.1.1' => [ 'level' => 'A', 'title' => 'Parsing', 'principle' => 'Robust', 'description' => 'Markup is valid, no duplicate IDs.', 'checks' => [ 'duplicate_ids', 'invalid_aria' ], ], '4.1.2' => [ 'level' => 'A', 'title' => 'Name, Role, Value', 'principle' => 'Robust', 'description' => 'All UI components have accessible name and role.', 'checks' => [ 'button_labels', 'aria_roles', 'input_names' ], ], // ── Level AA ─────────────────────────────────────────────── '1.4.3' => [ 'level' => 'AA', 'title' => 'Contrast (Minimum)', 'principle' => 'Perceivable', 'description' => 'Text contrast ratio of at least 4.5:1 (3:1 for large text).', 'checks' => [ 'color_contrast' ], ], '1.4.4' => [ 'level' => 'AA', 'title' => 'Resize Text', 'principle' => 'Perceivable', 'description' => 'Text can be resized to 200% without loss of content.', 'checks' => [ 'text_resize' ], ], '1.4.11' => [ 'level' => 'AA', 'title' => 'Non-text Contrast', 'principle' => 'Perceivable', 'description' => 'UI components and graphics have contrast of 3:1.', 'checks' => [ 'ui_contrast' ], ], '2.4.6' => [ 'level' => 'AA', 'title' => 'Headings and Labels', 'principle' => 'Operable', 'description' => 'Headings and labels describe topic or purpose.', 'checks' => [ 'heading_structure', 'empty_headings' ], ], '2.4.7' => [ 'level' => 'AA', 'title' => 'Focus Visible', 'principle' => 'Operable', 'description' => 'Keyboard focus indicator is visible.', 'checks' => [ 'focus_visible' ], ], // ── WCAG 2.2 ─────────────────────────────────────────────── '2.4.11' => [ 'level' => 'AA', 'title' => 'Focus Not Obscured (Minimum)', 'principle' => 'Operable', 'description' => 'Focused component is not entirely hidden by sticky/fixed elements.', 'checks' => [ 'focus_not_obscured' ], 'version' => '2.2', ], '2.5.7' => [ 'level' => 'AA', 'title' => 'Dragging Movements', 'principle' => 'Operable', 'description' => 'All drag operations have a single-pointer alternative.', 'checks' => [ 'dragging_movements' ], 'version' => '2.2', ], '2.5.8' => [ 'level' => 'AA', 'title' => 'Target Size (Minimum)', 'principle' => 'Operable', 'description' => 'Interactive targets are at least 24x24 CSS pixels.', 'checks' => [ 'target_size' ], 'version' => '2.2', ], ]; } public static function get( $id ) { return self::get_all()[ $id ] ?? null; } public static function get_by_level( $level = 'AA' ) { return array_filter( self::get_all(), function( $rule ) use ( $level ) { if ( $level === 'AA' ) return true; // AA includes A return $rule['level'] === 'A'; } ); } }