Tabs Implementation - Keyboard Interactions

Part 2 of Implementing the Tabs Component

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the The Approachable Guide to Accessible Components course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to The Approachable Guide to Accessible Components, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course The Approachable Guide to Accessible Components
  • [00:00 - 00:12] In this lesson, we'll finish building out our tabs component by implementing the keyboard interaction section of the tabs pattern specs. Oh, according to the specs, there are two variations of the tabs pattern that can be implemented.

    [00:13 - 00:25] One is the tabs with manual activation, and the other is tabs with automatic activation. For this example, we'll opt for the latter approach, automatically activating the tabs and showing their corresponding panels upon receiving focus.

    [00:26 - 00:40] But just to show an example of the two, tabs with manual activation, as you can see, I'm kind of tabbing through this one, but the content is not displaying until I press enter. And then the content for that particular tab that I'm focused on will display.

    [00:41 - 00:46] So that's kind of the unique distinction there. It doesn't show the content unless you explicitly say so.

    [00:47 - 01:06] Well, if you go to the automatic activation example, and this is the one you'll see 90% of the time in the world, by the way. As I row through each tab, it displays the currently focused tabs content. So that's the one that we'll be implementing. But I just wanted to provide show different examples of how they look and what the distinctions are.

    [01:07 - 01:13] Okay. So this means that we will not need to implement the space or enter key as outlined in the specs.

    [01:14 - 01:31] And additionally, for the sake of brevity, we'll skip incorporating optional keyboard interactions, for example, the home and end keys. So, yeah, so as you see here, like none of the optional things will implement just to keep it kind of simple. And then the space or enter key would kind of be not needed because we are doing the automatic activation.

    [01:32 - 01:45] So that will leave us with the tab key, the left arrow key and the right arrow key. So looking at the first bullet point within the tab key, it reads when focus moves into the tab list, it places focus on the active tab element.

    [01:46 - 02:03] So let's go back to tabs.jsx within code sandbox. And the first thing we need to do is add the tab and next attribute to the button element with the following logic. And button elements are focusable by default, but are added logic ensures that only the currently active tab stays focusable.

    [02:04 - 02:16] Without this, if a user continually pressed the tab key, it would move through each button instead of switching focus to the tab panel element. So, as you can see, my example, I'm actually going to revoke the code that I just added so you can see what the default behavior is with the buttons.

    [02:17 - 02:31] As I press the tab key, it's going through each button because they're focus able by default. Like I said, however, when I add back in the tab index logic, you'll notice that I can tab to the first button, but then after that, it goes away.

    [02:32 - 02:59] And so that actually transitions us nicely into our second bullet point within the tab key, which is when the tab list contains focus, it moves focus to the next element in the page tab sequence outside the tab list, which is the tab panel unless the first element containing the meaningful content inside the tab panel is focusable. So going back to code sandbox, the second thing we need to do is apply a tab index equals zero to all the tab panel elements.

    [03:00 - 03:14] So they can be focusable when they are in the DOM. So now when we press tab, we go to the first tab, which is stand gets in this case, and then pressing tab again will take us into the tab panel content.

    [03:15 - 03:21] Okay, so now we've got our tab key implementation wired up. Let's move on to the left arrow key.

    [03:22 - 03:31] And the first bullet point here says left arrow moves focus to the previous tab . If focus is on the first tab, it moves focus to the last tab.

    [03:32 - 03:45] It optionally activates the newly focused tab. So note here that since we're implementing tabs with automatic activation, that last sentence is not optional for us, and we must make it so that pressing the key activates the new tab.

    [03:46 - 04:04] So going back to the code, you know, we need to capture key presses and we'll include a keyboard event handler function and attach it to the div with a tab list role using the on key down attribute. Additionally, we'll create a handle key down function with the component that will be provided to the on key down handler.

    [04:05 - 04:14] So the handle key down function will be responsible for processing the logic of the arrow key presses and should include the following code. Right now, just wired up for the left arrow key.

    [04:15 - 04:23] And here's kind of a breakdown of what we have. So the switch statement will match on the key property of the keyboard event and attempt to guide the user to the desired tab.

    [04:24 - 04:34] If the tab that trying to navigate to falls within the array bounds. So for this particular example, which means zero and two, since there are three tabs, it will pass the value of that tab to the on value change prop.

    [04:35 - 04:52] However, if the user is at the first tab and the index is less than zero, it signifies that we're at the end of the list and needs to cycle back to the last tab. So now we have our left arrow key working.

    [04:53 - 05:06] Let's go ahead and go back to the specs and read out what's required for the right arrow key. The right arrow should move focus to the next tab. It focuses on the last tab element. It moves focus to the first tab and optionally activates newly focused tab.

    [05:07 - 05:22] So similarly to the left arrow key, we this last sentence is not optional for us and we need to make sure that pressing the key activates the new tab. So implementing the right arrow key will be actually a similar process to its counterpart.

    [05:23 - 05:35] And to register this additional key, we need to update the handle key down function. We just created to also check for the arrow right key event. And the code breakdown for the error right key is very similar to that of the arrow left key.

    [05:36 - 05:50] The main difference being that we increment by one when the right arrow is pressed instead of decrementing. We will still perform our out of bounds array check. However, this time, if the index of the tab they're trying to go to matches the length of the tab list array, then we loop them back around to the beginning of the list.

    [05:51 - 06:04] Integrating this logic actually introduces the concept of roving tab index, which is a lesson we covered earlier on in the course. Okay, so we've got our arrow key navigation and active states working, but there's still a little bit of a snag.

    [06:05 - 06:19] The focus doesn't change as we move through the tabs. So to fix this, we'll dive into React's use ref hook and learn how to switch focus to the appropriate element. So since this isn't a react course, I'm not going to dive too deep into the weeds of hooks and refs.

    [06:20 - 06:31] However, if you'd like to learn more, feel free to check out the official react documentation. Okay, so the first step we're going to do is create a variable with the component that will house an array of refs using the use ref hook.

    [06:32 - 06:46] This array will hold the references to all the button elements and needs to be defined at the top of the component. The second step is assigning the ref to buttons. So inside the mapping function where we create the button elements, we need to use a callback ref to assign the ref to each button.

    [06:47 - 07:03] A callback is executed with the DOM element as an argument, and you store that element in the button refs array at the corresponding index. So this code essentially says when a button is created, store reference to it in the button refs array at the same index as its position in the props that tabs list array.

    [07:04 - 07:30] Thirdly, we need to shift focus with the arrow keys. So in the handle key down function, when the left arrow or right arrow keys are pressed, we can now use the button refs array to access the button elements and shift the focus. So this code accesses the corresponding button element with the button refs array, and then it calls the focus method on it.

    [07:31 - 07:45] Okay, time for some testing. And we're going to run some accessibility audits to make sure our implementation is rock solid. So let's open up our code sandbox preview in a new tabs so that when we run the axe to F tools extension, we ensure it's only running on the preview and not in the sandbox environment.

    [07:46 - 07:51] And when we run a swift scan, it confirms that everything looks good and that there are no accessibility concerns. Great.

    [07:52 - 08:07] Next, we'll test out our functionality with a screen reader. So to start our voiceover, we'll press the shortcut command F5, and we'll start by pressing the tab key to select the first tab. And the screen reader will announce, Dan gets selected tab one of three.

    [08:08 - 08:21] Then we'll use the right arrow to move to the second tab, and the screen reader will announce Ella Fitzgerald selected tab two of three. And while focused on the second tab, let's press the tab key again, and then this action will shift the focus into the corresponding tab panel.

    [08:22 - 08:33] And the screen reader will announce Ella Fitzgerald tab panel. Select a tab two of three, jazz musician quote Charlie Parker, selected tab three, stand gets selected tab one Charlie Parker, selected tab Ella Fitzgerald .

    [08:34 - 08:43] And voila, we have now successfully implemented an accessible tabs component. Throughout this lesson, you learned how to reference the specifications and go step by step through building out and testing a tabs component.

    [08:44 - 08:50] The ARIA authoring practices guide provides a myriad of patterns and is a great resource to use when creating custom components.