Accessibility Rule Repository
Every accessibility rule TestMu AI evaluates across web, Android, and iOS, mapped to its WCAG success criterion with concise remediation guidance. Switch platforms with the tabs below.
- Web (37)
- Android (34)
- iOS (33)
Web rules map directly to WCAG success criteria.
| Rule | WCAG | What it checks | How to fix |
|---|---|---|---|
| Non-text Content (1.1.1) | 1.1.1 (A) | Meaningful non-text content exposes an accessible text alternative that explains its purpose or information. | • add accurate alt text for meaningful content • mark decorative content as decorative so it is ignored correctly • ensure icon-only controls expose an accessible name through the appropriate attribute or label |
| Info and Relationships (1.3.1) | 1.3.1 (A) | Headings, lists, tables, form groups, and landmark regions use correct semantic HTML rather than relying on visual appearance alone. | • use native semantic elements for headings, lists, tables, and form groups • add ARIA landmarks only when native elements are not available • verify heading levels follow a logical nesting order without skipping levels • test with a screen reader to confirm the announced structure matches the visual layout |
| Orientation (1.3.4) | 1.3.4 (AA) | CSS or viewport meta configurations that lock the page to a single orientation, preventing users from rotating their device. | • support both portrait and landscape orientations through responsive design • remove JavaScript orientation locks unless the content genuinely requires a specific orientation (e.g., a piano keyboard app) • test the layout in both orientations on real devices • if a specific orientation is essential, clearly document why |
| Use of Color (1.4.1) | 1.4.1 (A) | Instances where meaning is communicated exclusively through color without a secondary visual indicator such as text, icons, patterns, or underlines. | • pair color with a text label, icon, pattern, or border change • underline links or add a visible icon to distinguish them from surrounding text • use patterns, hatching, or data labels in charts alongside color • test the interface with a color-blindness simulator to confirm the secondary cue is visible |
| Reflow (1.4.10) | 1.4.10 (AA) | Content that causes horizontal scrolling at 320px viewport width (equivalent to 400% zoom on a 1280px desktop display). | • use responsive CSS (relative units, max-width, flexbox/grid) to allow single-column reflow• wrap wide tables in a scrollable container so the rest of the page still reflows • set images to max-width: 100% so they scale within their container• test at 320px viewport width (or 400% zoom) and fix any horizontal overflow |
| Non-text Contrast (1.4.11) | 1.4.11 (AA) | The contrast of interactive component boundaries (input borders, button outlines, focus indicators) and meaningful graphical objects (icons, chart segments) against their backgrounds. | • ensure input and button borders meet the 3:1 ratio against adjacent background colors • verify icon contrast against both light and dark backgrounds • add borders or patterns to chart segments alongside color fills • test all interactive states (default, hover, focus, active) for contrast compliance |
| Text Spacing (1.4.12) | 1.4.12 (AA) | Content is clipped, truncated, or overlapping when text spacing is increased to WCAG-specified thresholds: line height 1.5x font size, paragraph spacing 2x font size, letter spacing 0.12x font size, word spacing 0.16x font size. | • avoid fixed heights on text containers; use min-height or auto-height• do not use overflow: hidden on elements that contain user-facing text• test with a text-spacing bookmarklet that applies the WCAG thresholds • ensure layouts flex and reflow when text occupies more space |
| Audio Control (1.4.2) | 1.4.2 (A) | Pages where audio or video with sound starts automatically and runs beyond three seconds without an accessible pause or volume control. | • never autoplay audio; if autoplay is required, start muted and let the user opt in • provide a visible, keyboard-accessible pause or mute control near the top of the page • ensure the control is the first or one of the first focusable elements so screen-reader users reach it quickly |
| Contrast (Minimum) (1.4.3) | 1.4.3 (AA) | Text and important UI elements meet the minimum contrast requirements needed for readable, usable interfaces. | • increase the contrast between foreground and background colors • verify contrast after hover, focus, and active-state styling is applied • avoid relying on color tokens that look correct in one theme but fail in another |
| Keyboard (2.1.1) | 2.1.1 (A) | Interactive elements (links, buttons, form controls, and custom widgets) are reachable and operable using keyboard alone: Tab, Shift+Tab, Enter, Space, and arrow keys as appropriate. | • use native interactive elements (<button>, <a>, <select>) whenever possible• add tabindex="0" and keyboard event listeners to custom interactive widgets• provide keyboard-equivalent actions for drag-and-drop, swipe, and hover interactions • test the entire flow using only the keyboard (Tab, Enter, Space, Escape, arrows) |
| No Keyboard Trap (2.1.2) | 2.1.2 (A) | For components that capture keyboard focus and prevent the user from tabbing or escaping out using standard keys. | • ensure Escape closes modals and returns focus to the trigger element • implement focus trapping in modals correctly: trap while open, release on close • allow Tab to exit custom editors (provide a documented key combination or a skip link) • test by tabbing into and then out of every interactive region on the page |
| Character Key Shortcuts (2.1.4) | 2.1.4 (A) | Keyboard shortcuts bound to single character keys that are active globally on the page rather than scoped to a focused component. | • require a modifier key (Ctrl, Alt, Shift) for global shortcuts • scope single-character shortcuts so they only work when the relevant component is focused • provide a settings UI to disable or remap character shortcuts • if modifier keys are not feasible, ensure the shortcut deactivates when a text input has focus |
| Timing Adjustable (2.2.1) | 2.2.1 (A) | Session timeouts, auto-redirects, and auto-updating content that impose a fixed time limit without user control. | • provide a warning at least 20 seconds before a timeout and allow the user to extend it • allow users to disable or increase time limits in account or session settings • add pause, stop, and resume controls to auto-advancing content • for real-time exceptions (auctions, live events), clearly document the constraint |
| Pause, Stop, Hide (2.2.2) | 2.2.2 (A) | Animations, auto-scrolling regions, and auto-updating feeds that run for more than five seconds without user controls. | • add visible pause, stop, or hide controls for any auto-moving content • respect the prefers-reduced-motion media query to minimize or remove animation• for live feeds, allow the user to opt in to updates rather than pushing them automatically • ensure controls are keyboard accessible and announced by screen readers |
| Three Flashes or Below Threshold (2.3.1) | 2.3.1 (A) | Content that produces rapid flashing (more than three flashes per second) in a large enough area to exceed safety thresholds. | • limit flashing to three or fewer per second in any region of the page • use the Photosensitive Epilepsy Analysis Tool (PEAT) to test video and animation content • provide a warning before content that contains known flashing and allow the user to skip it • respect prefers-reduced-motion to suppress flashing animations |
| Bypass Blocks (2.4.1) | 2.4.1 (A) | Verifies the presence of skip-navigation links, landmark regions, or heading structures that allow users to bypass repeated content and jump directly to the main content area. | • add a "Skip to main content" link as the first focusable element, visible on focus • use <main> to wrap primary content and <nav> for navigation blocks• ensure heading levels provide meaningful structure users can navigate by • test by pressing Tab immediately after page load to confirm the skip link appears and works |
| Focus Not Obscured (Minimum) (2.4.11) | 2.4.11 (AA) | Focused elements are not fully concealed behind sticky navigation bars, cookie banners, chat widgets, or other fixed-position elements. | • add scroll padding (e.g., scroll-padding-top) to account for sticky header height• ensure cookie banners and chat widgets do not overlap focusable page content • implement scrollIntoView with appropriate offsets when managing focus programmatically• test by tabbing through the entire page and confirming every focused element is at least partially visible |
| Page Titled (2.4.2) | 2.4.2 (A) | Each page has a non-empty <title> element and that the title text meaningfully describes the page content. | • write titles in the format "Page Purpose - Site Name" (e.g., "Order History - Acme Store") • update document.title on route changes in single-page applications• include key differentiators when pages are similar (e.g., "Edit Profile" vs. "View Profile") • keep titles concise but specific enough to identify the page without seeing it |
| Focus Order (2.4.3) | 2.4.3 (A) | The tab order of interactive elements follows a logical reading sequence and does not jump unexpectedly between unrelated sections. | • rely on DOM source order rather than positive tabindex values• ensure CSS visual reordering matches the underlying DOM sequence • move focus into modals, dialogs, and drawers when they open, and return it when they close • test with keyboard-only navigation to confirm the sequence feels natural |
| Link Purpose in Context (2.4.4) | 2.4.4 (A) | Links whose text is generic (e.g., "click here", "read more", "learn more") without surrounding context that clarifies the destination or action. | • write link text that describes the destination or action (e.g., "View order #1234" instead of "Click here") • use aria-label or aria-labelledby to provide context when the visible text must remain short• add aria-label to icon-only links that describes the action• when multiple links share text, add visually hidden context (e.g., "Read more about pricing") |
| Focus Visible (2.4.7) | 2.4.7 (AA) | Users can see where keyboard focus currently is as they move through interactive elements. | • keep the default focus indicator or replace it with an equally visible custom style • test focus styling on links, buttons, form fields, menus, and dialogs • verify focus visibility in light and dark themes |
| Pointer Gestures (2.5.1) | 2.5.1 (A) | Interactions that require multi-touch gestures (pinch, two-finger swipe) or precise path-based gestures (drawing a shape, swiping in a specific direction) without a single-click or single-tap alternative. | • add button controls for zoom, pan, and delete alongside gesture support • provide single-tap or single-click alternatives for every multi-point gesture • for path-based input, offer an alternative method (e.g., typed input, button sequences) • test with a single mouse click/tap only to confirm all features remain accessible |
| Pointer Cancellation (2.5.2) | 2.5.2 (A) | Interactive elements do not fire irreversible actions on mousedown or touchstart alone, allowing users to abort by moving the pointer away before releasing. | • use click events rather than mousedown or touchstart for actions• if down-event activation is needed, provide an undo or confirmation step • implement drag cancellation by allowing users to drop outside the target zone • test by pressing down on a button, dragging away, and releasing to confirm no action occurs |
| Label in Name (2.5.3) | 2.5.3 (A) | Compares the visible label text with the computed accessible name and flags mismatches where the visible text is not included in the accessible name. | • ensure the accessible name starts with or contains the visible label text • prefer visible <label> elements over aria-label when visible text is available• when aria-label is needed, include the visible text within it (e.g., aria-label="Submit order form" for a button labeled "Submit")• test with voice-input software to confirm controls can be activated by speaking their visible label |
| Motion Actuation (2.5.4) | 2.5.4 (A) | Interactions driven by device sensors (accelerometer, gyroscope, camera) that do not offer an alternative button or control and cannot be disabled. | • provide an on-screen button or control for every motion-triggered action • allow users to disable motion-based activation in settings • if motion is essential to the functionality (e.g., a step counter), document the exception • test with the device in a fixed position to confirm all functionality is reachable through UI controls |
| Dragging Movements (2.5.7) | 2.5.7 (AA) | Drag-and-drop interactions, slider controls, and reorderable lists that do not offer an alternative single-click or keyboard-based method. | • add move-up/move-down buttons or a "Move to" menu for reorderable lists • support click-on-source then click-on-destination as an alternative to drag-and-drop • ensure sliders can be adjusted with arrow keys, or provide a text input alternative • always include a file browse button alongside drag-and-drop upload zones |
| Target Size (Minimum) (2.5.8) | 2.5.8 (AA) | Measures the rendered size of interactive elements (buttons, links, form controls) and flags those smaller than 24x24 CSS pixels that do not meet any exception criteria. | • ensure clickable areas are at least 24x24 CSS pixels (44x44 is recommended for touch interfaces) • add padding or transparent borders to increase the hit area without changing the visual size • use min-width and min-height on interactive elements• increase spacing between adjacent small targets so users can tap accurately |
| On Focus (3.2.1) | 3.2.1 (A) | Elements that initiate a context change (navigation, submit, new window) when they receive keyboard focus rather than when they are explicitly activated. | • trigger context changes only on explicit activation (click, Enter, Space) • for select menus that act as navigation, add a separate "Go" button • never attach onfocus handlers that cause navigation, submission, or window changes• test by tabbing through all interactive elements without pressing Enter |
| On Input (3.2.2) | 3.2.2 (A) | Form controls that trigger navigation, submission, or DOM restructuring on value change without prior notice to the user. | • separate the selection action from the submission action (add a "Submit" or "Apply" button) • if auto-submission is intentional, inform the user in advance (e.g., "Selecting a value will update the page") • use ARIA live regions to announce dynamic content changes without a full context switch • test all form controls by changing values and confirming no unexpected navigation occurs |
| Consistent Help (3.2.6) | 3.2.6 (A) | Help-related UI elements maintain consistent placement and ordering across pages within the same site. | • place help mechanisms in the same relative position across all pages (e.g., always last in the footer) • use a shared layout component to ensure consistent ordering automatically • if multiple help options exist, keep their order fixed (e.g., FAQ, then Chat, then Email) • audit help element placement across page templates to confirm consistency |
| Error Identification (3.3.1) | 3.3.1 (A) | Form validation errors are communicated through visible text messages associated with the relevant field, not only through color or icon changes. | • display a text error message adjacent to or within the erroneous field • associate the error message with the field using aria-describedby or aria-errormessage• set aria-invalid="true" on the field when an error is detected• move focus to the first error or provide a summary with links to each erroneous field |
| Labels or Instructions (3.3.2) | 3.3.2 (A) | Form inputs have visible labels programmatically associated with the control, and that fields requiring specific formats include instructions. | • add a visible <label> element with a matching for attribute for every form control• include format instructions or examples near the field (e.g., "Enter date as MM/DD/YYYY") • use <fieldset> and <legend> for logically grouped fields• never use placeholder as a substitute for a visible label |
| Redundant Entry (3.3.7) | 3.3.7 (A) | Multi-step flows where users are asked to re-enter information they have already provided in a previous step (e.g., name, address, email) without auto-fill or a selection option. | • auto-populate fields with information provided in earlier steps of the same process • offer a "same as shipping" checkbox for billing address forms • use session storage or form state management to carry data across steps • if security requires re-entry (e.g., password confirmation), document the exception |
| Accessible Authentication (Minimum) (3.3.8) | 3.3.8 (AA) | Login flows that rely solely on cognitive challenges (CAPTCHAs, image puzzles, memory-based knowledge questions) without offering an accessible alternative. | • allow password managers to auto-fill login fields (do not use autocomplete="off" on authentication fields)• provide an accessible CAPTCHA alternative (audio CAPTCHA, email verification, or WebAuthn) • support passwordless authentication (magic links, biometrics, passkeys) • allow pasting into verification code fields for users who receive codes via email or authenticator apps |
| Parsing (4.1.1) | 4.1.1 (A) | Validates HTML for duplicate IDs, malformed nesting, unclosed elements, and duplicate attributes that could confuse assistive technology parsers. | • run an HTML validator to catch structural issues • ensure every id is unique within the page• fix unclosed or misnested elements • avoid nesting interactive elements inside each other |
| Name, Role, Value (4.1.2) | 4.1.2 (A) | Interactive elements have an accessible name, an appropriate ARIA or native role, and that state changes (expanded, checked, selected, disabled) are communicated programmatically. | • use native HTML elements whenever possible (<button>, <input>, <select>)• for custom widgets, add the appropriate ARIA role and maintain state attributes ( aria-expanded, aria-checked, aria-selected)• ensure every interactive element has an accessible name through visible text, aria-label, or aria-labelledby• update ARIA states dynamically when the user interacts with the control |
| Status Messages (4.1.3) | 4.1.3 (AA) | Dynamic status messages use ARIA live regions (role="status", role="alert", aria-live) so screen readers announce them without moving focus away from the current task. | • use role="status" for non-urgent updates (result counts, save confirmations)• use role="alert" for urgent messages (errors, warnings)• ensure the live region exists in the DOM before the content is injected (add content to an existing container rather than injecting the container) • avoid overusing alerts, since frequent announcements interrupt the user's workflow |
Android rules map to the closest WCAG criterion and cover TalkBack behavior.
| Rule | WCAG | What it checks | How to fix |
|---|---|---|---|
| Button Element Capitalisation Check | 3.1.6 (AAA) | Reviews button labels — including clickable containers acting as buttons — for irregular capitalization: labels that begin with a lowercase letter, mid-word capitalization (e.g., "SubMit", "LogIn"), or an unnecessary capital after a hyphen in hyphenated labels (e.g., "Sign-In" instead of "Sign-in"). | • use sentence case (e.g., "Save changes") in the underlying string instead of all caps or inconsistent capitalization • if a stylized all-caps look is desired visually, apply it through styling ( android:textAllCaps) rather than in the source string, so the underlying accessible text remains normally cased• keep hyphenated labels in sentence case too — "Sign-in", not "Sign-In" |
| Duplicate Accessibility Label | 4.1.2 (A) | Groups of visible, interactive elements on the same screen that share an identical accessibility label — including duplicates across different widget types, such as a Button and a Switch both labeled the same way. All elements in a duplicate group are reported together so they can be reviewed as a set. | • ensure each interactive element has a unique accessibility label • make labels contextual — include the item they act on: "Delete — Order #1234", "More options for John's playlist" • review list and grid layouts first, since repeated item templates are the most common source of duplicates |
| Dynamic Type Support | 1.4.4 (AA) | Examines whether on-screen text is sized in sp (scale-independent pixels) — the only Android text unit that responds to the user's system-wide font-size preference. Text sized in fixed units (dp, px, pt, in, mm) renders at a fixed size no matter what the user configures. :::note The required device data is available on Android 11+ only; on older devices the rule passes silently rather than flagging an issue. ::: | • replace fixed-unit text sizes with scale-independent units so the accessibility font-scale takes effect — use sp for android:textSize instead of dp or px, and avoid setting a fixed text size on TextView / Button / EditText• test at the system's largest font-size setting and confirm no text clips or truncates (see Non-Responsive Container Clips Text for the layout side of this) |
| Emoji or Symbol Used in Accessibility Label | Interactive elements whose accessible label is made up mostly or entirely of emoji, arrows, geometric shapes, or other special symbols instead of descriptive text. A label is flagged when half or more of its meaningful characters are emoji or symbols. Ordinary punctuation is acceptable; static (non-interactive) text is not checked. | • determine intent — check whether the emoji is functional (represents an action) or decorative • replace functional emoji with a descriptive verb, e.g. change "❤️" to "Add to Favorites" • hide decorative icons from the focus order instead of labeling them — set android:importantForAccessibility="no" | |
| Fixed Orientation Lock (Android) | 1.3.4 (AA) | Activities with android:screenOrientation set to a fixed value (portrait, landscape, sensorPortrait, sensorLandscape) in the manifest, preventing device rotation. | • remove android:screenOrientation from the manifest (defaults to user-controlled rotation)• only lock orientation when the content genuinely requires it (e.g., a camera viewfinder) • test the app in both orientations to confirm layouts adapt correctly • document any essential orientation requirement with a clear justification |
| Images with Text | 1.1.1 (A) | Examines images on the screen for meaningful text rendered inside the image itself — text that exists only as pixels, with no real text element carrying it. Detection is AI-based, analyzing the rendered screen rather than the view hierarchy alone. (AI rule) | • avoid embedding meaningful text inside images — use real text views (TextView) so TalkBack can read the content, or provide the text through android:contentDescription• if the image is decorative, mark it as such ( android:importantForAccessibility="no") so screen readers skip it |
| Inaccessible Text Focus | 4.1.2 (A) | TextView and other text-bearing elements that are not reachable through TalkBack navigation because they are marked as not important for accessibility or are hidden behind non-focusable containers. | • ensure text elements containing meaningful content have importantForAccessibility="yes"• do not suppress descendant accessibility on containers that hold readable text • remove decorative text from the accessibility tree with importantForAccessibility="no" (only when truly decorative)• test by swiping through the screen with TalkBack and confirming all meaningful text is announced |
| Insufficient Color Ratio | 1.4.3 (AA) | Measures the contrast ratio between foreground text or icon colors and their background, flagging pairs that fall below the 4.5:1 ratio for normal text or 3:1 for large text. | • verify text contrast against backgrounds using a color contrast analyzer • use Material Design color tokens that are pre-validated for contrast compliance • ensure all text states (enabled, disabled, error, hint) meet minimum ratios • test on multiple devices and brightness levels to catch theme-specific failures |
| Insufficient Target Spacing | 2.5.5 (AAA) | Measures the gap between adjacent clickable or focusable elements and flags pairs that are too close together, increasing the risk of mis-taps. | • add at least 8dp of spacing between adjacent interactive elements • use android:layout_margin or Compose Modifier.padding to create clear separation• consider grouping related actions into an overflow menu to reduce density • test on the smallest supported screen size to confirm spacing remains adequate |
| Interactive Role Undefined (Android) | 4.1.2 (A) | Interactive elements (buttons, switches, checkboxes, sliders) that do not expose a recognized accessibility role to the Android accessibility framework. | • use native Android widgets (Button, Switch, CheckBox) whenever possible• for custom views, override getAccessibilityClassName() to return the appropriate widget class name• in Jetpack Compose, set role = Role.Button (or the appropriate role) inside Modifier.semantics { }• test with TalkBack to confirm the announced role matches the element's behavior |
| Invalid Range Values | 4.1.2 (A) | Examines range controls — sliders (SeekBar), progress bars, steppers — and reports controls whose range values are inconsistent: the current value falls outside the declared min/max range, or the min/max range itself is invalid. | • ensure min < max and the current value falls within [min, max] on the control (SeekBar / Slider)• for custom range controls, expose the range to assistive technology via AccessibilityNodeInfo.RangeInfo |
| Meaningful Sequence | 1.3.2 (A) | Examines whether the programmatic reading order of the screen matches the meaningful visual sequence of its content. Detection is AI-based, comparing the announced order against the visual layout. (AI rule) Where Traversal Order Does Not Match Visual Layout targets misused traversal overrides on focusable controls, this rule concerns the sequence of the content itself. | • reorder views in the layout to match the visual reading order • use android:accessibilityTraversalBefore / android:accessibilityTraversalAfter to set the correct TalkBack traversal sequence when reordering in code isn't possible |
| Minimum Text Size | Examines on-screen text and reports elements rendered below the recommended minimum size (16dp on Android) that do not scale with the user's font preference. | • increase any non-scalable text (dp/px) below 16dp to at least the recommended minimum • prefer sp so text scales with the user's font preference (see Dynamic Type Support) | |
| Mismatched Label Text (Android) | 2.5.3 (A) | Compares the visible text of interactive elements with their computed accessible name (from contentDescription or labeling relationships) and flags cases where the accessible name does not contain the visible text. | • the accessible name must contain the visible label as a substring, ignoring case and punctuation. Prefer an exact match; where extra context is needed, put the visible label first: visible "Delete" may be named "Delete" or "Delete, removes item", not "Remove item" • never use developer identifiers as accessible names • Android: contentDescription overrides visible text — if you set it, keep the on-screen string inside it |
| Misplaced Field Label (Android) | 2.5.3 (A) | Controls whose accessible name contains their visible text but does not begin with it. This is the companion to Mismatched Label Text: where that rule requires the visible text to appear in the accessible name at all, this one requires it to appear at the start. | • start the accessible name with the visible label; put any extra context after it ("Edit, order summary", not "Order summary, Edit") • Android: if contentDescription is set on a control with visible text, begin it with that text |
| Missing Field Label | 3.3.2 (A) | Android input controls expose clear labels that describe the purpose of the field to assistive technologies. | • provide a visible and accessible label for each field • connect the label and field programmatically when the UI framework supports it • avoid relying on hints alone when they disappear after input starts |
| Missing Image Alt | 1.1.1 (A) | Meaningful Android images expose a useful accessibility description for TalkBack and related assistive technologies. | • add a clear contentDescription to meaningful images• use an empty description for decorative content when appropriate • verify that image-based controls expose both purpose and action |
| Missing Input Value | 4.1.2 (A) | EditText, AutoCompleteTextView, and custom input elements expose their current text value through the accessibility framework. | • use standard EditText or TextInputLayout which expose values automatically• for custom inputs, populate AccessibilityNodeInfo.setText() with the current value• fire TYPE_VIEW_TEXT_CHANGED events when the value changes programmatically• test with TalkBack by focusing the input field and confirming the current value is announced |
| Missing Screen Title | 2.4.2 (A) | Each Activity or navigable screen exposes a title through the android:label attribute, toolbar title, or accessibility pane title. | • set android:label on every <activity> in AndroidManifest.xml• for fragment-based navigation, call ViewCompat.setAccessibilityPaneTitle(view, "Screen Name")• ensure toolbar titles reflect the specific screen, not just the app name • test by navigating between screens with TalkBack to confirm announcements |
| Missing View Accessibility | 4.1.2 (A) | Views that carry meaningful content or functionality but are marked as importantForAccessibility="no" or have focusable="false" when they should be focusable. | • set importantForAccessibility="yes" on views that carry meaningful content• only mark truly decorative elements as importantForAccessibility="no"• ensure custom views implement AccessibilityDelegate or override accessibility methods• test with TalkBack to confirm all meaningful elements are announced |
| Nested Control Issues | 4.1.2 (A) | Interactive views (buttons, switches, checkboxes) that are nested within other interactive containers (clickable layouts, other buttons), creating a confusing accessibility tree. | • flatten the interactive hierarchy: either the container or its children should be interactive, not both • use android:importantForAccessibility="no" on the container if individual child controls should receive focus• for list items with multiple actions, use AccessibilityAction to expose additional actions through TalkBack's actions menu• test by swiping through the screen with TalkBack to confirm each control is individually reachable |
| Non-accessible Interaction (Android) | 4.1.2 (A) | Elements that respond to touch events but are not reachable or activatable through TalkBack gestures (double-tap to activate, swipe to navigate). | • implement OnClickListener alongside any touch listeners• add AccessibilityAction entries for custom gestures (long press, swipe)• for canvas-drawn elements, create virtual child accessibility nodes using ExploreByTouchHelper• test with TalkBack to confirm every interactive element can be reached by swiping and activated by double-tapping |
| Non-Descriptive Link Text | 2.4.4 (A) | Links whose entire accessible name is a generic call-to-action that says nothing about the destination — labels like "Click here", "Read more", "Learn more", "Tap here", "More info", "Details", "Go", or "Link". Only exact matches are flagged: "Click here for pricing details" passes; a link whose whole text is "Click here" fails. The rule applies to elements containing actual links (URL-backed text). | • replace vague link text with a label that says what the link does (e.g., "Read more" → "Read our privacy policy") • if the visible text can't be changed, add a descriptive accessible label instead — set android:contentDescription with a clear label, or rewrite the visible android:text |
| Non-Focusable Interactive Element | 2.1.1 (A) | Examines visible, enabled interactive elements and reports any that respond to touch but cannot receive input (keyboard/d-pad) focus, and have no focusable interactive ancestor or descendant handling the action on their behalf. | • add android:focusable="true" or view.isFocusable = true on custom clickable views, or use native controls (Button, EditText, CheckBox, Switch), which are focusable by default• in Compose, use Modifier.clickable {} inside a Button, or add .focusable() to custom clickable containers• where a container handles its children's click (e.g., a list row), make the container both focusable and clickable so keyboard users can reach the action |
| Non-Responsive Container Clips Text | 1.4.10 (AA) | Examines whether layout containers that hold text use flexible sizing (match_parent, wrap_content, or 0dp with layout_weight) rather than fixed pixel dimensions. Containers without any text content are not checked — fixed dimensions are legitimate for icons and decorative elements. :::note The required device data is available on Android 11+ only; on older devices the rule reports Incomplete. ::: | • identify the container that wraps the clipped text • replace fixed dimensions with flexible sizing so the container grows with its content — use match_parent, wrap_content, or 0dp with layout_weight instead of a fixed dp/px dimension. In ConstraintLayout, use 0dp with constraint chains rather than fixed sizes. |
| Redundant Role Keyword in Accessibility Label | 4.1.2 (A) | Accessible names that embed the control's role — words like "button", "switch", "checkbox", "slider", "dropdown" — on controls whose role TalkBack already announces. Common informal variants are also flagged ("btn", "toggle", "text field", "picker"), since they encode the same role intent. Only role words matching the actual control type are considered: "Play button" on a text label that is not a button is not flagged. | • identify role keywords — find words like "button", "link", or "icon" in the label that describe the control's type • remove them — "Settings Icon" becomes "Settings" • assign the role programmatically instead — use the Button class, or role = Role.Button in Compose, so TalkBack announces it once, correctly |
| Redundant State Keyword in Accessibility Label | 4.1.2 (A) | Accessible names that embed the control's state — words like "on", "off", "checked", "selected", "disabled" — on controls whose state TalkBack already announces automatically. Only state words TalkBack actually speaks for that control type are flagged: "on"/"off" for switches and toggles, "checked"/"not checked" for checkboxes, "selected" for selected elements, and "disabled" for disabled interactive elements. | • audit dynamic text and remove status words like "On", "Checked", or "Expanded" from the label string • stabilize the label — keep it static (e.g., "Notifications") rather than describing its current state • map state to platform APIs instead — use stateDescription or android:checked="true" so TalkBack announces it correctly and it stays in sync |
| Text Spacing | 1.4.12 | Examines the rendered spacing of on-screen text and reports three distinct problems: line height, word spacing, or paragraph gaps rendered below a readability floor. Thresholds are calibrated for mobile typography, so default Android text styles pass. | • keep android:lineSpacingMultiplier at 1.0 or above and avoid negative android:letterSpacing• on typefaces with narrow default word spacing, set android:letterSpacing="0.05" explicitly on body text• separate consecutive multi-line TextViews with android:layout_marginTop |
| Traversal Order Does Not Match Visual Layout | 2.4.3 (A) | Examines misuse of Android's reading-order overrides (accessibilityTraversalBefore / accessibilityTraversalAfter, and Compose's traversalIndex) that break TalkBack navigation. Two failure modes are reported: 1. Circular traversal chains — overrides that form a loop, leaving TalkBack's reading order undefined and potentially trapping users cycling among the same elements. 2. Reading order contradicts visual order — the developer-defined traversal chain reads elements in a different sequence than they appear on screen (top-to-bottom, left-to-right). | • compare what you see on screen with the order TalkBack reads it • reorder elements in the view hierarchy so their code order matches the visual layout — the default (layout) order is correct for most screens, so prefer removing overrides • if reordering in code isn't possible, set the reading order deliberately with android:accessibilityTraversalBefore / android:accessibilityTraversalAfter, and verify the full chain reads in a meaningful sequence with no loops |
| Undersized Touch Target | 2.5.5 (AAA) | Measures the rendered size of clickable and focusable elements and flags those smaller than 48x48 dp (the Android accessibility guideline minimum). | • set android:minWidth and android:minHeight to at least 48dp on all interactive elements• use android:padding or TouchDelegate to expand the touch area without changing the visual size• in Jetpack Compose, use Modifier.sizeIn(minWidth = 48.dp, minHeight = 48.dp)• Material Design components enforce this by default; prefer using them over custom implementations |
| Unique Option Names | 4.1.2 (A) | Examines selection groups — radio groups, tabs, segmented pickers — and reports options within the same group that share an identical accessible name. Unlike Duplicate Accessibility Label, which compares elements across the whole screen, this rule looks specifically inside a single selection group, where identically named options make the choice itself meaningless. | • give every option within a picker, radio group, or segmented control a distinct accessible name so screen reader users can tell them apart • don't rely on position or visual styling to differentiate identically labeled options |
| Unlabeled Checkbox Element | 4.1.2 (A) | CheckBox and checkbox-like views that have no contentDescription, no associated <label>, and no text content, leaving TalkBack with nothing to announce beyond "Checkbox, not checked." | • set the android:text attribute on the CheckBox element• if visual text is provided by a separate TextView, use android:labelFor on the label pointing to the checkbox• for icon-only checkboxes, set contentDescription to describe the purpose• test with TalkBack and confirm the announced text clearly identifies the option |
| Unlabeled Toggle Control | 4.1.2 (A) | Switch, ToggleButton, and SwitchCompat elements that have no accessible label, leaving TalkBack to announce only the state ("On" / "Off") without context. | • set android:text on the toggle control to describe the setting• if the label is a separate view, use android:labelFor on the label pointing to the toggle• for Jetpack Compose, set a description in Modifier.semantics { contentDescription = "..." }• test with TalkBack to confirm the full announcement includes both the label and the state |
| Unnamed Nested Element | 4.1.2 (A) | Nested views (images, icons, text) inside a focusable parent container that do not have their own accessible label, causing their meaning to be lost in TalkBack's aggregated announcement of the parent. | • add contentDescription to each meaningful child element within a group• if the parent should aggregate children, compose the parent's contentDescription to include all relevant child information• use android:importantForAccessibility="no" only on truly decorative children• test with TalkBack at different granularity levels (character, word, element) to confirm all meaningful content is announced |
iOS rules map to the closest WCAG criterion and cover VoiceOver behavior.
| Rule | WCAG | What it checks | How to fix |
|---|---|---|---|
| Accessibility Label Not Punctuated | 3.3.2 (A) | Accessibility labels that do not end with proper punctuation (period, comma, question mark, or exclamation mark), which affects VoiceOver's speech cadence and clarity. | • end accessibility labels with a period when they form a complete phrase or sentence • use punctuation consistent with the label's tone: period for statements, question mark for questions • keep labels concise: a label should describe the element, not be a paragraph • test with VoiceOver to confirm the speech rhythm sounds natural between consecutive elements |
| Accessibility Role Definition | 4.1.2 (A) | Interactive elements have appropriate accessibilityTraits set, such as .button, .link, .header, .adjustable, or .selected, matching their actual behavior. | • set accessibilityTraits on every interactive element to match its behavior• use .button for tappable controls, .link for navigation links, .header for section headings• for adjustable controls (sliders, steppers), set .adjustable and implement accessibilityIncrement() and accessibilityDecrement()• in SwiftUI, use .accessibilityAddTraits() to add the appropriate traits• test with VoiceOver and confirm the announced trait matches the element's behavior |
| Assistive Technology Access | 4.1.2 (A) | Interactive elements that have isAccessibilityElement set to false or are hidden from the accessibility tree while still being visually present and functional. | • set isAccessibilityElement = true on all meaningful interactive elements• for custom container views, either make children individually accessible or provide a container-level label that summarizes the content • implement UIAccessibility protocols on custom views (label, traits, value, hint)• test with VoiceOver to confirm every interactive element is reachable by swiping |
| Button Element Capitalisation Check (iOS) | 3.1.6 (AAA) | Button labels that use all-uppercase text (e.g., "SUBMIT"), all-lowercase text (e.g., "submit"), or inconsistent capitalisation that affects VoiceOver pronunciation. | • use title case or sentence case for button labels ("Submit Order", "Learn more") • if visual uppercase is desired, apply it through UIButton.Configuration text transform or CSS text-transform rather than changing the actual text string• ensure the accessibility label uses natural capitalisation even if the visual display is uppercase • test with VoiceOver to confirm buttons are spoken as words, not spelled out |
| Color Contrast Issues | 1.4.3 (AA) | Measures the contrast ratio between foreground text or icon colors and their backgrounds, flagging pairs that fall below 4.5:1 for normal text or 3:1 for large text. | • verify text contrast against backgrounds using a color contrast analyzer or Xcode Accessibility Inspector • use Dynamic Colors or semantic color assets that adjust automatically for light and dark modes • ensure all text states (enabled, disabled, placeholder) meet minimum contrast ratios • test with Increase Contrast enabled in iOS Accessibility settings |
| Duplicate Accessibility Label (iOS) | 4.1.2 (A) | Multiple interactive elements on the same screen that share the same accessibility label, making them indistinguishable to VoiceOver users. | • include context in the label: "Edit shipping address" vs. "Edit billing address" • for list items, append identifying information: "Delete order #1234" • use accessibilityLabel with specific context rather than generic action words• in SwiftUI, use .accessibilityLabel("specific description") to override default labels• test by having VoiceOver list all actions on the screen and confirming each is distinguishable |
| Dynamic Type Support (iOS) | 1.4.4 (AA) | Text elements use scalable font styles (either system text styles or custom fonts registered with UIFontMetrics) and respond to the user's Dynamic Type preference. | • use UIFont.preferredFont(forTextStyle:) or register custom fonts with UIFontMetrics• set adjustsFontForContentSizeCategory = true on labels and text views• use Auto Layout with constraints that accommodate text growth • test with all Dynamic Type sizes, including the five Accessibility sizes beyond the standard range • in SwiftUI, use .font(.body) or other text styles which scale automatically |
| Emoji or Symbol Used in Accessibility Label | Examines whether a visible, interactive element's accessible label is made up mostly of emoji, arrows, geometric shapes, or other special symbols instead of descriptive text. A violation is reported when half or more of the label's meaningful characters are emoji or symbols. Whitespace is ignored, and ordinary punctuation is not counted as a symbol. Static (non-interactive) text is not checked. | ||
| Fixed Orientation Lock | 1.3.4 (AA) | Reports apps that restrict viewing to a single orientation (portrait or landscape only). | • support both portrait and landscape orientations unless there's a valid reason to lock one • if locking orientation is required, provide alternatives or ensure the design accommodates various accessibility needs |
| Images with Text | 1.1.1 (A) | Images whose pixels contain rendered text that exists nowhere else on the screen as real text — text content embedded inside an image that isn't readable by assistive technology. | • avoid embedding meaningful text inside images — use real text views with an accessibilityLabel set so VoiceOver can read the content• if the image is decorative, mark it as such so screen readers skip it |
| Insufficient Touch Target Spacing | 2.5.8 (AA) | This is a screen-level rule. The scanner collects every interactive element that is visible, on-screen, enabled, and reachable by touch, then measures the edge-to-edge gap between each pair: A pair is reported when the gap is less than 8 points. Both elements in the pair are reported. A pair is skipped when both elements already meet the 44x44pt minimum (an intentional design, not a hazard), and parent-child pairs are also skipped, since a control nested inside another has zero gap by construction. | • increase the spacing to at least 8pt between adjacent small targets, or • enlarge both targets to 44x44pt, at which point the pair is exempt • prefer separating destructive actions from their neighbours by more than the minimum, or placing them behind a confirmation |
| Interactive Role Undefined | 4.1.2 (A) | Finds custom views that behave like controls but expose no role to assistive technology. An element is flagged only when all of the following hold: In practice this narrows to a custom view that has been made tappable without being given a role. | • add the role so it is announced: view.accessibilityTraits = .button (UIKit), or .accessibilityAddTraits(.isButton) (SwiftUI)• prefer a real control (a UIButton or SwiftUI Button) over a tappable container — the control brings the role, the focus behaviour, and the activation semantics for free• ensure the element is also an accessibility element ( isAccessibilityElement = true) and carries a descriptive label• confirm with VoiceOver that the announcement ends with the role |
| Invalid Range Values | 4.1.2 (A) | Range controls (Slider, Stepper, ProgressIndicator) and any custom view with the .adjustable trait for two types of violation: 1. Missing value — the control has no programmatic accessibilityValue, so VoiceOver cannot announce its current state. 2. NaN value — a native UISlider whose minimumValue >= maximumValue produces a NaN accessibility value (UIKit computes (value - min) / (max - min), which yields NaN when the denominator is zero or negative). | • ensure min < max and the current value falls within [min, max] on the control (UISlider/UIStepper)• for custom range controls, expose the range to assistive technology via accessibilityValue and the .adjustable trait |
| Meaningful Sequence | 1.3.2 (A) | Whether the programmatic reading order matches the meaningful visual sequence. Where Traversal Order Does Not Match Visual Layout targets focusable controls, this rule concerns the sequence of the content itself. | • reorder views in the layout to match the visual reading order • set the accessibilityElements array on the container to define the correct VoiceOver reading sequence |
| Minimum Text Size | Estimates the font size of text elements from their accessibility frame height using the San Francisco font's line-height ratio: estimatedFontSize = frameHeight / 1.1777 Where 1.1777 is the constant ratio of ascender (0.9668) minus descender (−0.2109) for the San Francisco typeface. A violation is reported when the estimated font size falls below 11pt, the Apple Human Interface Guidelines minimum. Two guards prevent false positives from unreliable frame heights: elements with frame height below 4pt are skipped (collapsed/clipped SwiftUI composite controls), and elements whose frame touches the top or bottom screen edge (within 2pt) are skipped (partially scrolled-off elements with truncated frames). | • increase any non-scalable text below 11pt to at least the recommended minimum • prefer Dynamic Type styles so text scales with the user's font preference (see Dynamic Type Support) | |
| Mismatched Label Text | 2.5.3 (A) | For interactive elements, the scanner compares the text visibly rendered on the control against its accessibility label. The visible text is read from the screen using on-device text recognition and attributed to the element that contains it. Both strings are normalized before comparison: lowercased and reduced to letters, numbers, and single spaces. A violation is reported when the visible text is not contained within the label. The rule skips elements that are non-interactive, disabled, not visible, carry no recognizable text, or have an empty label. (Beta rule) | • the accessible name must contain the visible label as a substring, ignoring case and punctuation. Prefer an exact match; where extra context is needed, put the visible label first: a button reading "Next" may be labelled "Next" or "Next, proceed to payment", but not "Proceed to payment" • never use developer identifiers as accessible names • iOS: where the visible text is sufficient, simply do not override accessibilityLabel — UIKit derives it from the title |
| Misplaced Field Label | 2.5.3 (A) | This is the companion to Mismatched Label Text, using the same visible-text recognition and the same interactivity and visibility filters. This rule applies only when that one passes: it reports elements whose label contains the visible text but does not begin with it. If the label does not contain the visible text at all, Mismatched Label Text owns that finding and this one stays silent. (Beta rule) | • start the accessible name with the visible label; put any extra context after it ("Edit, order summary", not "Order summary, Edit") • iOS: begin accessibilityLabel with the control's visible text; keep supplementary detail after it or in accessibilityHint |
| Missing Accessibility Labels | 4.1.2 (A) | Meaningful iOS controls and interactive elements expose accessible names through labels and related accessibility properties. | • add accurate labels for meaningful interactive elements • make labels action-oriented when the element performs an action • verify the spoken output in VoiceOver after implementation |
| Missing Button Element Label | 4.1.2 (A) | UIButton and Button elements that have no title text, no accessibility label, and rely only on an image or icon without an accessible name for VoiceOver. | • set accessibilityLabel on every button that does not have visible title text• for icon-only buttons, describe the action: "Close", "Open menu", "Share" • in SwiftUI, use .accessibilityLabel("action description") on Button views• if the button already has title text, VoiceOver uses it automatically, no additional label needed |
| Missing Checkbox Element Label | 4.1.2 (A) | Toggle-like or checkbox-pattern elements that have no accessibility label, leaving VoiceOver to announce only the state (selected/not selected) without context. | • set accessibilityLabel on the checkbox element to describe the option• group the checkbox and its label into a single accessibility element using shouldGroupAccessibilityChildren• in SwiftUI, use .accessibilityLabel("Option description") on toggle or checkbox views• test with VoiceOver to confirm the full announcement includes both the label and the state |
| Missing Editable Element Label | 4.1.2 (A) | UITextField, UITextView, UISearchBar, and similar editable controls that have no accessibility label and no associated placeholder text that VoiceOver can use as a fallback. | • set accessibilityLabel on every editable element to describe the expected input• use UITextField's placeholder text as a fallback, but provide a proper label for clarity• group the label and input into a single accessibility element if appropriate • in SwiftUI, use .accessibilityLabel("Field description") on TextField views• test with VoiceOver to confirm each field announces a meaningful description before the user types |
| Missing Image Element Label | 1.1.1 (A) | Image elements that are visible on screen and carry meaningful content but have no accessibilityLabel, causing VoiceOver to either skip them or announce the image file name. | • set accessibilityLabel on every meaningful UIImageView to describe what the image shows• for decorative images, set isAccessibilityElement = false to hide them from VoiceOver• in SwiftUI, use .accessibilityLabel("description") on Image views• keep labels descriptive but concise (e.g., "Product photo: Blue running shoes" rather than "image_shoe_blue_v2.png") |
| Missing Switch Element Label | 4.1.2 (A) | UISwitch and Toggle (SwiftUI) elements that have no accessibility label, leaving VoiceOver to announce only "Switch, on" or "Switch, off" without context. | • set accessibilityLabel on the UISwitch to describe the setting (e.g., "Dark mode", "Push notifications")• use UITableViewCell's built-in textLabel as the accessibility label for the cell when the switch is the cell's accessory view• in SwiftUI, the Toggle("Label text") initializer automatically provides a label. Ensure it is always populated• test with VoiceOver to confirm the full announcement includes both the setting name and the state |
| Non-accessible Interaction | 4.1.2 (A) | Interactive elements that can't be accessed or activated via assistive technology. The action exists for touch users but is unreachable through VoiceOver, Switch Control, or Voice Control. | • make sure any action (a custom button, link, or gesture) can be reached by assistive technology and has a clear description • use descriptive labels like "Play", "Delete", rather than generic labels (e.g., "Button1") |
| Non-Descriptive Link Text | 2.4.4 (A) | Examines links whose entire accessible name is a generic call-to-action. The blocked phrases are: click here, click this, go, here, information, learn more, more, more info, more information, right here, read more, see more, start, this, tap here, tap this, view more, link, details. Matching is exact, not a substring. The label must be one of these phrases in full. Before comparison the label is cleaned up: non-breaking spaces are normalized, a trailing ellipsis is removed, and surrounding whitespace and punctuation are trimmed. So "Read more..." is flagged, while "Read more about our privacy policy" passes. Links with an empty label are covered by the missing-label rules instead. | • rewrite the visible text to name the destination: "Read more" becomes "Read our privacy policy" • if the visible text cannot change for design reasons, set a descriptive accessibilityLabel on the link instead — the visible text stays "Read more"; VoiceOver announces "Read our privacy policy"• ensure the label reads sensibly in isolation, since that is how the rotor presents it |
| Overlapping Interactive Elements | 2.5.5 (AAA) | This is a screen-level rule. The scanner collects every interactive element that is visible, on-screen, enabled, and reachable by touch, then compares each pair. A pair is treated as overlapping when they share a significant portion of the smaller element's area. Overlapping controls are joined into clusters, so a chain of mutually overlapping controls is reported as one group rather than as many pairs. Three exclusions prevent false positives: parent-child pairs are skipped (a control nested inside another necessarily overlaps it); cross-layer pairs are skipped (elements inside a fixed overlay like a navigation bar, tab bar, or toolbar are never compared against content elements, since the content scrolls beneath the overlay); and system scroll indicators are ignored. | • adjust layout so interactive frames do not intersect • if one control genuinely sits above another (a badge on a button), make the pair a single accessibility element with one clear action, or set isAccessibilityElement = false on the passive one• where a control is intentionally layered above scrolling content, confirm it lives inside a proper overlay container (navigation bar, tab bar, toolbar), which the rule already exempts |
| Redundant Role Keyword in Accessibility Label | 4.1.2 (A) | Examines whether a visible accessibility element's label embeds the control's own role. The role words checked are drawn from the control's actual type and role, so a role word is flagged only when it matches the real control — "Play button" on a piece of static text is not reported by this rule. | Control | Flagged keywords | |---|---| | Button, Toolbar button, Menu button, Pop-up button | button | | Switch, Toggle | switch | | Link | link | | Image, Icon | image | | Text field, Secure text field, Text view | text field, textfield | | Search field | search field, searchfield | | Slider | adjustable | | Stepper | stepper | Matching is whole-word and case-insensitive, with boundary checks, so "Unbutton" and "Onboarding" do not match. Deliberately not flagged: slider, toggle, icon, tab, picker, menu, progress, since VoiceOver does not announce those words as roles on iOS. | |
| Redundant State Keyword in Accessibility Label | 4.1.2 (A) | Examines whether a visible accessibility element's label embeds state that VoiceOver already announces from the control itself. Only state words that VoiceOver actually speaks for that control are flagged: | Condition | Flagged keywords | |---|---| | Control is marked selected | selected | | Control is disabled | dimmed | | Switch, toggle, or checkbox | on, off | | Disclosure triangle | expanded | Matching is whole-word and case-insensitive. Deliberately not flagged, because VoiceOver does not use these words on iOS: disabled (VoiceOver says "dimmed"), enabled, unselected, collapsed, and checked / unchecked (VoiceOver says "on" / "off"). | |
| Text Truncation Issues | 1.4.4 (AA) | Text elements that are clipped or truncated (ending with "...") when the content contains meaningful information that is lost, particularly when the user has increased the text size through Dynamic Type settings. | • use numberOfLines = 0 with Auto Layout constraints to allow text to wrap• set minimum cell heights using Auto Layout rather than fixed values • use adjustsFontForContentSizeCategory = true on text elements• test with the largest Dynamic Type size (Accessibility sizes enabled) to confirm no meaningful content is lost |
| Touch Target Sizing | 2.5.5 (AAA) | IOS controls provide enough touch area and spacing for reliable interaction. | • increase the tappable region of small controls • add spacing between adjacent actions • verify that custom components preserve touch size across device sizes |
| Traversal Order Does Not Match Visual Layout | 2.4.3 (A) | This is a screen-level rule. The scanner walks focusable elements in the order the screen reader reads them and compares each element with the one before it. An element is flagged when it comes later in the reading order but sits earlier on screen than the element before it — its centre sits noticeably above the previous element's centre, or its centre is on the same row but noticeably to the left. In other words, it fires when the reading order jumps backwards relative to the visual layout. Only neighbouring elements are compared, so a single misplaced element does not cascade into a report for everything after it. Excluded from the comparison: elements hidden from assistive technology, invisible or off-screen elements, zero-size elements, headers, system elements (scroll bars, the back button, page indicators), and non-interactive static text and images, since those frequently sit in a reading order that differs from their visual position without any consequence for navigation. | |
| Two-Dimensional Scrolling Required | 1.4.10 (AA) | This is a screen-level rule detecting content that requires scrolling in two directions to be read. It looks for: 1. Scroll views — a scroll view that scrolls horizontally while nested inside another scrollable area, or that is wider than the screen. A text view in this state is reported as a warning rather than a violation. 2. Tables and collection views — a cell whose content scrolls horizontally inside a vertically scrolling table, or a collection view that scrolls horizontally at any depth. 3. Web views — a web view that scrolls horizontally. 4. Element overflow — any element whose right edge extends past the screen, attributed to the scrollable area that contains it; if there is none, the overflowing element itself is flagged. Findings are deduplicated: once an element or one of its ancestors has been flagged, its descendants are not reported again. Legitimate horizontal scrolling is exempted through a set of recognized patterns: paging controls with a page indicator, step indicators, chip and filter bars, date pickers, image carousels, data tables, maps, and any element identified as a map, diagram, carousel, gallery, floor plan, or canvas. | • let text wrap rather than extend — use flexible widths and allow the container to grow vertically • where content must be wide (a data table, a chart), keep it in a container that scrolls horizontally on its own while the surrounding page scrolls only vertically, and confirm the container matches one of the exemption patterns, or is genuinely exempt under 1.4.10 • test at the largest Dynamic Type size and confirm no element's right edge crosses the screen boundary • avoid nesting a horizontal scroll view inside a vertical one unless the inner view is a carousel or paging control |
| Unique Option Names | 4.1.2 (A) | Children of recognized option group containers for duplicate accessible names. The recognized container types are XCUIElementTypeSegmentedControl, XCUIElementTypeTabBar / XCUIElementTypeTabGroup, XCUIElementTypeRadioGroup, XCUIElementTypePicker, and XCUIElementTypeMenu / XCUIElementTypeMenuBar. Within each container, the rule collects visible, interactive children (buttons, tabs, radio buttons, menu items, picker wheels) and checks for children with the same accessibilityLabel. Custom views with the .adjustable trait are also treated as option-like children. Duplicate labels are reported with a groupId that clusters the matching elements together. Unlike Duplicate Accessibility Label, which checks the entire screen, this rule looks specifically inside a single selection group, where identically named options make the choice itself meaningless. | • give every option within a picker, radio group, or segmented control a distinct accessible name so screen reader users can tell them apart • don't rely on position or visual styling to differentiate identically labeled options |
