1.3.1 Info and Relationships: Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. (Level A)
Requirements
Requirements for both native and web
- Headings are presented using the correct elements or properties in code.
- Form fields have properly associated labels.
- Form fields with format requirements or other additional information have properly associated hints.
Web only requirements
- Tabular data is presented using the correct markup.
- Lists of items are presented using the correct markup.
- Sets of radio buttons or checkboxes have a element to group them together, and a element to provide a label for the group.
Why is it important?
This ensures that the structure of the content that’s conveyed visually by the designs is also available to screen reader, screen magnifier and speech recognition tool users.
Summary
- Make sure that information and relationships that are conveyed visually (like distinct sections within a page, or a label next to a checkbox) are also identified in code.
- When content structures like tables, lists, sections and headings are communicated visually, they should also be coded in ways that assistive technologies can understand.
Common mistakes
- Tabular data is displayed using CSS to present the appearance of a table, but the proper HTML markup has not been used.
- A list of items is presented using line breaks to separate each item, and the proper HTML markup has not been used.
- Headings are text styled with CSS, and the proper HTML markup has not been used.
- Headings use the proper HTML markup, but do not accurately represent the hierarchy of content on the page (starting with, then for each section, for each sub-section and so on)
- Form fields do not have labels.
- Form fields have labels but they are not properly associated with their corresponding fields.
- Sets of radio buttons or checkboxes are not grouped inside a element, and do not have a element to provide a label for the group.
- Form fields have format requirements or additional information, but it is not properly associated with the corresponding field.
- Using the placeholder attribute to provide an ‘Accessible Name’ for a text input field. (The placeholder attribute is not well supported by some browsers and assistive technologies combinations, and shouldn’t be used for labelling text fields).
Design Guide
Information and Relationship Example for IOS
Setting a view as a heading (e.g. table section headings or screen titles)
headingLabel.isAccessibilityElement = true
headingLabel.accessibilityTraits = [.heading]
Setting a custom view as a search field
customSearchField.isAccessibilityElement = true
customSearchField.accessibilityTraits = [.searchField]
Setting a custom view as a tab bar
customTabBar.isAccessibilityElement = true
customTabBar.accessibilityTraits = [.tabBar]
You can combine traits if your element needs it
view.accessibilityTraits = [.heading, .updatesFrequently]
Providing a label for input fields
textField.accessibilityLabel = "First Name"
Information and Relationship Example for Android
Setting a view as a heading (requires API level 28)
view.isAccessibilityHeading = true
<TextView
...
android:accessibilityHeading="true"
... />
Information and Relationship Example for Flutter
Flutter deals with assistive technologies like screen readers using the Semantics() widget.
Setting a widget as a heading (e.g. table section headings or screen titles)
For widgets that don’t have this set by default and don’t expose this semantic property you can do this:
Semantics(
header: true,
child: HeadingWidget()
)
Setting a custom widget as a search field
Semantics(
label: 'Search',
child: CustomSearchField()
)
Note: if you set textField: true in the Semantics() widget which has a TextField() child then the screen reader will ignore the Semantics() widget and will only use the semantics of TextField().
Setting a custom widget as a tab bar
The Tab() widget is currently not very accessibility friendly when it comes to text size as its height is set. The use of a custom tab widget is recommended.
The accessibility labels can be added as you see fit with the use of the Semantics() widget one example shown below:
class CustomTabWidget extends StatelessWidget {
const CustomTabWidget({
Key key,
this.semanticLabel,
}) : super(key: key);
final String semanticLabel;
@override
Widget build(BuildContext context) {
return Semantics( // check the Semantics widget documentation to see what other properties youc could use
label: semanticLabel,
child: // construct the rest of your tab here
);
}
}
Note: keep in mind that the widgets you use might have their own semantic properties.
Setting a custom widget as a bottom nav bar
The BottomNavigationBar(items:[]) only takes a list of BottomNavigationBarItem so the item widgets cannot be wrapped in a Semantics() widget but their children can.
// Bottom navigation tab with a custom widget instead of icon
BottomNavigationBarItem(
// Your CustomWidget() could also just have a Semantics() widget inside
// of its own implementation and expose any property of Semantics() you need
// this would make the CustomWidget() more reusable and make for cleaner code
icon: Semantics(
label: 'CustomWidget Semantic Lable',
child: CustomWidget(),
),
),
// Bottom navigation tab with just a icon
BottomNavigationBarItem(
icon: Icon(
Icons.myIcon,
semanticLabel: 'Icon Semantic label',
),
),
// Bottom navigation tab with a icon and text lable
BottomNavigationBarItem(
icon: Icon(Icons.icon), // icon is a required property
title: Text(
'Text',
semanticsLabel: 'Text Semantic Label',
),
),
// If you wish to exlude any default semantics such as the text in a Text() widget you could do this
BottomNavigationBarItem(
icon: Icon(Icons.image),
title: ExcludeSemantics(child: Text('Image')),
),
// You can also combine or mix and match all of these together should you need to
BottomNavigationBarItem(
icon: Icon(
Icons.format_align_left,
semanticLabel: 'Icon Semantic label',
),
title: ExcludeSemantics(
child: Text(
'Form',
semanticsLabel: 'Text Semantic Label',
),
),
),
Dealing with invisible widgets and their semantics
If you have widgets that you want to exclude from semantics because they serve no purpose for a visually impaired user or if they are hidden until a change of state you can use the ExcludeSemantics() or Semantics(excludeSemantics: bool) as shown below:
ExcludeSemantics(
child: new Image.asset(ImageAssets.decorativeCandle),
),
// Here we can toggle whether semantics are being excluded with the `excluding: bool` property which is true by default
ExcludeSemantics(
excluding: bool,
child: Visibility(
visible: bool,
child: SometimesVisibleWidget(),
),
)
Semantics(
excludeSemantics: bool,
child: SometimesVisibleWidget(),
),
You can combine semantics if your element/widget needs it
MergeSemantics(
child: Column(
children: <Widget>[
Text('Line one'),
Text('Line two'),
],
),
),
Note: this will merge the semantics of the child and its children into one semantic node
Providing a label for input fields
There are two ways to add a label to the text field both are shown below one of them is the same as setting a custom view as a search field:
Semantics(
label: 'Search', // this will only add a semantic label to textfield and not a actual visible label
child: TextField()
)
// OR
TextField(
decoration: InputDecoration(
hintText: String, // hint text has its own built in semantics
labelText: String, // label text has its own builtsemantics
),
),
Information and Relationship Example for Web
Forming a correct headings structure
- Code headings with heading mark-ip ( <h1> – <h6> ).
- Assign headings the correct heading level:
- the main page heading is coded with <h1>
- its subheadings are coded with <h2>
- subheadings of any <h2> heading are coded with <h3>
- and so on…
- Text that does not represent a heading should not be coded with heading mark-ip.
- Use CSS classes rather than <h1> – <h6> elements to style text that don’t represent headings for sections of the page
<h1>Main heading for the page</h1>
<h2>Heading for the first top level section of the page</h2>
<h3>Heading for a sub-section, nested under the first top level section</h3>
<h4>Heading for a sub-sub-section, nested under that sub-section</h4>
<h2>Heading for the second top level section of the page</h2>
<h3>Heading for a sub-section, nested under the second top level section</h3>
Example: A heading structure for a weather website
<h1>Local Weather</h1>
<h2>Today's Forecast</h2>
<h2>Tomorrow Forecast</h2>
<h3>Tomorrow's Allergy Forecast</h3>
Failure example
<div class="heading">Local Weather</div>
<div class="subheading">Today's Forecast</div>
<div class="subheading">Tomorrow Forecast</div>
<div class="subheading2">Tomorrow's Allergy Forecast</div>
Forming a data table
- A cell that is a row header should be coded with ;
<th scope="row">
;
A cell that is a column header should be coded with ;<th scope="col">
;
Use<th scope="rowgroup">
and<th scope="colgroup">
if the same header applies to multiple rows or columns, but you should avoid complex tables like that as they are hard to understand for screen reader users; - Cells containing data are coded with
<td>
. - Avoid making complex data tables (for example tables containing several rows and/or columns of headers, irregular and multi-level headers).
- If you can’t avoid making a complex data table, each data cell should be associated with all its headers via the scope, id and headers attributes.
- A title should be provided for all data tables via the element (or aria-labelledby with the id of a h1-h6 element directly introducing the table).
<table>
<caption>
Short descriptive name for the table
</caption>
<thead>
<tr>
<th scope="col">Column Heading 1</th>
<th scope="col">Column Heading 2</th>
<th scope="col">Column Heading 3</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Row Heading 1</th>
<td>Cell value for row 2, column 2</td>
<td>Cell value for row 2, column 3</td>
</tr>
<tr>
<th scope="row">Row Heading 2</th>
<td>Cell value for row 3, column 2</td>
<td>Cell value for row 3, column 3</td>
</tr>
</tbody>
</table>
Do not use HTML tables for layout out content
- The use of layout tables (meaning HTML tables solely used to position items on the screen) should be avoided. Use CSS to position the content instead.
- When layout tables cannot be avoided, their source code should contain the ARIA role=”presentation” attribute, and should not include the <th> and <caption> elements, and the headers, axis, scope and summary attributes.
Forming lists
- Lists should be coded with the most appropriate HTML list element:
- for unordered lists;
- for ordered lists;
- for description lists.
- List mark-up should be used to accurately describe the structure of lists.
- For example, do not split items of a same list into two separate
- Content that does not represent a list should not be coded with list mark-up.
Example of an ordered list:
<ol>
<li>List value 1</li>
<li>List value 2</li>
</ol>
Example of a description list:
<dl>
<dt>Title 1</dt>
<dd>List value 1</dd>
<dd>List value 2</dd>
<dt>Title 2</dt>
<dd>List value 1</dd>
</dl>
Associating form controls with labels, and associating related form controls
- Form fields that have a visible label should be associated with their label in the HTML code (via the
id
andfor
attributes).
<label for="first-name">First Name</label> <input name="first-name" />
- Radio buttons and checkboxes are associated with their label via the
id
andfor
attributes, are grouped together with<fieldset>
and the question preceding each group is coded with<legend>
.
Use HTML5 sectioning elements to describe document structure and outline different parts of the page
- Identify the purpose of each section of content on a page using HTML5 sectioning elements. These create ‘Landmark regions’ that help screen reader users navigate within a page.
Element | What it means | Implicit landmark role |
<header> | A section of content that is repeated at the top of all/several pages | role=”banner” if it is a direct child of <body> |
<nav> | An area containing navigation links | role=”navigation” |
<search> | A component that allows users to search content on the site | role=”search” |
<main> | The main content of the page (meaning not header, navigation or footer elements or complementary content) | role=”main” |
<form> | A form | role=”form” if it has an Accessible Name |
<section> | A section of content that you want added in the list of Landmark regions | role=”region” if it has an Accessible Name |
<aside> | Content that complements the content in the main area, but would also make sense on its own | role=”complementary” |
<footer> | Information about the page (such as copyright) | role=”contentinfo” if it is a direct child of <body> |
- Make sure that every text node on the page is a descendant of a HTML sectioning element.
- Otherwise screen reader users who navigate pages by landmarks would never find it.
- If a same HMTL sectioning element and/or equivalent ARIA landmark role is used multiple times on a page, make sure that each instance has a unique Accessible Name.
Example:
<header>
<div id="logo">...</div>
<nav aria-label="BBC">
<ul>
<li><a href="...">News</a></li>
<li><a href="...">Weather</a></li>
<li><a href="...">Sport</a></li>
<li><a href="...">Travel</a></li>
...
</ul>
</nav>
<nav aria-label="News">
<ul>
<li><a href="...">Home</a></li>
<li><a href="...">UK</a></li>
<li><a href="...">World</a></li>
<li><a href="...">Business</a></li>
...
</ul>
</nav>
</header>
<main>
<h1>Pisa rankings: Why Estonian pupils shine in global tests</h1>
...
</main>
<aside>...</aside>
<footer>
<p>BBC 2016</p>
<ul>
...
</ul>
</footer>
Failure example:
<!-- Do not do this -->
<div id="header">
<div id="logo">...</div>
<div id="nav" aria-label="BBC">
<ul>
<li><a href="...">News</a></li>
<li><a href="...">Weather</a></li>
<li><a href="...">Sport</a></li>
<li><a href="...">Travel</a></li>
...
</ul>
</div>
<div id="nav2" aria-label="News">
<ul>
<li><a href="...">Home</a></li>
<li><a href="...">UK</a></li>
<li><a href="...">World</a></li>
<li><a href="...">Business</a></li>
...
</ul>
</div>
</div>
<div id="content">
<h1>Pisa rankings: Why Estonian pupils shine in global tests</h1>
...
</div>
<div id="related_content">...</div>
<div id="footer">
<p>BBC 2016</p>
<ul>
...
</ul>
</div>
References
- UIAccessibilityTraits developer reference
- What iOS Traits Actually Do on deque.com
setAccessibilityHeading
in Android’s developer reference- Accessibility Principles on developer.android.com
- A quick introduction of the
Semantics()
widget by Didier Boelens - Flutter accessibility developer reference
- Flutter accessibility widgets developer reference
- Building in Accessibility with Flutter (Flutter Interact ‘19) from Flutter Interact 2019
- A deep dive into Flutter’s accessibility widgets
- Tables tutorial by the W3C Web Accessibility Initiative
- Creating Accessible Tables article by WebAIM
- Intentionally Hiding Semantics with the presentation Role in the ARIA Authoring Practices Guide
- ‘Lists’ section of the Content Structure tutorial by the W3C Web Accessibility Initiative
- Creating Accessible Forms by WebAIM
- Labelling Controls tutorial by the W3C Web Accessibility Initiative
- Grouping Controls tutorial by the W3C Web Accessibility Initiative
- HTML Sectioning Elements article by the W3C Web Accessibility Initiative
- Page Regions tutorial by the W3C Web Accessibility Initiative
- Labelling regions tutorial by the W3C Web Accessibility Initiative
- Accessibility Guidelines- Github.io