Custom checks

You can extend Sa11y beyond checking for accessibility issues. For example, create checks that promote better usability, or encourage proper use of components and web style guidelines. Custom checks should be created within /src/sa11y-custom-checks.js - which is seperate from Sa11y's core library.

How to create a custom check

You can create custom checks with just a few lines of JavaScript. There are two types of annotations you create: Button annotations and full-page banner annotations that are appended to the top of the page. There are also several utilities and selectors you can use.

Utilities

  • this.sa11y.computeAriaLabel() - Process ARIA attributes on elements.
  • this.sa11y.fnIgnore() - Ignore children of element.
  • this.sa11y.debounce() - Simple debounce utility.
  • this.sa11y.computeTextNodeWithImage() - Compute alt text on images within a text node.
  • this.sa11y.sanitizeForHTML() - Converts various HTML characters back to their respective entity code.

Selectors

For performance, you can reuse these selectors when creating custom checks.

  • this.sa11y.$h - Select all H1, H2, H3, H4, H5, H6, [role='heading'][aria-level]
  • this.sa11y.$h1 - Select all H1
  • this.sa11y.$img - Select all IMG
  • this.sa11y.$links - Select all A[HREF]
  • this.sa11y.$inputs - Select all INPUT
  • this.sa11y.$iframes - Select all IFRAME
  • this.sa11y.$videos - Select all IFRAME that contains a video player only
  • this.sa11y.$audio - Select all IFRAME and AUDIO that contains an audio player only
  • this.sa11y.$dataviz - Select all IFRAME that contains a visualization only
  • this.sa11y.$twitter - Select all Twitter IFRAME
  • this.sa11y.$embeddedContent - Select all IFRAME that is not an identified audio player, video player, Twitter widget or visualization
  • this.sa11y.$strongitalics - Select all STRONG and EM
  • this.sa11y.$badDevLinks - Select all links via linksToFlag prop.
  • this.sa11y.$checkPDF - Select all a[href$='.pdf']
  • this.sa11y.$tables - Select all TABLE
  • this.sa11y.$lang - Select HTML's LANG attribute
  • this.sa11y.$blockquotes - Select all BLOCKQUOTE
  • this.sa11y.$p - Select all P
  • this.sa11y.$allCaps - Select all H1, H2, H3, H4, H5, H6, LI, BLOCKQUOTE
  • this.sa11y.$readability - Select all P and LI within specified container

Button annotation

  1. Create your condition.
  2. Use this.sa11y.root instead of document within your selector.
  3. Add respective CSS classes.
    • Highlighting text: .sa11y-warning-text or .sa11y-error-text
    • Border around elements: .sa11y-warning-border or .sa11y-error-border or .sa11y-good-border
  4. Use the insertAdjacentHTML() method to append the annotation either before or after the element.
  5. Use this.sa11y.annotate(string, string, boolean) within the aforementioned method to generate the annotation.
    • String (type of annotation): M["ERROR"] or M["WARNING"] or M["GOOD"]
    • String: C["YOUR_CUSTOM_TOOLTIP_MESSAGE"])
    • Boolean (Optional): Pass true if you'd like to position the annotation inline with text.

Full-width banner annotation

  1. Create your condition.
  2. Use this.sa11y.root instead of document within your selector.
  3. Banners should be appended after Sa11y's container. Use this.sa11y.panel.insertAdjacentHTML() method with a position parameter of afterend.
  4. Use this.sa11y.annotateBanner(string, string) within the aforementioned method to generate the annotation.
    • String (type of annotation): M["ERROR"] or M["WARNING"] or M["GOOD"]
    • String: C["YOUR_CUSTOM_TOOLTIP_MESSAGE"])

Recipes

Both recipes below are included in sa11y-custom-checks.js

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 $checkAnnouncement = this.sa11y.root.querySelectorAll(".announcement-component");
if ($checkAnnouncement.length < 1) {
    for (let i = 1; i < $checkAnnouncement.length; i++) {
        $checkAnnouncement[i].classList.add("sa11y-warning-border");
        $checkAnnouncement[i].insertAdjacentHTML(
            "beforebegin",
            this.sa11y.annotate(
                M["WARNING"],
                C["QA_TOO_MANY_COMPONENTS_EXAMPLE"]
            )
        );
    }
}

Code explained

If Sa11y detects more than one instance of the .announcement-component CSS class, it will be flagged as a warning. The warning button will only appear on every instance except the first component. C['QA_TOO_MANY_COMPONENTS_EXAMPLE'] represents a string (tooltip message).

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 $checkAccordions = this.sa11y.root.querySelectorAll(".sa11y-accordion-example");
$checkAccordions.forEach($el => {
    const checkForm = $el.querySelector("form");
    if (!!checkForm && checkForm.length) {
        $el.classList.add("sa11y-error-border");
        $el.insertAdjacentHTML("beforebegin",
            this.sa11y.annotate(
                M["ERROR"],
                C["ACCORDION_FORM_MESSAGE"]
            )
        );
    }
});

Demo

Add or suggest a recipe

Share your recipe on GitHub.