shipshapecode/shepherd

Aria-labels on .shepherd-elements of steps without titles

TheBrockEllis opened this issue · 0 comments

First off, thanks for the amazing tool. I love how customizable it is. It's worked perfectly for us.

We're going through a big accessibility audit (enroute to our first VPAT) and I've been wiring up all of our pages with automated a11y tests. While testing the tour, I came across this error and I'm not sure how to proceed.

Found 1 accessibility violation:

1) aria-dialog-name: ARIA dialog and alertdialog nodes should have an accessible name (serious)
    https://dequeuniversity.com/rules/axe/4.7/aria-dialog-name?application=axeAPI
    The following 1 node violate this rule:
    
        Selector: .shepherd-element
        HTML: <div aria-describedby="tour-step-zero-description" role="dialog" tabindex="0" class="shepherd-element shepherd-tour shepherd-centered shepherd-enabled" data-shepherd-step-id="tour-step-zero" data-popper-escaped="" data-popper-placement="top" style="position: fixed; left: 50%; top: 50%; margin: 0px; transform: translate(-50%, -50%);">
        Fix any of the following:
        - aria-label attribute does not exist or is empty
        - aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty
        - Element has no title attribute

(error via axe-core-api gem for a ruby on rails project)

The problem seems to be that the .shepherd-element doesn't have a proper aria-label on it. Our tours are just text and don't have shepherd's titles on them so the aria-labeledby doesn't get applied to the wrapper element. It seems like the right way to get this to work is to add a title to the step so that Shepherd generates the aria-labeledby for the wrapper, but then we have to get the product team involved since it modifies the UI.

Any chance you'd be open to a PR that allows users to specify a aria-label directly for each step so that it can be applied directly to the generated wrapped .shepherd-element?

Here's the code for the step:

tour.addSteps([
      {
        id: 'tour-step-zero',
        // label: "Proposed aria-label goes here??" <~~~~~~~~~~~
        text: `
          <div class='d-flex flex-column justify-content-center align-items-center' style='min-height: 200px;'>
            <div class='my-5 d-flex flex-direction-column justify-content-center align-items-center' style='width: 100px; height: 100px; border-radius: 100px;'>
              <img src='image.src' style='width: 50%; display: block; margin: 0 auto;' />
            </div>
          </div>
          <div class='p-5'>
            <h2 class='mb-5 text-center'>Hi, ${firstName}!</h2>
            <p class='text-muted text-center lh-base'>Text goes here.
            <div class='text-center'>
              <button class='btn-sm border-0 px-5 py-3 mb-3' type='button' id='tour-lets-go'>
                Let's Go!
              </button>
              <br>
              <a class='text-decoration-none' href='#' id='tour-skip'>No, thanks! I'll explore on my own.</a>
            </div>
          </div>
        `,
        when: {
          show: () => {
            const skipButton = document.querySelector('#tour-skip')
            skipButton.addEventListener('click', function (event) {
              event.preventDefault();
              tour.complete()
            })

            const goButton = document.querySelector('#tour-lets-go')
            goButton.addEventListener('click', function (event) {
              tour.next()
            })
          }
        }
      },
     ...