14 Develop Accessible Oracle JET Apps

Oracle JET and Oracle JET components have built-in accessibility features for people with disabilities. Use these features to create accessible Oracle JET web and hybrid mobile app pages.

About Oracle JET and Accessibility

Oracle JET components have built-in accessibility support that conforms with the Web Content Accessibility Guidelines version 2.1 at the AA level (WCAG 2.1 AA), developed by the World Wide Web Consortium (W3C).

Accessibility involves making your app usable for people with disabilities such as low vision or blindness, deafness, or other physical limitations. This means, for example, creating apps that can be:

  • Used without a mouse (keyboard only).

  • Used with assistive technologies such as screen readers and screen magnifiers.

  • Used without reliance on sound, color, animation, or timing.

Oracle JET components provide support for:

  • Keyboard and touch navigation

    Oracle JET components follow the Web Accessibility Initiative - Accessible Rich Internet Application (WAI-ARIA) Developing a Keyboard Interface guidelines. Follow these guidelines when using Oracle JET components in your app. For example, avoid setting tabindex to values greater than 0. The API documentation for each Oracle JET component lists its keyboard and touch end user information when applicable, including a few deviations from the WAI-ARIA guidelines.

  • Zoom

    Oracle JET supports browser zooming up to 400%. For example, on the Firefox browser, you can choose View -> Zoom -> Zoom In.

  • Screen reader

    Oracle JET supports screen readers such as JAWS, Apple VoiceOver, and Google Talkbalk by generating content that complies with WAI-ARIA standards, and no special mode is needed.

  • Oracle JET component roles and names

    Each Oracle JET component has an appropriate role, such as button, link, and so on, and each component supports an associated name (label), if applicable.

  • Sufficient color contrast

    Oracle JET provides the Redwood theme, starting in Oracle JET release 9.0.0, which is designed to provide a luminosity contrast ratio of at least 4.5:1.

Oracle JET does not support the use of access keys due to their negative impact on assistive tooling and accessibility.

Oracle documents the degree of conformance of each product with the applicable accessibility standards using the Voluntary Product Accessibility Template (VPAT). You should review the appropriate VPAT for the version of Oracle JET that you are using for important information including known exceptions and defects, if any.

While Oracle JET is capable of rendering an app that conforms to WCAG 2.1 AA to the degree indicated by the VPAT, it is the responsibility of the app designer and developer to understand the applicable accessibility standards fully, use JET appropriately, and perform accessibility testing with disabled users and assistive technology.

About the Accessibility Features of Oracle JET Components

Oracle JET components are designed to generate content that conforms to the WCAG 2.1 AA standards. In most cases, you don't need to do anything to add accessibility to the Oracle JET component. However, there are some components where you may need to supply a label or other property.

For those components, the component's API documentation contains an Accessibility section that provides the information you need to ensure the component's accessibility.

Note:

Some Oracle products have run-time accessibility modes that render content optimized for certain types of users, such as users of screen readers. For the most part, Oracle JET renders all accessibility-related content all of the time. There is only a mode for users that rely on the operating system's high contrast mode, which is described in Create Accessible Oracle JET Pages.

Oracle JET components that provide keyboard and touch navigation list the keystroke and gesture end user information in their API documentation. Since the navigation is built into the component, you do not need to do anything to configure it.

You can access an individual Oracle JET component's accessibility features and requirements in the API Reference for Oracle® JavaScript Extension Toolkit (Oracle JET). Select the component you're interested in from the list on the left. You can also find the list of supported keystrokes and gestures for each Oracle JET component that supports keystrokes and gestures in the Oracle® JavaScript Extension Toolkit (JET) Keyboard and Touch Reference.

Create Accessible Oracle JET Pages

Content generated by Oracle JET is designed to conform to the WCAG 2.1 AA standards. However, many standards are not under the complete control of Oracle JET, such as overall UI consistency, the use of color, the quality of on-screen text and instructions, and so on.

A complete product development plan that addresses accessibility should include proper UI design, coding, and testing with an array of tools, assistive technology, and disabled users.

Note:

In most cases, end-user documentation for your app must describe information about accessibility, such as example keystrokes needed to operate certain components.

Configure WAI-ARIA Landmarks

WAI-ARIA landmarks provide navigational information to assistive technology users. Landmark roles identify the purpose of a page region and allow the user to navigate directly to a desired region. Without landmarks, assistive technology users must use the TAB key to navigate through a page.

The Oracle JET team recommends the use of WAI-ARIA landmarks to ensure page accessibility and provides examples you can use in the Oracle JET Starter Template collection. The following figure shows the run-time view of the Oracle JET Web Nav Drawer Starter Template. In this example, the page is organized into regions compatible with WAI-ARIA landmark regions, including regions for the banner, navigation, main, and contentinfo landmarks.

The highlighted code in the following example shows the landmarks for the Web Nav Drawer Starter Template. Each landmark is placed on the HTML element that defines the landmark region: div for the navigation regions, header for the banner region, oj-module for the main region, and footer for the contentinfo region.

<!DOCTYPE html>
<html lang="en-us">
  
  <head>
    <title>Oracle JET Starter Template - Web Nav Drawer</title>
    ...contents omitted
  </head>
  
  <body class="oj-web-applayout-body">
    ...contents omitted

    <div id="globalBody" class="oj-offcanvas-outer-wrapper oj-offcanvas-page">
      <div id="navDrawer" role="navigation" class="oj-contrast-marker oj-web-applayout-offcanvas oj-offcanvas-start">
        <oj-navigation-list id="navDrawerList" data="[[navDataProvider]]"
                            edge="start"
                            item.renderer="[[KnockoutTemplateUtils.getRenderer('navTemplate', true)]]"
                            on-click="[[toggleDrawer]]"
                            selection="{{selection.path}}">
        </oj-navigation-list>
      </div>

      <div id="pageContent" class="oj-web-applayout-page">

        <header role="banner" class="oj-web-applayout-header">
          <div class="oj-web-applayout-max-width oj-flex-bar oj-sm-align-items-center">
            ...contents omitted
          </div>
          <div role="navigation" class="oj-web-applayout-max-width oj-web-applayout-navbar">
            <oj-navigation-list id="navTabBar" class="oj-sm-only-hide oj-md-condense oj-md-justify-content-flex-end"
                                data="[[navDataProvider]]"
                                edge="top"
                                item.renderer="[[KnockoutTemplateUtils.getRenderer('navTemplate', true)]]"
                                selection="{{selection.path}}">
            </oj-navigation-list>
          </div>
        </header>

        <oj-module role="main" class="oj-web-applayout-max-width oj-web-applayout-content" config="[[moduleAdapter.koObservableConfig]]">
        </oj-module>

        <footer class="oj-web-applayout-footer" role="contentinfo">
          ...contents omitted
        </footer>
      </div>
    </div>
    <!-- This injects script tags for the main javascript files -->
		<!-- injector:scripts -->
		<!-- endinjector -->
  </body>
</html>

If your app includes a complementary region, add role="complementary" to the HTML div element:

<div role="complementary"></div>

For additional information about WAI-ARIA landmark roles, see landmark_roles.

Configure High Contrast Mode

High contrast mode is for people who require a very high level of contrast in order to distinguish components on the page. Operating systems such as Microsoft Windows and macOS provide methods for users to run in high contrast mode.

The graphic below shows the effect of changing to high contrast mode on Oracle JET icon font images.

Oracle JET provides the oj-hicontrast class that you can use to configure high contrast mode in your app.

Understand Color and Background Image Limitations in High Contrast Mode

There are color and background image limitations in high contrast mode that your app may need to work around.

In high contrast mode the colors in the CSS may be ignored or overridden, including background, border, and text colors. Therefore, in high contrast mode you may need to find an alternative way to show state. For example, you might need to add or change the border to show that something is selected. Also, your app may need to show alternate high contrast images that work on dark or light background color. Some operating systems, like Microsoft Windows, offer multiple display profiles for high contrast mode, including a black-on-white and white-on-black mode.

Consider providing an image that includes a background, so either black on a white background or white on a black background. That way it won’t matter what the background color is on the page since the contrast is in the image itself.

Add High Contrast Mode to Your Oracle JET App

In most cases, you do not need to do anything to enable high contrast mode in your Oracle JET app. If you're using RequireJS to load Oracle JET component modules, Oracle JET loads a script that attempts to detect if a user is running in high contrast mode. If the script succeeds at detection, it places the oj-hicontrast class on the body element.

There is a limitation to this method, however. There is no standard way to detect high contrast mode, and we can't guarantee that the script works in all cases on all browsers. To guarantee that the .oj-hicontrast styles are applied, add a user preference setting for high contrast to your app and configure the app to add the oj-hicontrast class to the body element when the preference is set. When the class is added, the .oj-hicontrast CSS styles are applied to the page where defined. The code below shows an excerpt from the Redwood CSS which changes the outline-width to 3 on the ojButton component when the button has focus.

.oj-hicontrast .oj-button.oj-focus .oj-button-button {
  outline-width: 3px; }

Note:

For disabled content, JET supports an accessible luminosity contrast ratio, as specified in WCAG 2.1 - Success Criterion 1.4.3 Contrast (Minimum), in the themes that are accessible.

Section 1.4.3 says that text or images of text that are part of an inactive user interface component have no contrast requirement. Because disabled content may not meet the minimum contrast ratio required of enabled content, it cannot be used to convey meaningful information. For example, a checkbox may still appear checked in disabled mode, but since the colors may not pass contrast ratio, you cannot rely on all users being able to see that it's checked. If the information is needed to interact with the page correctly, you must convey it using a different method, for example as plain text.

Add High Contrast Images or Icon Fonts

To support high contrast image files, Oracle JET provides Sass mixins that you can use to generate the correct CSS in high contrast mode to:

  • Use an alternate image.

  • Use images without using background images.

The Oracle JET Cookbook provides examples that you can use at: CSS Images.

You can also use icon fonts instead of image files to support high contrast mode. The limitation is that icon fonts use a single color. Since these icons are text, they will be guaranteed to contrast against the background color on systems that ignore colors in the CSS. However, if you use color to show state (for example, changing an icon to blue when selected), the colors may be ignored in high contrast modes. You may need to find an alternative like setting a border instead. The Oracle JET cookbook provides icon font demos at: Icon Fonts.

Test High Contrast Mode

The recommended way to test high contrast mode in Oracle JET app is to set high contrast mode at the operating system level and test your app in browsers that support high contrast mode, such as Google Chrome, Microsoft Edge, and Mozilla Firefox.

For information about enabling high contrast mode, see the documentation for your computer’s operating system. For example, to turn high contrast mode on and off in Microsoft Windows, use the following key combination: Left Alt+Left Shift+PrtScn. You may need to refresh your browser to see the new mode.

Hide Screen Reader Content

Sometimes you want to have some text on the page that is read to the screen reader user, but the sighted user doesn't see. Oracle JET provides the oj-helper-hidden-accessible class that you can use to hide content.

You can find the .oj-helper-hidden-accessible style defaults in the Redwood theme CSS file (web/css/redwood/x.x.x/web/redwood.css). For additional information about theming and Oracle JET, see Use CSS and Themes in Oracle JET Apps.

Use ARIA Live Region

An ARIA live region is a mechanism to notify assistive technologies when a web page content is updated.

When using a single page app, if there are any changes to the page or the page region, the user of assistive technologies, such as screen readers, is not notified about the changes in the page content since the URL of the app has not changed. To help provide a notification to the screen readers when a page or segments of a page change, an ARIA live region announces the dynamic changes within a web page. When the update takes place within an ARIA live region, a screen reader is automatically notified, wherever its focus is at the time, and it announces the updated content to the user. This can be achieved by using the aria-live attribute.

The aria-live attribute identifies an element as a live region. It takes three possible values:

  • off: No notification

  • polite: Screen reader notifies user once the current task is complete

  • assertive: Screen reader interrupts the current task to notify user

If the value of the aria-live attribute defined for an element is set to polite, your screen reader will not be interrupted and will announce the changes in the ARIA live region when the user has no activity on the screen. If the value is set to assertive, the new information has high priority and should be notified or announced to the user immediately.

You can also use some of the advanced ARIA live region attributes to communicate information about the entire live region or a portion of the live region to assistive technologies. Some of the advanced live region attributes to use are:

  • aria-atomic: The aria-atomic attribute is used along with aria-live attribute when the page contains live regions. This attribute is used to set whether the assistive technologies should present the entire live region as a single unit or to only announce the regions that have been changed. The possible values are true or false. The default value is false.

  • aria-relevant: This attribute is used in conjunction with the aria-live attribute to describe the types of changes that have occurred within a given live region that should be announced to the user. The possible settings are additionsremovalstext, and all. The default setting is additions text.

The following example shows a sample of the ARIA live region defined in the index.html file of an app. In this example, the aria-live attribute is set to assertive and the aria-atomic attribute is set to true:

<div id="globalBody" class="oj-offcanvas-outer-wrapper oj-offcanvas-page">
    <!-- Region for announcing messages to Screen Readers -->
    <div id="announce" class="sendOffScreen" :aria-live="[[manner() ? manner() : 'assertive']]" aria-atomic="true">
      <p id="ariaLiveMessage"><oj-bind-text value="[[message]]"></oj-bind-text>
    </div>
    <div id="navDrawer" role="navigation" class="oj-contrast-marker oj-web-applayout-offcanvas oj-offcanvas-start">
      <oj-navigation-list id="navDrawerList" data="[[navDataProvider]]" edge="start" item.renderer="[[KnockoutTemplateUtils.getRenderer('navTemplate', true)]]"
        on-click="[[toggleDrawer]]" selection="{{selection.path}}">
      </oj-navigation-list>
    </div>
    <div id="pageContent" class="oj-web-applayout-page">
		... contents omitted 

The following example shows how to set up the observables and event listeners in the appController.js file of the app.

define(['knockout', 'ojs/ojresponsiveutils', 'ojs/ojresponsiveknockoututils', 'ojs/ojcorerouter', 'ojs/ojarraydataprovider', 'ojs/ojknockout', 'ojs/ojoffcanvas'],
  function (ko, ResponsiveUtils, ResponsiveKnockoutUtils, CoreRouter, ArrayDataProvider) {
    function ControllerViewModel() {
      var self = this;

      self.manner = ko.observable('assertive');
      self.message = ko.observable();
      function announcementHandler(event) {
        self.message(event.detail.message);
        self.manner(event.detail.manner);
      };

      document.getElementById('globalBody').addEventListener('announce', announcementHandler, false);


      var smQuery = ResponsiveUtils.getFrameworkQuery(ResponsiveUtils.FRAMEWORK_QUERY_KEY.SM_ONLY);
      self.smScreen = ResponsiveKnockoutUtils.createMediaQueryObservable(smQuery);
      var mdQuery = ResponsiveUtils.getFrameworkQuery(ResponsiveUtils.FRAMEWORK_QUERY_KEY.MD_UP);
      self.mdScreen = ResponsiveKnockoutUtils.createMediaQueryObservable(mdQuery);

      let navData = [
        { path: '', redirect: 'dashboard' },
        { path: 'dashboard', detail: { label: 'Dashboard', iconClass: 'oj-ux-ico-bar-chart' } },
        { path: 'incidents', detail: { label: 'Incidents', iconClass: 'oj-ux-ico-fire' } },
        { path: 'customers', detail: { label: 'Customers', iconClass: 'oj-ux-ico-contact-group' } },
        { path: 'about', detail: { label: 'About', iconClass: 'oj-ux-ico-information-s' } }
      ];

      // Router setup
      let router = new CoreRouter(navData, {
        urlAdapter: new UrlParamAdapter()
      });
      router.sync();

      this.moduleAdapter = new ModuleRouterAdapter(router);

      this.selection = new KnockoutRouterAdapter(router);

      // Setup the navDataProvider with the routes, excluding the first redirected
      // route.
      this.navDataProvider = new ArrayDataProvider(navData.slice(1), {keyAttributes: "path"});
	... contents omitted 

To send the announcement, you can use a dispatcher that can be defined within the page viewModel files or in the router enter method. The following example shows a page viewModel file that fires a dispatch in the self.transitionCompleted lifecycle method.

define(['knockout','ojs/ojpopup'],
  function (ko) {

    function DashboardViewModel() {
      var self = this;

      self.transitionCompleted = function (info) {
        var message = "Loaded Dashboard page"
        var params = {
          'bubbles': true,
          'detail': { 'message': message, 'manner': 'assertive' }
        };
        document.getElementById('globalBody').dispatchEvent(new CustomEvent('announce', params));
      };

For more information on using an ARIA live region, see ARIA Live Regions.