Custom checks

Unlock the full potential of Sa11y beyond accessibility checks. Craft custom checks that encourage proper component usage or align with your web style guidelines. Sa11y not only aids in identifying accessibility issues but also contributes to enhancing usability and overall user experience.

Different ways of adding custom checks

There are three methods for incorporating custom checks, ordered by their level of flexibility:

1. Have Sa11y "listen" for custom checks

Using an event dispatcher, have Sa11y "listen" for custom checks. Sa11y will wait for a dispatched event before running all checks.

  • Benefits: No build step required. Keep Sa11y easily up-to-date via CDN or when using official plugin.
  • Cons: Limited helper methods and functions.
  • Good for: cases where Sa11y is integrated within content management systems like Joomla, WordPress, or Drupal, where direct JavaScript file uploads may not be feasible. Custom checks logic can be provide via plugins that allow for the insertion of inline JavaScript or through a separate JavaScript file uploaded manually.

Example code

When instantiating, include the string listen within the customChecks prop. Sa11y will wait for up to 500 milliseconds before continuing with the rest of the core checks. You have the option to adjust this timing by modifying the delayCustomCheck: 500 prop.

// Instantiate
const sa11y = new Sa11y({
  checkRoot: 'main',
  customChecks: 'listen',
});

Add your custom checks within the event listener wherever it is convenient:

document.addEventListener('sa11y-custom-checks', () => {

  const accordions = sa11y.find('.sa11y-accordion-example', 'root');
  accordions.forEach((accordion) => {
    const form = accordion.querySelector('form');
    if (!!form && form.length) {
      sa11y.results.push({
        element: accordion,
        type: 'error',
        content: 'Do <strong>not nest forms</strong> within the Accordion component. If the form contains validation issues, a person may not see the form feedback since the accordion panel goes back to its original closed state.',
        inline: false,
        position: 'beforebegin',
      });
    }
  });

  // Event is dispatched to let Sa11y know custom checks are done.
  const allDone = new CustomEvent('sa11y-resume');
  document.dispatchEvent(allDone);
});

2. Passing a results object upon instantiation

Passing a results object via customChecks prop when instantiating.

  • Benefits: No build step or local installation required. Keep Sa11y easily up-to-date via CDN.
  • Cons: No access to helper methods and functions.
  • Good for: rapid development of custom checks without local installation.

Example code

Order is crucial. Custom checks must precede instantiation. Bring Your Own Code (BYoC): no access to Sa11y's helper methods and utilities. If necessary, copy and paste any one of these utility functions.

// Initialize array
let customResults = [];

// Should match the root area you are checking for.
const root = document.querySelector('main');

const accordions = root.querySelectorAll('.sa11y-accordion-example');
accordions.forEach((accordion) => {
  const form = accordion.querySelector('form');
  if (!!form && form.length) {
    customResults.push({
      element: accordion,
      type: 'error',
      content: 'Do <strong>not nest forms</strong> within the Accordion component. If the form contains validation issues, a person may not see the form feedback since the accordion panel goes back to its original closed state.',
      inline: false,
      position: 'beforebegin',
    });
  }
});

// Instantiate.
const sa11y = new Sa11y({
  checkRoot: 'main',
  customChecks: customResults,
});

3. Locally using build step

Install Sa11y locally and create custom checks within /src/sa11y-custom-checks.js.

  • Benefits: Full access to all helper utilities and elements. Custom checks compiled into core library resulting in a single, minified JavaScript file.
  • Cons: Requires Sa11y to be installed locally and a build step.
  • Good for: developers who prefer to have a local installation and keep everything in one place.

Add your custom logic via sa11y-custom-checks.js and be sure to set customChecks prop to true when instantiating.

Anatomy of a custom check

You can create custom checks with just a few lines of code. There are two types of annotations you can create:

  1. Button annotations appended at the source of the issue.
  2. Page issues that are displayed within Sa11y's panel.

Simply populate the results object.

Annotation

  • element (Element) - Issue element
  • type (String) - Type of issue: error, warning, or good
  • content (String) - Tooltip message.
  • inline (Boolean) - Optional. Set to true to display annotation inline with text.
  • position (String) - Where to insert annotation. View insertAdjacentHTML() parameters.
  • dismiss (String) - Optional. A string that uniquely identifies the found issue.
    • Important: Errors cannot be dismissed, only warnings can.
    • Important: The dismissal key must be unique for each check. It's recommended to pass the element's inner text for example.
      • If using the event dispatcher method for custom checks, use the sa11y.prepareDismissal method to generate a key. If using the second method (passing an object upon instantiation), copy and paste the prepareDismissal() function from the utilities file.

Page Issue

Simply pass a type and content to create a Page Issue that's displayed in the main panel.

  • type (String) - Type of issue: error, warning, or good
  • content (String) - Issue message.
  • dismiss (String) - Optional. A string that uniquely identifies the found issue.
    • Errors cannot be dismissed, only warnings can.
    • It's very important that the key is unique.

Try: Press alt A to enable Sa11y and view an example "Page Issue."

Recipes

The following recipes utilize the first 'listen' method of adding custom checks. Inspect the script area of this page to see it in action!

Overusing a component on a page

The example detects if more than one announcement is detected on a page, although it could be easily extended to anything. Too many slideshows? Too many accordions? You name it.

const announcement = sa11y.find('.sa11y-announcement-component', 'root');
if (announcement.length > 1) {
  for (let i = 1; i < announcement.length; i++) {
    const key = sa11y.prepareDismissal(announcement[i].textContent);
    sa11y.results.push({
      element: announcement[i],
      type: 'warning',
      content: 'More than one Announcement component found! The Announcement component should be used strategically and sparingly. It should be used to get attention or indicate that something is important. Misuse of this component makes it less effective or impactful. Secondly, this component is semantically labeled as an Announcement for people who use screen readers.',
      inline: false,
      position: 'beforebegin',
      dismiss: key,
    });
  }
}

Demo

This is an announcement component!
This is another announcement component!

Accordion component contains a form

Forms nested within accordion components can be problematic if it uses server-sided validation. The page will reload, and the accordion usually returns back to its closed state. This is problematic for everyone.

const accordions = sa11y.find('.sa11y-accordion-example', 'root');
accordions.forEach(($el) => {
  const form = $el.querySelector('form');
  if (!!form && form.length) {
    results.push({
      element: $el,
      type: 'error',
      content: 'Do <strong>not nest forms</strong> within the Accordion component. If the form contains validation issues, a person may not see the form feedback since the accordion panel goes back to its original closed state.',
      inline: false,
      position: 'beforebegin',
    });
  }
});

Demo

Add or suggest a recipe

Share your recipe on GitHub.