<div class="bux-tabpanel">
    <ul class="bux-tabpanel__tabs" role="tablist" aria-label="An accessible tabpanel example" tabindex="-1">
        <li class="bux-tabpanel__button" role="presentation">
            <button id="bux-tabpanel__tab--1" class="bux-tabpanel__tab" role="tab" aria-selected="true" aria-controls="bux-tabpanel__panel--1">
                Tab one
            </button>
        </li>
        <li class="bux-tabpanel__button" role="presentation">
            <button id="bux-tabpanel__tab--2" class="bux-tabpanel__tab" role="tab" aria-selected="" aria-controls="bux-tabpanel__panel--2" tabindex="-1">
                Tab two
            </button>
        </li>
        <li class="bux-tabpanel__button" role="presentation">
            <button id="bux-tabpanel__tab--3" class="bux-tabpanel__tab" role="tab" aria-selected="" aria-controls="bux-tabpanel__panel--3" tabindex="-1">
                Tab three has longer text
            </button>
        </li>
        <li class="bux-tabpanel__button" role="presentation">
            <button id="bux-tabpanel__tab--4" class="bux-tabpanel__tab" role="tab" aria-selected="" aria-controls="bux-tabpanel__panel--4" tabindex="-1">
                Tab four
            </button>
        </li>
    </ul>
    <div id="bux-tabpanel__panel--1" class="bux-tabpanel__panel" role="tabpanel" aria-labelledby="bux-tabpanel__tab--1" tabindex="0">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
    </div>
    <div id="bux-tabpanel__panel--2" class="bux-tabpanel__panel" role="tabpanel" aria-labelledby="bux-tabpanel__tab--2" tabindex="0" hidden="hidden">
        Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?
    </div>
    <div id="bux-tabpanel__panel--3" class="bux-tabpanel__panel" role="tabpanel" aria-labelledby="bux-tabpanel__tab--3" tabindex="0" hidden="hidden">
        At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus.
    </div>
    <div id="bux-tabpanel__panel--4" class="bux-tabpanel__panel" role="tabpanel" aria-labelledby="bux-tabpanel__tab--4" tabindex="0" hidden="hidden">
        Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.
    </div>

</div>
{# 

Tabpanel

Available variables:

- title:        Aria-label for the tabpanel component.
- items:        Array of tabpanel items.
- item.id:      Unique ID for the panel item.
- item.title:   Title for the panel item.
- item.content: Content for the panel item.

#}

{% set panels = '' %}
<div class="bux-tabpanel">
	<ul class="bux-tabpanel__tabs" role="tablist" aria-label="{{ title }}" tabindex="-1">
		{% for item in items %}
			{% set isTabSelected = (loop.first) ? true : false %}
			{% set tabTabIndex = (isTabSelected) ? '' : 'tabindex="-1"' %}
			{% set tabId = 'bux-tabpanel__tab--' ~ item.id %}
			{% set panel = '' %}
			{% set panelId = 'bux-tabpanel__panel--' ~ item.id %}
			{% set panelVisibility = (isTabSelected) ? '' : 'hidden="hidden"' %}
			<li class="bux-tabpanel__button" role="presentation">
<button id="{{ tabId }}" class="bux-tabpanel__tab" role="tab" aria-selected="{% if isTabSelected %}true{% endif %}" aria-controls="{{ panelId }}" {{ tabTabIndex }}>
					{{ item.title }}
				</button>
			</li>
			{% set panel %}
			<div id="{{ panelId }}" class="bux-tabpanel__panel" role="tabpanel" aria-labelledby="{{ tabId }}" tabindex="0" {{panelVisibility}}>
				{{ item.content }}
			</div>
			{% endset %}
			{% set panels = panels ~ panel %}
		{% endfor %}
	</ul>
	{{ panels }}
</div>
site_name_prefix: Office of
site_name: Learning Relations Excellence
site_slogan: Additional text or site slogan
address_1: 100 Building Name
address_2: 1 Oval Mall
city: Columbus
state: OH
zip: '43210'
contact_email: email@osu.edu
contact_phone: 614-292-OHIO
contact_tty: 614-688-8605
title: An accessible tabpanel example
items:
  '1':
    id: 1
    title: Tab one
    content: >-
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
      tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
      veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
      commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
      velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
      cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
      est laborum.
  '2':
    id: 2
    title: Tab two
    content: >-
      Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium
      doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo
      inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
      Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut
      fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem
      sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit
      amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora
      incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad
      minima veniam, quis nostrum exercitationem ullam corporis suscipit
      laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum
      iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae
      consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?
  '3':
    id: 3
    title: Tab three has longer text
    content: >-
      At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis
      praesentium voluptatum deleniti atque corrupti quos dolores et quas
      molestias excepturi sint occaecati cupiditate non provident, similique
      sunt in culpa qui officia deserunt mollitia animi, id est laborum et
      dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio.
      Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil
      impedit quo minus id quod maxime placeat facere possimus, omnis voluptas
      assumenda est, omnis dolor repellendus.
  '4':
    id: 4
    title: Tab four
    content: >-
      Et harum quidem rerum facilis est et expedita distinctio. Nam libero
      tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo
      minus id quod maxime placeat facere possimus, omnis voluptas assumenda
      est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis
      debitis aut rerum necessitatibus saepe eveniet ut et voluptates
      repudiandae sint et molestiae non recusandae. Itaque earum rerum hic
      tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias
      consequatur aut perferendis doloribus asperiores repellat.
  • Content:
    .bux-tabpanel {
      &__tabs {
        display: -webkit-box;
        display: -ms-flexbox;
        display: flex;
        flex-direction: row;
        max-width: 100%;
        outline: 0;
        overflow: auto hidden;
        scroll-behavior: smooth;
        scrollbar-width: none;
        width: auto;
        will-change: scroll-position;
        -webkit-box-orient: horizontal;
        -webkit-box-direction: normal;
    
        &::-webkit-scrollbar {
          display: none;
        }
    
        @media (max-width: $bp-md) {
          flex-direction: column;
          width: 100%;
        }
      }
    
      &__tabs--scrollable {
        display: flex;
        height: auto;
        width: 100%;
      }
    
      &__tab {
        @include button-reset();
        background-color: $white;
        border: 2px solid $white;
        color: $gray-dark-80;
        cursor: pointer;
        padding: 12px $sp-16;
        margin-right: 1px;
        font-family: $sans;
        font-size: $ts-base;
        font-weight: 400;
        line-height: 1.25;
        position: relative;
    
        @media (max-width: $bp-md) {
          width: 100%;
          text-align: left;
        }
    
        &::after {
          background: $gray-light-80;
          bottom: 0;
          content: "";
          height: 4px;
          left: 0;
          position: absolute;
          width: 100%;
        }
    
        &[aria-selected="true"] {
          border: 2px solid $gray-light-80;
          outline: none;
    
          @media (max-width: $bp-md) {
            border: 2px solid transparent;
          }
    
          &::after {
            background: $scarlet;
          }
        }
        &:focus {
          border: 2px solid $gray-light-80;
          outline: 2px solid $focus;
          outline-offset: -2px;
    
          &::after {
            background: $scarlet;
          }
        }
    
        @media (hover: hover) and (pointer: fine) {
          &:hover {
            background-color: $gray-light-90;
            border-color: $gray-light-90;
            color: $gray-dark-80;
    
            &::after {
              background: $gray-dark-60;
            }
          }
        }
      }
    
      &__panel {
        padding: $sp-32;
        border-bottom: 2px solid $gray-light-80;
        font-family: $serif;
        font-size: $ts-base;
        font-weight: 400;
        margin-top: -4px;
    
        &:focus {
          outline: 2px solid $focus;
          outline-offset: -2px;
          border-color: $gray-light-40;
        }
      }
    }
    
  • URL: /components/raw/tabpanel/_tabpanel.scss
  • Filesystem Path: src/components/tabpanel/_tabpanel.scss
  • Size: 2.1 KB
  • Content:
    {
      const tabpanelClass = "bux-tabpanel";
    
      if (document.querySelector(`.${tabpanelClass}`)) {
        let tabpanels = document.querySelectorAll(`.${tabpanelClass}`);
        tabpanels = Array.prototype.slice.call(tabpanels);
    
        tabpanels.forEach((tabpanel) => {
          let tabs = tabpanel.querySelectorAll(`.${tabpanelClass}__tab`);
          tabs = Array.prototype.slice.call(tabs);
    
          let panels = tabpanel.querySelectorAll(`.${tabpanelClass}__panel`);
          panels = Array.prototype.slice.call(panels);
    
          tabs.forEach((tab) => {
            tab.addEventListener("keydown", handleKeys);
    
            tab.addEventListener("click", (event) => {
              const { currentTarget: currentTab } = event;
              const otherTabs = tabs.filter((element) => element !== currentTab);
    
              currentTab.removeAttribute("tabindex");
              currentTab.setAttribute("aria-selected", true);
              otherTabs.forEach((otherTab) => {
                otherTab.setAttribute("tabindex", "-1");
                otherTab.setAttribute("aria-selected", false);
              });
    
              const currentPanelId = currentTab.getAttribute("aria-controls");
              const currentPanel = document.querySelector(`#${currentPanelId}`);
              const otherPanels = panels.filter((element) => element !== currentPanel);
    
              currentPanel.removeAttribute("hidden");
              currentPanel.setAttribute("tabindex", "0");
              otherPanels.forEach((otherPanel) => {
                otherPanel.setAttribute("hidden", "hidden");
                otherPanel.setAttribute("tabindex", "-1");
              });
            });
          });
        });
    
        function handleKeys(event) {
          const { key: currentKey, currentTarget: currentTab } = event;
    
          const {
            parentNode: {
              parentNode: { children: tabs },
              previousElementSibling: previousElementChild,
              nextElementSibling: nextElementChild,
            },
          } = currentTab;
    
          const { firstElementChild: previousElement } = previousElementChild || {};
    
          const { firstElementChild: nextElement } = nextElementChild || {};
    
          const firstTab = tabs[0].querySelector("button");
          const lastTab = tabs[tabs.length - 1].querySelector("button");
    
          switch (currentKey) {
            case keys.left:
              if (!isTab(previousElement)) {
                activateTab(lastTab);
              } else {
                activateTab(previousElement);
              }
              event.preventDefault();
              break;
            case keys.right:
              if (!isTab(nextElement)) {
                activateTab(firstTab);
              } else {
                activateTab(nextElement);
              }
              event.preventDefault();
              break;
            case keys.home:
              activateTab(firstTab);
              event.preventDefault();
              break;
            case keys.end:
              activateTab(lastTab);
              event.preventDefault();
              break;
          }
        }
    
        function isTab(element) {
          return element && element.classList.contains(`${tabpanelClass}__tab`);
        }
    
        function activateTab(element) {
          element.focus();
          element.click();
        }
      }
    }
    
  • URL: /components/raw/tabpanel/tabpanel.js
  • Filesystem Path: src/components/tabpanel/tabpanel.js
  • Size: 3.1 KB

Tabpanel component

This component is based on the guidelines laid out in the W3’s example, Example of Tabs with Automatic Activation.

Keyboard support

Key Fuction
Tab
  • When focus moves into the tab list, places focus on the active tab element.
  • When the tab list contains the focus, moves focus to the next element in the tab sequence, which is the tabpanel element.
Right Arrow
  • Moves focus to the next tab.
  • If focus is on the last tab, moves focus to the first tab.
  • Activates the newly focused tab.
Left Arrow
  • Moves focus to the previous tab.
  • If focus is on the first tab, moves focus to the last tab.
  • Activates the newly focused tab.
Home Moves focus to the first tab and activates it.
End Moves focus to the last tab and activates it.

Data attributes

Delay tablist keypress

data-delay — By adding a delay to the tabpanel each key stroke within the tablist will be delayed 300ms.

data-delay usage

<div class="bux-tabpanel" data-delay>
  ...
</div>