accessibility-guidelines

This document is in beta. Help us by reporting issues via Github or email

Back to the overview page

Focus order

On this page:


Summary

Make sure that interactive controls receive focus in an order that makes sense, when users navigate through them with the keyboard.

Actionable items should receive the keyboard focus in an order that makes sense for users, and makes the interface easy to operate.


Requirements

Why?

This ensures that content can be navigated in a logical way by screen reader users, keyboard users and Switch device users.

The order in which elements appear in code matters

Official wording in the Web Content Accessibility Guidelines

2.4.3 Focus Order: If a Web page can be navigated sequentially and the navigation sequences affect meaning or operation, focusable components receive focus in an order that preserves meaning and operability. (Level A)

This applies to native apps by replacing “Web page” with “screen”.

See the W3C’s detailed explanation of this guideline with techniques and examples.


Guidance for Design

Example: designing a modal dialog for the Web

Common mistakes

More guidance for design


Guidance for iOS

This section needs a review and more content. Contribute via Github or email.


Guidance for Android

This section needs a review and more content. Contribute via Github or email.


Guidance for Flutter

Flutter deals with assistive technologies like screen readers using the Semantics() widget. For a quick introduction of the Semantics() widget you can read this article. ##

There are ways of affecting the order for custom navigation of elements with a screen reader. By passing a SemanticSortKey to the sortKey property of Semantics() the element with the lower key value will be read first.

Column(
    children: <Widget>[
        Semantics(
            sortKey: OrdinalSortKey(1), // will be read/focused second by the screen reader
            child: SecondWidget()
        ),    
        Semantics(
            sortKey: OrdinalSortKey(0), // will be read/focused first by the screen reader
            child: FirstWidget()
        ),
    ]
),

Focus can be controlled using a FocusNode():

FocusNode myFocusNode;

@override
void initState() {
  super.initState();
  myFocusNode = FocusNode();
}

@override
void dispose() { 
  myFocusNode.dispose(); // Clean up your focus node by disposing it when the widget is disposed
  super.dispose();
}
// A widget with the focus node
TextField(
  focusNode: myFocusNode,
);

// Focusing the focus node using a fuction call
() => FocusScope.of(context).requestFocus(myFocusNode),

Guidance for Web

Be careful how you order elements in the DOM is it impacts user experience

Example

<div>
    <h3>Shipping Address</h3>
    <label for="n1">Name</label><input type="text">
    <label for="a1">Address</label><input type="text">
...
</div>
<div>
    <h3>Billing Address</h3>
    <label for="n2">Name</label><input type="text">
    <label for="a2">Address</label><input type="text">
...
</div>     

Failure example

<table>
 <tr>
  <td>Shipping Address</td>
  <td>Billing Address</td>
 </tr>
 <tr>
  <td>
    <label for="n2">Name</label>
    <input type="text">
  </td>
  <td>
    <label for="n1">Name</label>
    <input type="text">
  </td>
 </tr>
 <tr>
  <td>
    <label for="a1">Address</label>
    <input type="text">
  </td>
  <td>
    <label for="a2">Address</label>
    <input type="text">
  </td>
 </tr>
</table>  

Manage keyboard focus when needed to make the interface easy to operate

Do not use positive values with tabindex

Example

<h1 tabindex="-1">I'm the heading for a modal dialog</h1>
...
<div tab-index="0" class="fake_button" aria-role="button">Button Label</div>

Failure example

<div tab-index="3" class="fake_button" aria-role="button">Button Label</div>

Be careful when using CSS Float, Flexbox, Grid and position

Make new content appear in the DOM right after the element that triggers it

How to test whether elements appear in a logical reading order in the DOM

Common mistakes

More guidance for Web


More info

Sources

Contribute

This document is in beta. Help us by reporting issues via Github or email