Skip to content

Accessibility v4 → v5

kirsty-hames edited this page Nov 6, 2024 · 31 revisions

What has changed?

The development of Adapt v5 has continued focus on ensuring both the framework and core suite of Adapt plugins meet the WCAG criteria. Whilst some of the development has been addressing failed criteria, accessibility in Adapt is constantly being improved with the intention to further align with the WCAG. This is largely due to learners, clients, external auditors and accessibility specialists providing valuable feedback on courses authored in Adapt.

Aside from the built-in behaviours covered below, there's been a multitude of plugin updates. From enhanced ARIA, to setting default accessible labels and configurations to provide the most accessible experience. Reducing the setup and knowledge required of the content author.

References: https://www.w3.org/TR/WCAG22/ and https://www.w3.org/WAI/ARIA/apg/patterns/


New built-in behaviours

Scalability

Adopted relative unit measurement (rem) instead of fixed (px)

Rem units allow for dynamic scaling based on a single root font size and adjust proportionally. Giving greater flexibility over pixels. This is particularly beneficial for users who adjust their browser settings for larger text, as these units scale according to user-defined preferences.

Reference: WCAG 2.2 SC 1.4.4 Resize Text


Headings

Automated heading levels

Aria levels are calculated dynamically, based upon the existence of a title on the defined parent and the offset through the tree. For example, if a component has no article or block title, its aria level will be calculated relative to the page heading level 1, this is as the block and article offsets are not applied.

a11y.ariaLevel calculates the aria-level of a heading. Default levels come from config.json:_accessibility._ariaLevels attribute names. These are: "menu", "menuGroup", "menuItem", "page", "article", "block", "component", "componentItem" and "notify".

See: Automated aria levels

Reference: WCAG Headings


Navigation

Navigation button order

Button order was introduced to ensure the DOM order matches the visual order of the navigation bar. This also gives flexibility in the visual display of the navigation bar. The sorting order can be configured per course.

See: Navigation sorting order on the wiki

Reference: WCAG C27: Making the DOM order match the visual order

Navigation button labels

As part of the navigation button API, button labels were introduced to provide a text label based upon the button aria-label. Icons are a helpful tool to improve perception, but they aren't a replacement for text. Adding a visible text label makes the button’s purpose obvious.

See: Navigation Button API on the wiki

Skip to main content

Added the ability to skip to menu or page main content bypassing the navigation bar. Focus is directed to the main content with the first focusable element typically being the menu or page title.

Reference: WCAG G1: Adding a link at the top of each page that goes directly to the main content area


ARIA question field introduced across question components

When focusing on a question group or radiogroup, reading the component displayTitle, body & instruction in reference to the question options is too verbose. The ariaQuestion field provides the option to specify a succinct aria-label for the question with the original aria-labelledby as a fallback.

See: Adds ariaQuestion field to JSON default settings


Allow question answer buttons to be keyboard and screen reader accessible

Used by ButtonsView to retrieve question-specific user answer / correct answer text when question _canShowModelAnswer is enabled. When the learner selects the 'show correct answer' / 'hide correct answer' button, a 'live region' gets updated containing the user answer / correct answer aria-label text. Prior to this, the button was inaccessible but still enabled for visual users with question-specific user answer / correct answer provided by marking only (ticks and crosses displayed on answer options).

See: Allow correct answer/hide correct answer buttons to be keyboard/screen reader accessible


Support user preference for motion

Some users experience distraction or nausea from animated content. For example, if scrolling a page causes elements to move such as content transitioning into the page. When _isPrefersReducedMotionEnabled is enabled and prefers-reduced-motion is set in the browser (user preference) an is-prefers-reduced-motion class is appended to the html tag.

See: Animations in theme should use reduced motion media queries

Reference: WCAG C39: Using the CSS reduce-motion query to prevent motion


Support for semantic quotes

It's important to ensure that the HTML used in Adapt is semantically correct to ensure the content is accessible to as many users as possible. Representing quotes with CSS styling alone is not identified by assistive tech. The text will read as any other body text on the page regardless how different the visual styling is. Adapt styling for <blockquote> and <q> was introduced to Vanilla and a brief explanation of some common HTML elements has been detailed on the wiki.

See: Making quotes semantic to encourage inclusive design, Semantic HTML Quotation on the wiki

Reference: WCAG H49: Using semantic markup to mark emphasized or special text


Error message when no input provided for question

For question components, the submit button is now disabled until the learner makes a selection. Note: If you prefer the old behaviour, you can use the instructionError plugin to restore it and display an error reiterating the question instructions on an invalid submission.

See: Error message when no input provided for question

Updated and improved built-in behaviours

Invisible labels

The aria-label and aria-labbelledby attributes only work on interactive, focusable content. Instead, the .aria-label class is used on span elements to create invisible labels, available to screen readers only.

<span class='aria-label'>label text<span>

Including html markup in labels can cause some unexpected reading. Often the screen reader will read the html markup as content announcing each character. To prevent this, a11y.normalize is used to convert html to text. This remove all html encoded characters, such as &apos;.


Heading attribute helper

The heading attribute helper {{a11y_attrs_heading}} was deprecated in favour of {{a11y_aria_level}} as it's clearer for the handlebars. The helper produces the role and default heading level attribute for an element. The default heading level is taken from the map in config.json: _accessibility._ariaLevels. An absolute value would be "1" or "2" etc. A relative increment would be "@page+1" or "@block+1" (as per Automated heading levels above). They are calculated from ancestor values, respecting both _ariaLevel overrides and not incrementing for missing displayTitle values.

See: heading.hbs example


Heading completion settings

  • model: _disableAccessibilityState replaced by _isA11yCompletionDescriptionEnabled.
  • model: _isA11yCompletionDescriptionEnabled: true Will cause each HeadingView to render the completion description that describes the state of the content part (menu item, component, etc) to assistive tech.

See: Add _isA11yCompletionDescriptionEnabled to schema and remove legacy _disableAccessibilityState


Focus control

Readability, focus and seeking forward

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.

a11y.focusFirst($element) Assigns focus to either the specified element if it is readable or the next readable element. This replaces the deprecated $("selector").focusOrNext().

a11y.focusNext($element) Assigns focus to the first readable element after the specified element. This replaces the deprecated $("selector").focusNext().

Adapt will automatically focus next if the active element triggers a blur event due to becoming visibility hidden, display none or becoming disabled using the disabled attributes. This is controlled via config.json:_accessibility._options._isFocusNextOnDisabled.

Force focus for screen readers when space or enter keys pressed. Screen readers can send a click event without causing focus to happen in some circumstances. This is controlled via config.json:_accessibility._options._isFocusOnClickEnabled and is enabled by default.

Add a small delay to each click to allow screen readers to process focus. This is controlled mostly by config.json:_accessibility._options._isFocusOnClickEnabled and is disabled by default.

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. See a11y.js for further _accessibility._options defaults.

Visible focus

Rather than relying on keyboard detection, :focus-visible is used instead. The :focus pseudo-class always matches the currently-focused element and displays a visible focus ring, which some consider obtrusive. The :focus-visible pseudo-class only matches the focused element if the user needs to be informed where the focus currently is. The :focus-visible pseudo-class respects user agents' selective focus indication behaviour while still allowing focus indicator customisation.

See: MDN :focus vs :focus-visible, Updated focus to focus-visible

Self-disabling buttons

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.

Using aria-disabled improves discoverability as buttons will not be removed from the focus order of the page. Instead, it signals to assistive tech that a button is disabled whilst reading any associated label, for example “Submit button, unavailable”. Disabled buttons using the disabled attribute are typically ignored by assistive tech meaning a user won't be aware a button exists until it becomes available whilst the button is visually available to a sighted user.

Note, Adapt automatically cancels click events on aria-disabled elements but developers will need to change the appearance of elements so sighted users know they are disabled. This should be done via the .is-disabled class.

See: Switch from disabled to aria-disabled for ButtonsView

Reference: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-disabled, https://css-tricks.com/making-disabled-buttons-more-inclusive/


Popups

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.

Content restrictions

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.

a11y.popupOpened($element) restricts the tabbing and screen reader access to just the element provided. This replaces the deprecated Adapt.trigger("popup:opened", $popupElement).

a11y.popupClosed($target) reverts the tabbing and screen reader access and moves the focus to the element provided. This replaces the deprecated Adapt.trigger("popup:closed", $focusTarget).

Tab wrapping

When a modal popup, such as Notify or Drawer, is displayed, tabbing should work the same as it does for pages, in that the focus should circle around the browser UI when you leave the bottom of the content. tabindex="0" was added to {{{a11y_wrap_focus}}} to return focus to the first focusable element within the modal popup.

Clone this wiki locally