Focus Order is the sequence in which interface elements receive focus when navigating with a keyboard, Switch Control, VoiceOver or TalkBack. In mobile applications, the focus order determines how the user moves between controls using gestures or buttons. According to W3C WCAG 2.2, Success Criterion 2.4.3, 2023, focus must follow a logical order that preserves the meaning of the content. Violation of this principle is one of the common reasons for failing an accessibility audit.
Key Takeaways
Focus Order is the sequence in which the user moves between interactive elements using alternative input methods: keyboard (Tab), Switch Control (step by step), VoiceOver (swipe right/left) or TalkBack. Unlike a mouse or touch screen, where the user selects an element directly, focus navigation is linear — each step moves focus to the next element.
According to Apple HIG, 2024, VoiceOver uses the order of elements in the accessibility tree, which is built based on visual placement: top-left corner → bottom-right. If the screen has complex layout (columns, Grid, ZStack), the tree may not match the visual order.
WCAG 2.4.3 principle: “If a web page can be sequentially navigated through sections and the focus order affects meaning, then the focus must follow an order that preserves meaning and operability.” Exception: dynamic content where focus may jump to draw attention (alerts, modal windows).
A Switch Control user (people with motor impairments) moves through elements automatically — cycle after cycle. If the order is broken, the user spends 3 times longer completing the form. According to Deque University, 2024, correct Focus Order reduces form completion time by 60% for assistive technology users.
Special attention — modal windows. After opening a modal, focus must immediately move to the first interactive element inside the modal (usually a “Close” or “Confirm” button). After closing — return to the element that triggered the modal. This is a WCAG 2.4.3 requirement and also a common mistake.
In iOS, VoiceOver automatically builds the order based on geometry: elements are sorted by Y, then by X. For screens with complex structure, this order may be incorrect — the developer must intervene.
Main tools:
Example of setting custom order for a product card:
class ProductCardView: UIView {
let titleLabel = UILabel()
let priceLabel = UILabel()
let buyButton = UIButton()
override var accessibilityElements: [Any]? {
get {
return [titleLabel!, priceLabel!, buyButton!]
}
set {}
}
}
For programmatic focus movement after an action:
UIAccessibility.post(
notification: .layoutChanged,
argument: newlyAddedItem
)
The shouldGroupAccessibilityElement property is useful for cards in collections. If set to true on the parent card, VoiceOver perceives the entire card as one element. The user can double-tap to activate the whole card, or configure the rotor for internal navigation. Recommended for UICollectionViewCell and UITableViewCell.
In Android, TalkBack also uses geometric order, but priority is given to explicit nextFocus* attributes. These attributes are set in XML or programmatically:
| Attribute | Purpose | Example |
|---|---|---|
| nextFocusDown | Element when navigating down | @+id/field_email |
| nextFocusUp | Element when navigating up | @+id/field_name |
| nextFocusLeft | Element to the left | @+id/btn_back |
| nextFocusRight | Element to the right | @+id/btn_next |
Example for a registration form:
<EditText
android:id="@+id/field_email"
android:nextFocusDown="@+id/field_password" />
<EditText
android:id="@+id/field_password"
android:nextFocusDown="@+id/btn_submit" />
For RecyclerView, the focus order is dynamic — determined by the adapter. If cells have a complex structure, set descendantFocusability = “beforeDescendants” and define the order in the list item node. For Jetpack Compose, focus order is set via Modifier.focusOrder() and FocusOrder. Priority: previous (child), next (following), custom key.
If an element is too small for focus (smaller than 44pt), increase the hit area via TouchDelegate in iOS or minWidth/minHeight in Android. According to Google Material Design, 2024, the minimum touch area is 48×48dp. VoiceOver and TalkBack focus on the element’s bounding box. Elements smaller than 30pt may be inaccessible for gesture focus — the user physically cannot tap them.
Jumping focus — when after an action (e.g., deleting an element) focus moves to the beginning of the list or to the system “Back” button. The VoiceOver user loses context. Solution: programmatically move focus to the element nearest to the deleted one.
Invisible focus — an element receives focus but there is no visual indicator (keyboard users cannot see where they are). In iOS, check UIAccessibility.isVoiceOverRunning for custom indicators. According to Deque University, 2024, invisible focus is the second most common cause of failing an accessibility audit.
Modals — focus remains on the background content after opening a modal. In iOS, the modal view automatically captures focus if modalPresentationStyle = .pageSheet is set. In Android, use setFocusable(true) on the dialog container.
The reverse problem: focus gets stuck inside a modal and cannot exit (except by closing). This is acceptable only for modal windows — the user must intentionally close the window. For regular screens, focus trap is a critical error. Solution: ensure the last element of the modal (the “Close” button) passes focus back.
For custom screens (maps, canvases, games) automatic geometric order is not applicable. The developer must build the accessibility tree manually. In iOS, the UIAccessibilityContainer method is overridden for this purpose.
Example for a custom canvas:
class CanvasView: UIView {
var shapes: [ShapeView] = []
override var accessibilityElements: [Any]? {
get {
// Sort shapes by Z-index, not by geometry
return shapes.sorted { $0.zIndex < $1.zIndex }
}
set {}
}
}
In Android, for a custom View, override onInitializeAccessibilityNodeInfo:
override fun onInitializeAccessibilityNodeInfo(
info: AccessibilityNodeInfo
) {
super.onInitializeAccessibilityNodeInfo(info)
info.addChild(firstElement)
info.addChild(secondElement)
info.isFocusable = true
}
For dynamic lists (chat, news feed), after adding an element, move focus to the first new element. In iOS: UIAccessibility.post(notification: .layoutChanged, argument: newMessage). In Android: sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED).
iOS automatically determines the focus area based on the element’s frame. If an element has a transformation (transform, rotation), VoiceOver may focus on the wrong area. Explicitly set accessibilityFrame in screen coordinates: element.accessibilityFrame = UIAccessibility.convertToScreenCoordinates(element.bounds, in: element). This ensures VoiceOver highlights the correct area.
For animated screens (UIKit Dynamics, Lottie, SpriteKit), programmatic focus is especially important. VoiceOver cannot build an accessibility tree for dynamically moving elements. Set isAccessibilityElement = false on animation containers and true only on interactive elements inside.
Manual testing: enable VoiceOver (iOS) or TalkBack (Android), swipe right through the entire sequence. Focus must follow the visual order — left to right, top to bottom. Each interactive element must receive focus exactly once.
Automated testing is challenging but possible:
func testKeyboardFocusOrder() {
let app = XCUIApplication()
app.launch()
app.textFields["Email"].tap()
// Tab — hardware keyboard only
}
For Android, use the Accessibility Testing Framework:
@Test
fun testFocusOrder() {
onView(withId(R.id.fieldEmail))
.check(matches(isFocusable()))
onView(withId(R.id.fieldEmail))
.perform(focus())
onView(withId(R.id.fieldPassword))
.check(matches(isFocused()))
}
The most reliable method is a UI scenario test: fill out the form step by step (Email → Password → Submit), checking that each step completes successfully. If the focus order is broken, the scenario will fail when trying to interact with an out-of-focus element.
The Accessibility Inspector tool in Xcode shows the full accessibility tree. You can walk through elements in VoiceOver order and see the exact focus path. Use the “Audit” tab for automatic detection of Focus Order violations.
Frequently Asked Questions
WCAG 2.4.3 (Focus Order) is a Level A success criterion. It requires that the focus order preserves content meaning during sequential navigation. Violation is considered critical and blocks certification.
Hidden elements must have isAccessibilityElement = false in iOS or visibility = gone/invisible in Android. When they appear, programmatically move focus via UIAccessibility.post(notification: .layoutChanged).
iOS manages via accessibilityElements and shouldGroupAccessibilityElement, Android via nextFocus* attributes and AccessibilityNodeInfo. The principle is the same: geometric order by default with the ability to override.
Set descendantFocusability = “beforeDescendants” on the root element and configure the order in the adapter via onInitializeAccessibilityNodeInfo for each cell.
Connect a hardware keyboard via Bluetooth or USB. On iOS press Tab to move focus. On Android enable TalkBack and use the Tab and arrow keys.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also