-
Notifications
You must be signed in to change notification settings - Fork 249
Accessibility v3 → v4
Adapt Framework v2 and v3 have an 'accessibility mode' which, when enabled through a button at the start of the course (accessible only via a tab keypress in v2), causes virtually all elements on the page to be 'tabbable'.
While the tabbing approach worked well and was popular with casual users of screen readers, we received feedback that it wasn't the best way to implement accessibility. Blind users universally reported that the Adapt accessibility implementation confounded their expectations of how they should navigate through the content with a screen reader (as standard keyboard shortcuts were not supported in the v2/3 implementation), and these users rejected the approach of having a specific accessibility 'mode'. Accessibility specialists from organisations who use Adapt agreed, and additionally pointed out that the tabbed approach did not align with the WCAG guidelines.
In Adapt v4, the framework's accessibility implementation has been re-engineered to follow WCAG guidelines. This change effectively reverts tabbing to default browser behaviour (interactive elements only) and removes the need for an 'accessibility mode'.
W3C Web Accessibility Initiative (WAI) being a sub-group of World Wide Web Consortium (W3C) produce the guidelines and standards for web accessibility. Out of these guidelines, the Web Content Accessibility Guidelines (WCAG) are the most relevant to the Adapt Framework and content produced using it.
Accessibility is often seen as an additional and restrictive set of design patterns catering for only people with recognised disabilities. The reality is much more subtle and much broader in scope. Some essential reading can be found at W3C WAI - Accessibility Fundamentals - Introduction to Web Accessibility and Mozilla - MDN - Handling common accessibility problems.
As developers of the framework, we have no control over the plugins, assets, content or styling included in a final course. The scope of accessibility within Adapt is naturally, therefore, limited to ensuring that the Framework gives the course author the ability to create a course that is accessible. The Framework cannot do this for the author - nor can it ensure that the author has done so - as what constitutes 'accessible' is subjective and will naturally vary from course to course.
The onus is therefore on the course author to:
- understand what exactly 'accessible' means for learners with accessibility requirements
- configure the course correctly to enable that level of accessibility
- ensure that only plugins, assets & content that meet that level of accessibility are used in the course
- verify through testing that the course has been successfully configured and meets the learners' requirements as best as possible
The bulk of the accessibility work in the Adapt Framework - if we necessarily exclude content - revolves around screen reader access as ensuring that the framework efficiently describes visual content to users who are not able to see it is the biggest challenge to creating accessible online content. This document will mostly cover how we facilitate screen reader access with references for further reading.
Note: All of the specifications and recommendations utilised in the Adapt Framework are tested as working. Anything which cannot provide a consistent experience (whilst excusing minor variations) across the most commonly used screen readers and browsers should not be utilised in the framework.
A standard browser 'tabbing cursor' follows the tabbability of elements through the DOM order. Tabbability is determined and reordered by the (implied or explicitly declared) value of each element's tabindex attribute.
Elements gain and lose 'focus' as the learner navigates through the page, with the currently focused tabbable element being represented by the document.activeElement
value.
Focus, which determines the position of the tabbing cursor, can be moved through the document with the tab key (forward), the shift+tab keys (backward) and by clicking or tapping on various tabbable elements.
A screen reader adds a distinct second cursor to the browser: the 'screen reader cursor'. The screen reader cursor follows the readability of elements through the DOM order, but the flow of the screen reader cursor is not directly influenced by tabbability.
Unlike the tabbing cursor, the existence, position and state of a screen reader cursor is not represented in the browser in any way. Although the screen reader cursor does move with the standard browser tab cursor, it can also be directed independently of it. When the tab cursor is moved, the screen reader cursor follows. But when the screen reader cursor moves, the tab cursor will not follow.
The screen reader cursor will read all readable text blocks and any additional information supplied to the cursor about the ownership, form, intent and behaviour of the readable element.
It is possible to declare elements and their descendants inaccessible to the screen reader. By setting an [aria-hidden="true"]
attribute value on an element, the parent and its entire descendant tree becomes inaccessible. Elements which are { visibility: hidden; }
or { display: none; }
will also go unrepresented by a screen reader. Off screen, transparent or tiny elements remain readable and can be read (with variation) by different screen readers.
By default, a screen reader changes the behaviour of the keyboard in such a way that it becomes the primary method through which the user interacts with the computer. There are distinct and sometimes automatic modes assigned to key presses which allow for various tasks, such as navigation, text input and item selection. Keyboard shortcuts are an essential navigational tool for users who need to build a picture of graphically orientated content as a supplement to or in lieu of direct sight perception.
Reference: Accessibility Developer Guide - Screen readers' browse and focus modes
It is sometimes necessary to move browser tab focus (and hence the screen reader cursor), either to return the user to a previous location or to assist in the navigation of more complicated content presentations, such as accessing the non-active items in a narrative component.
The Adapt Framework can be reasonably modified and contains some complicated or changing user interface sequences. Programmatically assigning focus to an element that may become disabled or removed (due to user interaction or developer modification) will cause the screen reader cursor to drop to the document body instead. When the focus and screen reader cursor drop to the document body unexpectedly, the user loses their place in the document. The submit and feedback buttons are good examples of an Adapt Framework user interface which expresses many behavioural variations.
The Adapt Framework has two functions to aid in the allocation of focus, both of which seek forward in the document and assess the readability of elements as they go.
$("selector").focusOrNext();
Will try to focus on the selected element; on failing readability tests it will focus on the first readable child, or then the next readable element in the document.
$("selector").focusNext();
Will focus on the first readable child or the next readable element in the document.
These behaviours allow us to more loosely move the screen reader cursor and focus, relying on Adapt Framework to handle the correct allocation for us.
Note: Sometimes the next readable element cannot have focus assigned because it is not tabbable. If so, the Adapt Framework will temporarily add [tabindex=-1][data-ally-force-focus]
to the element, allowing it to receive focus as needed and then remove these attributes on blur.
The Adapt Framework occasionally makes use of functionality we call a 'self-disabling button'. The Submit button used in question components is a good example of this.
Disabled elements cannot receive the screen reader cursor or browser focus. When elements transition from having the focus of both the screen reader cursor and the browser to a disabled state, the screen reader cursor is moved by the screen reader to the document body. As described in the previous section, this isn't something you want to happen to a screen reader user.
Adapt Framework prevents the screen reader cursor from returning to the document body for self-disabling elements as it listens to blur events from elements with a [disabled]
attribute and moves the cursor on to the next readable element in the document. This retains the forward momentum of the content whilst allowing for self-disabling buttons.
When 'clicking' any element with the space or enter keys whilst the screen reader is running, the 'clicked' element will not receive focus but will fire a click
event. Since clickable elements often act as the point of return from popups or other activities, this lack of focus assignment on click can be somewhat problematic when we need to be aware of the last active element.
Adapt helps mitigate the lack of focus on screen reader keyboard 'clicks' by using a capture event handler to force focus when tabbable elements are clicked. This behaviour allows document.activeElement
to work as one would expect it to with a mouse.
References: javascript.info - Bubbling and capturing events
Navigation, menu, page, notify and drawer content is now assigned an appropriate aria-role. This affords screen reader users easily distinguishable areas of the Adapt Framework user interface. Screen reader users can now cycle through the regions, giving them a sense of the user interface and allowing them to locate content with greater ease.
Note: A component can optionally have a region by setting "_isA11yRegionEnabled": true
on its model. This attribute is not yet represented in the schemas for the Authoring Tool as it is currently difficult to quantify its usefulness.
Menu, page, article, block and component headings are set up to provide a more useful navigation layer for screen reader users. Headings now provide an extension to the header text by including a representation of the model's completion attribute along with an appropriate heading level.
Any view which triggers a menuView:render
, pageView:render
, articleView:render
, blockView:render
or componentView:render
event and which contains an element with the class js-heading
will now have a new HeadingView
injected into it.
HeadingView
is a visually hidden aria label; its text is transparent, tiny and absolutely positioned so that it is only read by the screen reader. Any visual representation of the heading text should therefore be hidden from the screen reader with an [aria-hidden="true"]
attribute. This is so that the heading text will not be read twice by the screen reader.
See: component.hbs
as an example.
- model:
_disableAccessibilityState: false
Will enable/disable theHeadingView
render - model:
_isA11yCompletionDescriptionEnabled: true
Will cause theHeadingView
to render the completion description - course.json:
_globals._accessibility._ariaLabels.complete: "Completed"
- course.json:
_globals._accessibility._ariaLabels.incomplete: "Incomplete"
Additional to and utilised by the HeadingView is the heading attribute helper {{a11y_attrs_heading 'componentItem'}}
. This helper will produce the role and default heading level attribute for an element. The default heading level is taken from the map in config.json: _accessibility._ariaLevels
. If the appropriate _ariaLevel
value is found in the current context, it overwrites the default. This behaviour allows menu, page, article, block and component default heading levels to be controlled from config.json
and also to be overridden on each model.
Note: Fixed in AAT v0.6.3_ariaLevel
is not yet defined in the Authoring Tool schemas.
- config.json:
_accessibility._ariaLevels: { componentItem: 5, ... }
- model:
_ariaLevel: 0
Note: Notify will always have heading level 1 since it is the only content represented on screen when active. As of v4.3.0 the Notify heading level can now be set via config.json
The Adapt Framework has a couple of template helpers functions which allow a developer to provide additional screen reader-only text labels for the user. These labels are visually hidden; their text is transparent, tiny and absolutely positioned so as not to influence the document flow.
{{a11y_aria_label 'text'}}
Will create a hidden text label.
{{a11y_aria_image 'alt text'}}
Will create a fake image label, so that a background image or content image label need to be represented out of place.
References: W3C - Invisible labels
Generic invisible label descriptions are defined by and enabled on each component. They appear under the component heading and are designed to help users understand how to interact with the user interface by providing a simple name and description of its behaviour.
Component descriptions can be disabled by setting "_isA11yComponentDescriptionEnabled": false
on the component models.
Note: _isA11yComponentDescriptionEnabled
is not yet defined in the Authoring Tool schemas.
See: narrative\properties.schema
as an example
When a modal popup, such as Notify or Drawer, is displayed, the user's actions should be restricted to just the popup content. With browsers and screen readers there are two ways of navigating to content under a modal popup: via tabbing and via the screen reader cursor.
To prevent users from accessing content outside of the modal popups, Adapt Framework can restrict both the tabbing and screen reader cursor, isolating the user to just the popup content.
Adapt.trigger("popup:opened", $popupElement);
will restrict the tabbing and screen reader access to just the element provided.
Adapt.trigger("popup:closed", $focusTarget);
will revert the tabbing and screen reader access and move the focus to the element provided.
Note: Notify and drawer both use this soft API to control content access and require no additional configuration. This API will be turned into a hard API at some point in the near future.
To enable 'tab wrapping' - where tabbing beyond the popup will return the user to the first tabbable element in the popup - add {{{a11y_wrap_focus}}
to the end of your popup template.
Note: Notify and Drawer both already implement this API and so require no additional modifications. Making use of these is therefore highly recommended.
Buttons and links which change the browser URL and/or load new content (that isn't a modal popup) should have an implied or declared [role="link"]
attribute.
Adapt Framework v4 applies this principle throughout by having [role="link"]
on all navigation buttons.
Reference: Mozilla - MDN - Using the link role
As focus moves through the document and on to elements which overflow the viewport, the browser will tend to scroll the subject elements into the viewport.
Adapt Framework v3 has a top navigation bar which overhangs the scrollable area of the viewport, obscuring any content beneath it. As the browser is both unaware of the navigation bar's obscuring of content and of the location of the screen reader cursor, the browser tends to scroll overflowing content underneath the navigation bar when bringing it into the viewport.
Adapt Framework v4 therefore introduces an additional configuration option which introduces a scrollable area beneath the navigation bar. Enabling this allows the browser to scroll Adapt content in a way which does not allow any content to be obscured by the navigation bar, which is preferable for screen reader access.
-
config.json: _scrollingContainer._isEnabled = true
Enable the scrolling container -
config.json: _scrollingContainer._limitToSelector = ""
Selectively apply the scrolling container according to this html tag selector. Useful for device targeting:".os-windows.chrome, .os-windows.ie.version-11-0"
Note: the downside of enabling _scrollingContainer
is that it will disable some functionality of mobile browsers. For example, on Safari for iOS, the address bar will not shrink in size when you scroll down the page, nor will the 'double-tap the top of the screen to scroll to top' functionality work.
Table showing the use cases for alternative scrolling:
Screen reader | iOS iframe | Browser gestures | |
---|---|---|---|
"_scrollingContainer": { "_isEnabled": false, "_limitToSelector": "" } |
❎ | ❎ | ✅ |
"_scrollingContainer": { "_isEnabled": true, "_limitToSelector": "" } |
✅ | ✅ | ❎ |
"_scrollingContainer": { "_isEnabled": true, "_limitToSelector": ".os-ios" } |
❎ | ✅ | ✅ (Not on iOS) |
"_scrollingContainer": { "_isEnabled": true, "_limitToSelector": ":not(.os-android)" } |
✅ | ✅ | ✅ (Android only) |
Occasionally, screen readers like Jaws do not read out the on-screen text correctly - and changing it to suit Jaws may not always be an option. The classic example is Jaws reading 'IT' as 'it' rather than 'eye tea'. As a workaround, Adapt v4.4.0 introduced the ally_text
handlebars helper which can be used to give screen readers a different version of the text to read out, for example: {{a11y_alt_text 'IT' 'I T'}}
or {{a11y_alt_text '$5bn' 'five billion dollars'}}
Accessibility in Adapt Framework v4 has been built with the following screen readers, browsers and operating systems in mind:
- JAWS 18+ and IE11 on Windows (primary testing target)
- JAWS 18+ and Chrome on Windows (used in development process)
- Voiceover and Chrome on OSX (used in development process)
- NVDA and Firefox ESR on Windows (untested at present)
Note: 'Evergreen' Firefox is currently having accessibility issues despite being one of the most requested browsers for corporate e-learning accessibility.
In lieu of semantic h
tags (<h1>
, <h2>
etc.) Adapt uses role="heading"
and aria-level="1"
attributes. This is because we use classes to style heading texts and templates+JSON to define the heading level throughout the framework, which makes using semantic h
tags unfeasible. The role="heading"
, aria-level="1"
attribute syntax is very well supported and an acceptable alternative for semantic h
tags. However, it is a relatively new technique, and some older accessibility testing tools/documentation may incorrectly flag it as an issue.
Reference: W3C - WCAG Technique - ARIA12
Instead of using <img alt="text">
we use <img aria-label="text">
. The aria-label
attribute supersedes the alt
attribute and is common to all elements in the DOM as well as being very well supported. The alt
attribute is only available on the <img>
tag and so should be thought of as a legacy attribute.
As with the use of role="heading"
attribute, some older accessibility testing tools/documentation may incorrectly flag the lack of alt
attributes as an issue. No matter how commonplace is the message that 'images must have alt tags in order to be accessible,' it is no longer accurate. Likewise, confusion persists between the alt
attribute and a visual “tooltip” that appears when hovering over an image.
References: W3C - WCAG Technique - ARIA6, Mozilla - MDN - ARIA:img role, W3C - WAI-ARIA 1.0 User Agent Implementation Guide, W3C - HTML Accessibilty API Mappings 1.0 - Draft
- ↑ = move backward
- ↓ = move forward
- space = make selection
- space/return = click
- h = cycle through headings
- r = cycle through regions
- q = go to main content
- esc = exit selection (forms mode) or dismiss notify
- W3C - Web content accessibility guidlines (WCAG) 2.1
- W3C - How to meet WCAG 2 (Quick reference)
- tink.uk - Understanding screen reader interaction modes
- javascript.info - Bubbling and capturing events
- Accessibility Developer Guide - Screen readers' browse and focus modes
- Freedom Scientific - JAWS 2019
- WebAIM - Keyboard shortcuts for JAWS
- NV Access - About NVDA
- Mozilla - Firefox ESR
- W3C - WCAG Technique - ARIA6
- W3C - WCAG Technique - ARIA12
- Mozilla - MDN - ARIA:img role
- Mozilla - MDN - Using the link role
- W3C - WAI-ARIA 1.0 User Agent Implementation Guide
- W3C - Accessibilty API Mappings 1.0 - IMG Element - Draft
- W3C - Invisible labels
- Framework in Five Minutes
- Setting up Your Development Environment
- Manual Installation of the Adapt Framework
- Adapt Command Line Interface
- Common Issues
- Reporting Bugs
- Requesting Features
- Creating Your First Course
- Styling Your Course
- Configuring Your Project with config.json
- Content starts with course.json
- Course Localisation
- Compiling, testing and deploying your Adapt course
- Core Plugins in the Adapt Learning Framework
- Converting a Course from Framework Version 1 to Version 2
- Contributing to the Adapt Project
- Git Flow
- Adapt API
- Adapt Command Line Interface
- Core Events
- Core Model Attributes
- Core Modules
- Web Security Audit
- Peer Code Review
- Plugins
- Developing Plugins
- Developer's Guide: Components
- Developer's Guide: Theme
- Making a theme editable
- Developer's Guide: Menu
- Registering a Plugin
- Semantic Version Numbers
- Core Model Attributes
- Adapt Command Line Interface
- Accessibility v3
- Adapt Framework Right to Left (RTL) Support