<div class="wrapper">
   <img src="https://uploads-ssl.webflow.com/649c7f9a7739b1da3f52cb29/650ca0f162b5c62b83930c15_Zenyth Assessment_Logo-01.svg" width="200px" alt="Zenyth logo">
    <h1>Accessible Tabpanel Pattern</h1>
  
  <div class="tabs">
  <div role="tablist" aria-label="Testing Tools">
    <button role="tab"
            aria-selected="true"
            aria-controls="arc-tab"
            id="arc">
      ARC Toolkit
    </button>
    <button role="tab"
            aria-selected="false"
            aria-controls="andi-tab"
            id="andi"
            tabindex="-1">
      ANDI
    </button>
    <button role="tab"
            aria-selected="false"
            aria-controls="axe-tab"
            id="axe"
            tabindex="-1"
            data-deletable="">
      axe DevTools
    </button>
  </div>
  <div tabindex="0"
       role="tabpanel"
       id="arc-tab"
       aria-labelledby="arc">
    <p>
      The ARC Toolkit is a set of accessibility tools which aids developers in identifying accessibility problems and features for WCAG 2.0, WCAG 2.1, EN 301 549, and Section 508.
    </p>
  </div>
  <div tabindex="0"
       role="tabpanel"
       id="andi-tab"
       aria-labelledby="andi"
       hidden="">
    <p>
      ANDI is a "favelet" or "bookmarklet" that will: Provide automated detection of accessibility issues, reveal what a screen reader should say for interactive elements and. give practical suggestions to improve accessibility.
    </p>
  </div>
  <div tabindex="0"
       role="tabpanel"
       id="axe-tab"
       aria-labelledby="axe"
       hidden="">
    <p>
      The free axe DevTools browser extension is a fast, lightweight, yet powerful testing tool driven by the world’s most trusted accessibility testing engine, axe-core, developed by Deque.
    </p>
  </div>
</div>  
    <p>For more information you can reach a friendly accessibility expert at <a href="https://zenythgroup.com/contact">Zenyth</a></p>
</div>
/* Default Codepen Styles */
* {
  font-family: ui-serif, Assistant;
}
.wrapper {
  margin: 0 auto 2.5em;
  max-width: 650px;
}
h1 {
  padding-bottom: 25px;
}
#info {
  margin-top: 60px;
}
*:focus {
  outline-color: #53389e;
  outline-style: auto!important;
  outline-offset: 2px;
}
.flex {
  display: flex;
}
.flex-row {
  flex-direction: row;
}
.flex-col {
  flex-direction: column;
}
.flex-wrap {
  flex-wrap: wrap;
}
.m-auto {
  margin: auto;
}
.justify-evenly {
  justify-content: space-evenly;
}

/* Component styles */
.tabs {
  width: 100%;
  font-family: "lucida grande", sans-serif;
}

[role="tablist"] {
  margin: 0 0 -0.1em;
  overflow: visible;
}

[role="tab"] {
  position: relative;
  margin: 0;
  padding: 10px 16px;
  border: 1px solid hsl(219, 1%, 72%);
  border-radius: 0.2em 0.2em 0 0;
  box-shadow: 0 0 0.2em hsl(219, 1%, 72%);
  overflow: visible;
  font-family: inherit;
  font-size: inherit;
  background: hsl(220, 20%, 94%);
  cursor: pointer;
  font-weight: bold;
}

[role="tab"]:hover::before,
[role="tab"]:focus::before,
[role="tab"][aria-selected="true"]::before {
  position: absolute;
  bottom: 100%;
  right: -1px;
  left: -1px;
  border-radius: 0.2em 0.2em 0 0;
  border-top: 3px solid #53389e;
  content: '';
}

[role="tab"][aria-selected="true"] {
  border-radius: 0;
  background: hsl(220, 43%, 99%);
  outline: 0;
}

[role="tab"][aria-selected="true"]:not(:focus):not(:hover)::before {
  border-top: 5px solid #53389e;
}

[role="tab"][aria-selected="true"]::after {
  position: absolute;
  z-index: 3;
  bottom: -1px;
  right: 0;
  left: 0;
  height: 0.3em;
  background: hsl(220, 43%, 99%);
  box-shadow: none;
  content: '';
}

[role="tab"]:hover,
[role="tab"]:focus,
[role="tab"]:active {
  outline: 0;
  border-radius: 0;
  color: inherit;
}

[role="tab"]:hover::before,
[role="tab"]:focus::before {
  border-color: #7f56d9;
}

[role="tabpanel"] {
  position: relative;
  z-index: 2;
  padding: 24px 16px;
  border: 1px solid hsl(219, 1%, 72%);
  border-radius: 0 0.2em 0.2em 0.2em;
  box-shadow: 0 0 0.2em hsl(219, 1%, 72%);
  background: hsl(220, 43%, 99%);
}

[role="tabpanel"] p {
  margin: 0;
}

[role="tabpanel"] * + p {
  margin-top: 1em;
}

(function () {
  // Select the first [role="tablist"] element
  const tablist = document.querySelectorAll('[role="tablist"]')[0];
  let tabs;
  let panels;

  // Generates arrays for tabs and panels
  generateArrays();

  function generateArrays() {
    tabs = document.querySelectorAll('[role="tab"]');
    panels = document.querySelectorAll('[role="tabpanel"]');
  };

  // Key codes for easy reference
  const keys = {
    end: 35,
    home: 36,
    left: 37,
    up: 38,
    right: 39,
    down: 40,
    delete: 46,
    enter: 13,
    space: 32
  };

  // Direction object for navigation
  const direction = {
    37: -1,
    38: -1,
    39: 1,
    40: 1
  };

  // Bind event listeners to each tab
  for (let i = 0; i < tabs.length; ++i) {
    addListeners(i);
  };

  function addListeners(index) {
    tabs[index].addEventListener('click', clickEventListener);
    tabs[index].addEventListener('keydown', keydownEventListener);
    tabs[index].addEventListener('keyup', keyupEventListener);

    // Assign index to each tab for reference
    tabs[index].index = index;
  };

  function clickEventListener(event) {
    const tab = event.target;
    activateTab(tab, false);
  };

  function keydownEventListener(event) {
    const key = event.keyCode;

    switch (key) {
      case keys.end:
        event.preventDefault();
        focusLastTab();
        break;
      case keys.home:
        event.preventDefault();
        focusFirstTab();
        break;
      case keys.up:
      case keys.down:
        event.preventDefault();
        determineOrientation(event);
        break;
    };
  };

  function keyupEventListener(event) {
    const key = event.keyCode;

    switch (key) {
      case keys.left:
      case keys.right:
        determineOrientation(event);
        break;
      case keys.delete:
        determineDeletable(event);
        break;
      case keys.enter:
      case keys.space:
        activateTab(event.target);
        break;
    };
  };

  function determineOrientation(event) {
    const key = event.keyCode;
    const vertical = tablist.getAttribute('aria-orientation') === 'vertical';
    let proceed = false;

    if (vertical) {
      if (key === keys.up || key === keys.down) {
        proceed = true;
      }
    } else {
      if (key === keys.left || key === keys.right) {
        proceed = true;
      }
    }

    if (proceed) {
      switchTabOnArrowPress(event);
    }
  };

  function switchTabOnArrowPress(event) {
    const pressed = event.keyCode;

    if (direction[pressed]) {
      const target = event.target;
      if (target.index !== undefined) {
        if (tabs[target.index + direction[pressed]]) {
          tabs[target.index + direction[pressed]].focus();
        } else if (pressed === keys.left || pressed === keys.up) {
          focusLastTab();
        } else if (pressed === keys.right || pressed === keys.down) {
          focusFirstTab();
        }
      }
    }
  };

  function activateTab(tab, setFocus = true) {
    // Deactivate all tabs
    deactivateTabs();

    tab.removeAttribute('tabindex');
    tab.setAttribute('aria-selected', 'true');

    const controls = tab.getAttribute('aria-controls');
    document.getElementById(controls).removeAttribute('hidden');

    if (setFocus) {
      tab.focus();
    }
  };

  function deactivateTabs() {
    for (let t = 0; t < tabs.length; t++) {
      tabs[t].setAttribute('tabindex', '-1');
      tabs[t].setAttribute('aria-selected', 'false');
    }

    for (let p = 0; p < panels.length; p++) {
      panels[p].setAttribute('hidden', 'hidden');
    }
  };

  function focusFirstTab() {
    tabs[0].focus();
  };

  function focusLastTab() {
    tabs[tabs.length - 1].focus();
  };

  function determineDeletable(event) {
    let target = event.target;

    if (target.getAttribute('data-deletable') !== null) {
      deleteTab(event);

      generateArrays();

      if (target.index - 1 < 0) {
        activateTab(tabs[0]);
      } else {
        activateTab(tabs[target.index - 1]);
      }
    }
  };

  function deleteTab(event) {
    const target = event.target;
    const panel = document.getElementById(target.getAttribute('aria-controls'));

    target.parentElement.removeChild(target);
    panel.parentElement.removeChild(panel);
  };

  function determineDelay() {
    const hasDelay = tablist.hasAttribute('data-delay');
    let delay = 0;

    if (hasDelay) {
      const delayValue = tablist.getAttribute('data-delay');
      delay = delayValue ? parseInt(delayValue, 10) : 300;
    }

    return delay;
  };
}());

External CSS

  1. https://fonts.googleapis.com/css?family=Assistant

External JavaScript

This Pen doesn't use any external JavaScript resources.