Tablist

Demo

Tab 1 Content

Example text for Tab 1...

				
					
<div class="tablist">
  <div role="tablist" aria-label="Sample Tablist" class="tab-headers">
    <button id="tab1" role="tab" aria-controls="panel1" aria-selected="true" tabindex="0">Tab 1</button>
    <button id="tab2" role="tab" aria-controls="panel2" tabindex="0">Tab 2</button>
    <button id="tab3" role="tab" aria-controls="panel3" tabindex="0">Tab 3</button>
    <button id="tab4" role="tab" aria-controls="panel4" tabindex="0">Tab 4</button>
  </div>
  <div class="tab-panels">
    <div id="panel1" role="tabpanel" aria-labelledby="tab1">
      <!-- Content for Tab 1 -->
      <h2>Tab 1 Content</h2>
      <p>Example text for Tab 1...</p>
      <button>Button 1</button>
      <!-- Additional content for Tab 1 -->
    </div>
    <div aria-hidden="true" id="panel2" role="tabpanel" aria-labelledby="tab2" hidden>
      <!-- Content for Tab 2 -->
      <h2>Tab 2 Content</h2>
      <p>Example text for Tab 2...</p>
      <button>Button 2</button>
      <!-- Additional content for Tab 2 -->
    </div>
    <div aria-hidden="true" id="panel3" role="tabpanel" aria-labelledby="tab3" hidden>
      <!-- Content for Tab 3 -->
      <h2>Tab 3 Content</h2>
      <p>Example text for Tab 3...</p>
      <button>Button 3</button>
      <!-- Additional content for Tab 3 -->
    </div>
    <div aria-hidden="true" id="panel4" role="tabpanel" aria-labelledby="tab4" hidden>
      <!-- Content for Tab 4 -->
      <h2>Tab 4 Content</h2>
      <p>Example text for Tab 4...</p>
      <button>Button 4</button>
      <!-- Additional content for Tab 4 -->
    </div>
  </div>
</div>
				
			
				
					
@import url("https://fonts.googleapis.com/css2?family=Barlow&family=Oswald&display=swap");

/* Tablist container */
.tablist {
  display: flex;
  flex-direction: column;
  width: 90%; /* Adjust as needed */
  max-width: 1200px; /* Optional: Set a maximum width for larger screens */
  margin: 0 auto;
}

/* Tab headers styling */
.tab-headers {
  display: flex;
  justify-content: space-between;
  width: 100%; /* Ensure the tab headers span the full width */
}

button[role="tab"] {
  font-family: "Oswald", sans-serif;
  font-size: 1.2em;
  flex: 1;
  background-color: #005e86; /* Your main brand color */
  color: #fff;
  border: none;
  padding: 15px 20px;
  cursor: pointer;
  box-sizing: border-box;
  margin-left: 3px;
  position: relative;
  transition: background-color 0.3s ease-in-out;
}

/* Differentiate the selected tab */
button[aria-selected="true"] {
  background-color: #99b4c0; /* Your secondary brand color */
  color: #211224;
}

/* Tab panels styling */
[role="tabpanel"] {
  width: 100%;
  padding: 20px; /* Add padding as needed */
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-family: "Barlow", sans-serif;
  border: 2px #211224 solid;
  margin-top: 5px;
}

/* Hide the hidden tab panels */
[role="tabpanel"][aria-hidden="true"] {
  display: none;
  visibility: hidden;
  height: 0;
  overflow: hidden;
}
/* Style buttons inside tab panels */
[role="tabpanel"] button {
  font-family: "Oswald", sans-serif;
  background-color: #005e86; /* Primary color */
  color: #fff;
  border: none;
  padding: 10px 15px;
  cursor: pointer;
  border-radius: 5px;
  transition: background-color 0.3s ease-in-out, color 0.3s ease-in-out;
  margin: 5px;
}

/* Hover effect for buttons inside tab panels */
[role="tabpanel"] button:hover {
  background-color: #cce1e7; /* Secondary color on hover */
  color: #005e86; /* Primary color text on hover */
}
				
			
				
					
// Wait for the DOM content to be fully loaded before executing the code
document.addEventListener("DOMContentLoaded", function () {
  // Select all elements with role="tab" and role="tabpanel"
  const tabs = document.querySelectorAll('[role="tab"]');
  const panels = document.querySelectorAll('[role="tabpanel"]');

  // Show the first tab and panel by default
  tabs[0].setAttribute("aria-selected", "true"); // Activate the first tab
  tabs[0].setAttribute("tabindex", "0"); // Set tabindex for focus
  panels[0].removeAttribute("hidden"); // Show the first panel

  // Add event listeners to each tab for click and keyboard interactions
  tabs.forEach((tab) => {
    tab.addEventListener("click", () => {
      activateTab(tab); // Call the activateTab function on click
    });

    tab.addEventListener("keydown", (e) => {
      if (e.key === "Enter" || e.key === " ") {
        e.preventDefault();
        activateTab(tab); // Call the activateTab function on Enter or Space key press
      }
    });
  });

  // Function to activate a tab and associated panel
  function activateTab(currentTab) {
    const tabID = currentTab.getAttribute("id");

    // Deactivate all tabs and hide all panels
    tabs.forEach((t) => {
      t.setAttribute("aria-selected", "false");
    });

    panels.forEach((panel) => {
      panel.setAttribute("aria-hidden", "true");
    });

    // Activate the selected tab and show its associated panel
    currentTab.setAttribute("aria-selected", "true");
    currentTab.setAttribute("tabindex", "0");

    const panel = document.getElementById(
      currentTab.getAttribute("aria-controls")
    );
    panel.setAttribute("aria-hidden", "false");

    currentTab.focus(); // Set focus to the activated tab
  }
});

// Another event listener for when the DOM content is loaded
document.addEventListener("DOMContentLoaded", function () {
  const tabs = document.querySelectorAll('[role="tab"]');

  // Add click event listeners to each tab for animation (not recommended)
  tabs.forEach((tab) => {
    tab.addEventListener("onclick", () => {
      const currentTab = document.querySelector('[aria-selected="true"]');
      const newTab = tab;

      // Create a div for the animation
      const animation = document.createElement("div");
      animation.classList.add("tab-animation");
      animation.style.backgroundColor = getComputedStyle(
        currentTab
      ).backgroundColor;

      currentTab.appendChild(animation); // Append animation to the current tab

      const rect = newTab.getBoundingClientRect();
      const offset = rect.left - currentTab.getBoundingClientRect().left;

      animation.style.transform = `translateX(${offset}px)`; // Apply animation

      // Perform animation transitions and tab switching
      setTimeout(() => {
        currentTab.removeChild(animation);
        newTab.appendChild(animation);
        animation.style.transform = `translateX(0)`;

        setTimeout(() => {
          newTab.removeChild(animation);
          newTab.setAttribute("aria-selected", "true"); // Activate new tab
          newTab.focus(); // Set focus to the new tab
          currentTab.removeAttribute("aria-selected"); // Deactivate current tab
        }, 300);
      }, 300);
    });
  });
});
				
			

Why use a tablist component?

Tablist components in web development serve as efficient navigational elements that organize content into discrete sections, allowing users to switch between different panels or sections within a single space. They offer a structured layout that presents information in a clear and organized manner, enhancing user experience by reducing clutter and enabling users to focus on specific content sections. Accessibility considerations play a crucial role in the use of tablist components. For users reliant on assistive technologies or keyboard navigation, ensuring the proper implementation of tablist components is vital. Accessibility concerns include keyboard operability, providing clear focus indicators, and ensuring that users using screen readers understand the relationship between tabs and associated content. By following accessibility best practices, such as utilizing semantic markup, implementing ARIA attributes to convey structure, and maintaining keyboard navigability, tablist components can provide an inclusive and intuitive way for users of all abilities to navigate and access content effectively within a web interface.

What are some accessibility concerns?

  1. Keyboard Accessibility:
    • Concern: Users relying on keyboards might have difficulties navigating and selecting tabs within the tablist.
    • Mitigation: Ensure keyboard operability by allowing users to navigate and select tabs using keyboard controls (e.g., arrow keys, Enter key). Apply proper focus management and use ARIA attributes (role=”tab”, aria-selected) to indicate the selected tab and manage focus transitions.
  2. Screen Reader Compatibility and Tab Association:
    • Concern: Screen reader users may struggle to understand the relationship between tabs and their associated content.
    • Mitigation: Use proper ARIA attributes (aria-controls, aria-labelledby) to associate tabs with their respective content panels, allowing screen readers to convey the connection. Announce changes in tab states and content updates using ARIA live regions to provide clear notifications to screen reader users.
  3. Focus Indication and Visual Clarity:
    • Concern: Insufficient focus indicators or unclear visual cues may confuse users with visual impairments or low vision.
    • Mitigation: Ensure clear and visible focus indicators, such as distinct outlines or changes in color, to indicate the focused tab. Maintain sufficient contrast between the selected tab and its background to ensure visibility.
  4. Semantic Markup and ARIA Roles:
    • Concern: Improper use of markup or ARIA roles may lead to inconsistencies or confusion for assistive technologies.
    • Mitigation: Use semantic HTML elements (<ul>, <li>) to structure the tablist and associated content. Apply ARIA roles and attributes as necessary (role=”tablist”, role=”tab”, role=”tabpanel”) to convey the component’s structure and relationships to assistive technologies.
  5. Responsive Design and Interaction on Mobile Devices:
    • Concern: Tablist components may not function optimally on smaller screens or touch devices.
    • Mitigation: Ensure responsive design principles are applied, allowing the tablist to adapt and remain usable across various devices and screen sizes. Optimize tab interaction for touch-based devices, providing larger touch areas and suitable touch interactions.
Tested using
Chrome
with NVDA
Firefox
with NVDA
  • W3C SVG
    1.3.1
    Info and Relationships ( level A ) :
    Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text.
  • W3C SVG
    2.1.1
    Keyboard ( level A ) :
    All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints.
  • W3C SVG
    2.4.3
    Focus Order ( level A ) :
    If a Web page can be navigated sequentially and the navigation sequences affect meaning or operation, focusable components receive focus in an order that preserves meaning and operability.
  • W3C SVG
    2.4.7
    Focus Visible ( level AA ) :
    Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible.
  • W3C SVG
    4.1.1
    Parsing ( level A ) :
    In content implemented using markup languages, elements have complete start and end tags, elements are nested according to their specifications, elements do not contain duplicate attributes, and any IDs are unique, except where the specifications allow these features.