Tabs Implementation - Roles, States, and Properties

Part 1 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:18] All righty, now that we have an overview of what we're building, we'll kick things off by putting into actions of the instructions laid out in the "Wall ARIO Role States" and "Properties" section of the documentation. We'll proceed methodically adding code step by step, demonstrating the gradual process involved in implementing the tabs component pattern.

    [00:19 - 00:29] Let's take a look at the first bullet point in this section of the documentation. It reads, "The element that serves as a container for the set of tabs has role tab list."

    [00:30 - 00:40] If we go back to our tabs.jsx file within code sandbox, we're going to start things off by building our base for our tabs component. To do this, we'll add two div elements.

    [00:41 - 00:55] The first div will act as the root element for the inner tab component parts, and the second div aligns with the specifications functioning as the encompassing container for the tab elements. This specific element will be designated with the tab list role.

    [00:56 - 01:08] As you can see, now our tabs, we start to see some things show up on the right-hand side of the screen. Now let's go back to the tab specs and take a look at the second bullet point.

    [01:09 - 01:24] This one reads, "Each element that serves as a tab has role tab and is contained within the element with role tab list." Let's go back to code sandbox and with our container in place, we can now start integrating the tab elements.

    [01:25 - 01:34] To achieve this, we'll utilize the tab list prop, which holds an array of object details for each tab. We're going to iterate through this list to present the container with the tab list role.

    [01:35 - 01:51] Considering the interactive nature of tabs, we'll implement the use of the button element, and each button element will be assigned the tab role. All right, so let's go back to the specs and take a look at the third bullet point, which reads, "Each element that contains the content panel for a tab has role tab panel."

    [01:52 - 02:04] When you interact with a tab, the expected behavior is that it will reveal some kind of content. We'll once more iterate through the tab list prop, generating separate div elements, each designated to hold distinct content for display.

    [02:05 - 02:19] Each of these div elements will be attributed with the tab panel role, which is used to indicate that it is an element that contains the content associated with a particular tab. Outside the div with the tab list role, but within the parent container, we will add the following code.

    [02:20 - 02:44] All right, so going back to our specs one more time, let's look at the fourth bullet point, which says, "If the tab has a visible label, the element with role tab list has aria labeled by set to a value that refers to the labeling element. Otherwise, the tab list element has a label provided by aria label."

    [02:45 - 03:00] In this particular example, there intentionally is an visible label since this is a simple demo, and we've previously clarified that the tab's component is presenting quotes from renowned jazz musicians. Therefore, we'll assign an aria label to the div holding the tab list role.

    [03:01 - 03:15] The aria label attribute will allow you to define a value that labels an interactive element, which in this case is the tab interface. We define that label in the app.jsx file, which just says, "Jazz musician quotes."

    [03:16 - 03:39] All right, so going back to the tab specs, I'm looking at the next bullet point, we'll see that it reads, "Each element with role tab has a property aria controls, referring to its associated tab panel element." When utilizing an interactive component like a tab to modify other elements, you can use the aria controls attribute to link the controlling element to its corresponding elements that it affects.

    [03:40 - 03:57] To effectively tackle the step, we must assign an id to the div elements with the tab panel role. In sync with this, we must introduce an aria controls attribute to our tab element, and the value assigned to this attribute should match the value of the id applied to the corresponding tab panel elements.

    [03:58 - 04:04] All right, back to the tab specs. As you notice, there's a pattern, we're going back and forth as we implement.

    [04:05 - 04:29] The next bullet point that we see there is the active tab element has a state a ria selected set to true, and all other tab elements have it set to false. Up until this point, you've probably observed that all tab contents are visible on the screen, and to display only the currently selected tab's content, we must begin tracking the active tab, implementing this specification will involve multiple steps, so we're going to walk through each one.

    [04:30 - 04:45] And the first step is finding the active tab index. Since we're creating a controlled react component, meaning the logic is driven by props rather than its own local state, since the parent component specifies its behavior, we will have to rely on the value prop to determine which tab is active.

    [04:46 - 05:07] In order to determine this, we will define a variable at the top of the component that iterates through the array of tabs and gives us the index of the currently active tab. The next step is determining the selected tab, and we need to add an on-click handler to the button element so that clicking on the tab element properly updates the value prop.

    [05:08 - 05:23] Then, we'll take the active tab index variable from the previous step and use it to determine the appropriate value to pass to the array of selected attribute. Third step in this process is controlling how to show and hide the content.

    [05:24 - 05:40] So with the active tab now being monitored through the active tab index variable, we can leverage it to decide when to display or hide the relevant content. And there are various ways we can achieve this, such as selectively adding a hidden class to the div containing tab contents.

    [05:41 - 05:53] However, for today, we're going to opt for a different approach. Using a distinct data attribute named data-active-content, and this choice ensures clear visibility in the DOM regarding the ongoing action.

    [05:54 - 06:35] [silence] So inside the tab that CSS style sheet, you actually come across a CSS selector that detects when data-active content is equal to false, and then utilizes the display non-property to eliminate the content of inactive tabs from the DOM. So that's kind of how that whole show-high process is working.

    [06:36 - 07:02] So let's go back to the specs now and go to the next bullet point, which says each element with role tab panel has the property ARIA labeled by referring to its associated tab element. So each tab element has an ID attribute, and we're going to take that ID and pass it to the ARIA labeled by attribute that gets applied to each tab panel element.

    [07:03 - 07:26] Okay, once more now to the specs, we can skip the second to last bullet point, because we don't have a pop-up menu within this particular tab's implementation . So just going to the last bullet point here, we see that it reads, "If the tab list element is vertically oriented, it has the property ARIA orientation set to vertical.

    [07:27 - 07:33] The default value of our orientation for a tab list element is horizontal." But even those defaults, I want us to explicitly set it to horizontal.

    [07:34 - 07:53] And our tab's component is only intended to be displayed horizontally, so let's just go ahead and apply the ARIA orientation equals horizontal to the div with the tab list role applied to it. And just as the refresher, the ARIA orientation attribute is used to indicate to assistive technologies whether an element's orientation is horizontal, vertical, or undefined.

    [07:54 - 08:06] Understanding the orientation is key for users to navigate specific widgets effectively, as it influences the anticipated actions of directional moves. Alrighty, so we've made a ton of progress so far, but our work is not done yet.

    [08:07 - 08:17] At this point in our implementation, the component only works as expected when you're using a mouse to interact with the tabs. In the upcoming lesson, we'll complete our implementation by introducing keyboard interactions.

    [08:18 - 08:19] (inhales deeply)