We'd like to set Google Analytics cookies to learn how people use the Knowledge Base, so we can improve it. We set them only if you accept.

Skip to main content
Digital Accessibility Knowledge Base

focus-trap

Mobile validation

Keyboard users must be able to move into, through, and out of any part of the UI that contains interactive elements.

A focus trap occurs when keyboard focus gets stuck cycling within a group of elements, and users cannot move on to other parts of the UI. Everything outside the loop becomes unreachable with the keyboard. This often happens when a custom focus order is set with nextFocusForward or the other nextFocus attributes, and the chain leads back to an earlier element instead of continuing through the screen. In most cases, don't override the default focus order. If you do, make sure the chain reaches every interactive element and lets focus continue past the last one.

Note: This validation is about keyboard focus. Screen readers and switch access move through the screen in the accessibility order, which the nextFocus attributes do not change.

Focus traps on iOS and in Flutter

The examples below use Android's nextFocus attributes, Jetpack Compose's focusProperties, and React Native's Android-only nextFocusForward prop. Other platforms have no single attribute that causes this issue, but focus traps can still happen:

  • UIKit and SwiftUI: iOS has no per-element "next focus" attribute. Keyboard focus is managed with focus groups and focus guides, and in SwiftUI also with focus sections and @FocusState. A trap usually comes from custom focus logic, such as code that moves focus back whenever it leaves an area.
  • Flutter: Focus order is managed with FocusTraversalGroup, traversal policies, and FocusScope. A misconfigured scope or custom traversal logic can keep focus inside part of the screen.

On these platforms, review any custom focus logic and test it with a keyboard, as described in Try It Yourself.

Pass Patterns

Android

XML Views

The buttons have no nextFocus attributes, so focus moves through all of them, including Continue, and then out of the group.

XML
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button 1" />


    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button 2" />


    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button 3" />


    <Button
        android:id="@+id/continueButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Continue" />
</LinearLayout>

Fail Patterns

Android

XML Views

The nextFocusForward attributes send focus from Button 1 to Button 3 and back to Button 1, so Button 2 and Continue can never receive focus.

XML
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:nextFocusForward="@id/button3"
        android:text="Button 1" />


    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button 2" />


    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:nextFocusForward="@id/button1"
        android:text="Button 3" />


    <Button
        android:id="@+id/continueButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Continue" />
</LinearLayout>

Try It Yourself

Using a keyboard

  1. Connect an external keyboard to the device. On iOS, also turn on Settings > Accessibility > Keyboards > Full Keyboard Access.
  2. Press Tab repeatedly to move focus through the screen. You may also need to use the arrow keys within a container.
  3. Check whether focus reaches every interactive element and can move past each group of elements.

Expected results: Focus moves through all the interactive elements on the screen and does not get stuck in a loop.

Actual results: Focus cycles within a group of elements, and some elements on the screen can never receive focus.

FAQ

How does this issue relate to 2.1.2 No Keyboard Trap?

SC 2.1.2 requires that if keyboard focus can move into a component, it can also move away from it using only the keyboard. When a custom focus order creates a loop, focus cannot leave the looped elements with standard keys, so the criterion is not met.

Why is this issue marked as Critical?

A focus trap makes the rest of the screen unreachable for keyboard users. They cannot get to other controls or content, which prevents them from completing tasks, so it is rated critical.

Standards and Regulations

StandardSuccess CriterionReference
WCAG 2.22.1.2 No Keyboard Trap (A)If keyboard focus can be moved to a component using a keyboard interface, focus can be moved away from that component using only a keyboard interface, and if it requires more than unmodified arrow or tab keys, the user is advised of the method.
EN 301 54911.2.1.2 No Keyboard TrapSame as the WCAG text.

Personas impacted by this issue

Learn More