Accessible switch element (web)

Viewed 171

I need to create a switch element in the header of an internal application that lets you switch between two user roles. The rest of the content is rendered based on the selected role — e.g. there are different navigation items and actions available for Role A and for Role B. It is also important to note that changing the role makes the page reload.

So far I wasn't able to find an accessible solution using only an input (checkbox/radio) or a button, so I combined both ideas into following (please ignore the styling of the button, it's only for demo purposes):

var button = document.querySelector('button');
var inputRoleA = document.getElementById("role-A");
var inputRoleB = document.getElementById("role-B");

button.addEventListener('click', function(event) {
  if (inputRoleA.checked) {
    inputRoleA.checked = false;
    inputRoleB.checked = true;
    button.innerText = 'Role B';
    button.classList.remove('selected-role-A');
    button.classList.add('selected-role-B');
    // AJAX call to server and page reload
  } else {
    inputRoleA.checked = true;
    inputRoleB.checked = false;
    button.innerText = 'Role A';
    button.classList.add('selected-role-A');
    button.classList.remove('selected-role-B');
    // AJAX call to server and page reload
  }
});
fieldset {
  border: none;
}

.sr-only {
  position: absolute;
  left: -10000px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

input:focus ~ button {
  outline: 2px solid blue;
}

button {
  width: 120px;
  padding: 4px 2px;
  background: #4a6a9e;
  border: 1px solid white;
  border-radius: 16px;
  color: white;
  font-size: 18px;
  cursor: pointer;
}

.selected-role-A {
  text-align: left;
}

.selected-role-B {
  text-align: right;
}

.selected-role-A::before,
.selected-role-B::after {
  content: "";
  display: inline-block;
  width: 22px;
  height: 22px;
  margin: 0 4px;
  background: white;
  border-radius: 50%;
}
<fieldset>
  <legend class="sr-only">Choose role (reloads the page):</legend>
  <input class="sr-only" id="role-A" name="roles" type="radio" value="Role A" checked>
  <label class="sr-only" for="role-1">Role A</label>
  <input class="sr-only" id="role-B" name="roles" type="radio" value="Role B">
  <label class="sr-only" for="role-B">Role B</label>
  <button tabindex="-1" aria-hidden="true" class="switch selected-role-A">
    Role A
  </button>
</fieldset>

So basically I show a fieldset with the two options as radio inputs for screen-reader users and a switch-alike button in the browser.

Regarding the page reload — WCAG technique G13 requires to inform the user what will happen if a change on a form element leads to change of the context (like a reload in our case). Unfortunately, I can't put a visual information around the button because of design requirements, so I only added it in the fieldset for screen-readers. However, as far I understand it, this shouldn't be a problem if the page is a intranet application and the users are going to be trained (like in our case).

Are there any other accessibility problems with my solution? Does anyone know how to achieve this without using two separate elements? Thanks in advance.


EDIT: Solved tabbing problem.

1 Answers

Will it be common for a person to have both roles and that they'd want to switch roles while they're using the application? It'd be nice if you predetermined the role before getting to the app so that you don't need this switch. But going on the premise that you need to switch roles mid-application, I'll provide a few accessibility thoughts.

You are correct that separate elements are going to cause problems. As noted, using a "sr-only" type class just visually hides the information but does not prevent keyboard focus. You'd need tabindex="-1" for that, but then a screen reader user that uses the tab key would not be able to get to the element. That would be bad, mmkay.

"sr-only" classes are for visually hiding text and not interactive elements.

For the low-vision user that has some sight but augments their experience by also using a screen reader (and possibly a screen magnifier), they'll see a button with "Role A" but they won't hear it because it's aria-hidden even though they can tab to it. That will also cause confusion.

The best solution is to have one interface for all users and make it semantically correct. The challenge, as you obviously encountered, is what is the best widget to use and how to convey what that widget does.

One possibility is using a tab control. You can have "Role A" on one tab and "Role B" on another tab. Then the user can switch between the two to their heart's content. If a user doesn't have both roles, then either one of the tabs is disabled, or the tab is removed completely and they just have the elements for their one role. Using a tab control might make reloading the page unnecessary, but I can't say for sure because I don't know enough about what changing a role means.

If a page reload is necessary, all users should be notified about that. It's not just for visually impaired. Some "disabilities" are hidden, such as cognitive impairments, which has a huge spectrum of issues. Having a page reload unexpectedly can be confusing for some cognitive impairments. I'm not sure I follow why a simple phrase, such as "(reloads page)", cannot be woven into the interface because of "design requirements". "Accessibility requirements" are as important as "design requirements".

Your original <fieldset> solution looks promising (if you remove the "sr-only" class) because it handles sighted users, low vision users, blind users, cognitive issues, etc. But it's still a little weird to have a radio button cause a page reload. That goes against the "Understandable" principle of WCAG even though you can satisfy 3.2.2 by having an appropriate "reload" warning.

It seems like a widget with more "oomph" is required. A widget that implies an action will occur, which is usually a button.

When you struggle with design questions like this, you sometimes have to go back to the design and rethink the workflow. Can a role be determined before you get to the page (thus eliminating the need to switch roles)? Is there a need for the role to be changed mid-application? Etc.

Also, one side note, the accessibility of an application doesn't matter if the app is internal or external, even if training is provided. If the page doesn't have semantically correct elements, it will be inaccessible to some users no matter how much training you have.

Related