Tabs

<div class="tabs">
    <ul class="tabs__list">
        <li class="tabs__label">
            <a class="tabs__item" href="#section1">publications</a>
        </li>
        <li class="tabs__label">
            <a class="tabs__item" href="#section2">videos</a>
        </li>
        <li class="tabs__label">
            <a class="tabs__item" href="#section3">infographics</a>
        </li>
    </ul>
    <div class="tabs__bar"></div>

    <section id="section1" class="tabs__panel">
        <h3 class="h3">publications</h3>
        <p class="text">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam euismod, tortor nec pharetra ultricies, ante erat imperdiet velit, nec laoreet enim lacus a velit. <a href="#">Nam luctus</a>, enim in interdum condimentum, nisl diam iaculis lorem,
            vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan.</p>
    </section>

    <section id="section2" class="tabs__panel">
        <h3 class="h3">videos</h3>
        <p class="text">Nullam at diam nec arcu suscipit auctor non a erat. Sed et magna semper, eleifend magna non, facilisis nisl. Proin et est et lorem dictum finibus ut nec turpis. Aenean nisi tortor, euismod a mauris a, mattis scelerisque tortor. Sed dolor risus,
            varius a nibh id, condimentum lacinia est. In lacinia cursus odio a aliquam. Curabitur tortor magna, laoreet ut rhoncus at, sodales consequat tellus.</p>
    </section>

    <section id="section3" class="tabs__panel">
        <h3 class="h3">infographics</h3>
        <p class="text">Phasellus ac tristique orci. Nulla maximus <a href="">justo nec dignissim consequat</a>. Sed vehicula diam sit amet mi efficitur vehicula in in nisl. Aliquam erat volutpat. Suspendisse lorem turpis, accumsan consequat consectetur gravida, <a href="#">pellentesque ac ante</a>.
            Aliquam in commodo ligula, sit amet mollis neque. Vestibulum at facilisis massa.</p>
    </section>

</div>
<div class="tabs">
  <ul class="tabs__list">
    <li class="tabs__label">
      <a class="tabs__item" href="#section1">publications</a>
    </li>
    <li class="tabs__label">
      <a class="tabs__item" href="#section2">videos</a>
    </li>
    <li class="tabs__label">
       <a class="tabs__item" href="#section3">infographics</a>
    </li>
  </ul>
  <div class="tabs__bar"></div>

  <section id="section1" class="tabs__panel">
    <h3 class="h3">publications</h3>
    <p class="text">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam euismod, tortor nec pharetra ultricies, ante erat imperdiet velit, nec laoreet enim lacus a velit. <a href="#">Nam luctus</a>, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan.</p>
  </section>
  
  <section id="section2" class="tabs__panel">
    <h3 class="h3">videos</h3>
    <p class="text">Nullam at diam nec arcu suscipit auctor non a erat. Sed et magna semper, eleifend magna non, facilisis nisl. Proin et est et lorem dictum finibus ut nec turpis. Aenean nisi tortor, euismod a mauris a, mattis scelerisque tortor. Sed dolor risus, varius a nibh id, condimentum lacinia est. In lacinia cursus odio a aliquam. Curabitur tortor magna, laoreet ut rhoncus at, sodales consequat tellus.</p>
  </section>
  
  <section id="section3" class="tabs__panel">
    <h3 class="h3">infographics</h3>
    <p class="text">Phasellus ac tristique orci. Nulla maximus <a href="">justo nec dignissim consequat</a>. Sed vehicula diam sit amet mi efficitur vehicula in in nisl. Aliquam erat volutpat. Suspendisse lorem turpis, accumsan consequat consectetur gravida, <a href="#">pellentesque ac ante</a>. Aliquam in commodo ligula, sit amet mollis neque. Vestibulum at facilisis massa.</p>
  </section>


</div>
/* No context defined for this component. */
  • Content:
    // Necessary for tabs and accordion.
    const hasClass = (element, identifier) => {
      const className = identifier.replace('.', '');
      const classes = (' ' + element.className + ' ').replace(/[\n\t]/g, ' ');
    
      return classes.indexOf(' ' + className + ' ') > -1;
    }
    
    // Get wrapper element. Necessary for tabs and accordion.
    const getWrapperElement = (element, identifier) => {
      let parent = element.parentElement;
      while (parent !== null) {
        if (hasClass(parent, identifier)) {
          return parent;
        } else {
          parent = parent.parentElement;
        }
      }
      return null;
    }
    
    // Moves bar bellow tab
    const moveBar = (tab, bar) => {
      const initialSectionSize = tab.offsetWidth;
      const tablist = getWrapperElement(tab, '.tabs__list');
      const initialPositionBar = tablist.querySelectorAll('.tabs__label')[0].offsetLeft; // First tab.
      const startSection = tab.offsetLeft - initialPositionBar;
      
      const getStyle = (width, left, start) => (
        `width: ${width}px; left: ${left}px; transition: all .5s ease-in-out; transform: translate(${start}px, 0);`
      );
    
      // Apply properties to bar
      bar.setAttribute('style', getStyle(initialSectionSize, initialPositionBar, startSection));
    }  
    
    // Selects tab according to URL hash and shows corresponding panel.
    const selectTab = (hash) => {
      let selectedTab = document.querySelector("[href='" + hash + "']");
      const tabbed = document.querySelector('.tabs');
      const tablist = tabbed.querySelector('.tabs__list');
      const tabs = tablist.querySelectorAll('.tabs__item');
      const panels = tabbed.querySelectorAll('.tabs__panel');
      const bar = tabbed.querySelector('.tabs__bar');
    
      if (selectedTab !== null) {
        const showTabIndex = Array.prototype.indexOf.call(tabs, selectedTab);
    
        tabs.forEach((element, index, array) => {
          if (index == showTabIndex) {
            panels[index].hidden = false;
            selectedTab.focus();
            selectedTab.removeAttribute('tabindex');
            selectedTab.setAttribute('aria-selected', 'true');
    
          } else {
            panels[index].hidden = true;
            element.removeAttribute('aria-selected');
            element.setAttribute('tabindex', '-1');
          }
        });
    
        moveBar(selectedTab, bar);
      }
    }
    
    // Show the correct tab when accessing the URL, if it has a fragment
    // identifier (the part beginning with and following the # symbol).
    // The code below should run as soon as the HTML page is loaded.
    //
    if (location.hash) {
      if (document.readyState === "complete") {
        selectTab(location.hash);
    
      } else if (document.readyState !== "loading" && !document.documentElement.doScroll) {
        selectTab(location.hash);
    
      } else {
        document.addEventListener("DOMContentLoaded", () => {
          selectTab(location.hash);
        });
      }
    }
    
    (function() {
      // Get relevant elements and collections
      const tabsgroup = document.querySelectorAll('.tabs');
    
      tabsgroup.forEach(tabbed => {
        const tablist = tabbed.querySelector('.tabs__list');
        const tabs = tablist.querySelectorAll('.tabs__item');
        const panels = tabbed.querySelectorAll('.tabs__panel');
        const bar = tabbed.querySelector('.tabs__bar');
    
        // The tab switching function
        const switchTab = (oldTab, newTab) => {
          newTab.focus();
          // Make the active tab focusable by the user (Tab key)
          newTab.removeAttribute('tabindex');
          // Set the selected state
          newTab.setAttribute('aria-selected', 'true');
          oldTab.removeAttribute('aria-selected');
          oldTab.setAttribute('tabindex', '-1');
          // Get the indices of the new and old tabs to find the correct
          // tab panels to show and hide
          let index = Array.prototype.indexOf.call(tabs, newTab);
          let oldIndex = Array.prototype.indexOf.call(tabs, oldTab);
          panels[oldIndex].hidden = true;
          panels[index].hidden = false;
    
          // Move bar to the right place.
          moveBar(newTab, bar);
        }
        
        // Add the tablist role to the first <ul> in the .tabbed container
        tablist.setAttribute('role', 'tablist');
      
        // Add semantics are remove user focusability for each tab
        Array.prototype.forEach.call(tabs, (tab, i) => {
          tab.setAttribute('role', 'tab');
          tab.setAttribute('id', 'tab' + (i + 1));
          tab.setAttribute('tabindex', '-1');
          tab.parentNode.setAttribute('role', 'presentation');
      
          // Handle clicking of tabs for mouse users
          tab.addEventListener('click', e => {
            e.preventDefault();
            let currentTab = tablist.querySelector('[aria-selected]');
            if (e.currentTarget !== currentTab) {
              switchTab(currentTab, e.currentTarget);
            }
          });
      
          // Handle keydown events for keyboard users
          tab.addEventListener('keydown', e => {
            // Get the index of the current tab in the tabs node list
            let index = Array.prototype.indexOf.call(tabs, e.currentTarget);
            // Work out which key the user is pressing and
            // Calculate the new tab's index where appropriate
            let dir = e.which === 37 ? index - 1 : e.which === 39 ? index + 1 : e.which === 40 ? 'down' : null;
            if (dir !== null) {
              e.preventDefault();
              // If the down key is pressed, move focus to the open panel,
              // otherwise switch to the adjacent tab
              dir === 'down' ? panels[i].focus() : tabs[dir] ? switchTab(e.currentTarget, tabs[dir]) : void 0;
            }
          });
        });
      
        // Add tab panel semantics and hide them all
        Array.prototype.forEach.call(panels, (panel, i) => {
          panel.setAttribute('role', 'tabpanel');
          panel.setAttribute('tabindex', '-1');
          let id = panel.getAttribute('id');
          panel.setAttribute('aria-labelledby', tabs[i].id);
          panel.hidden = true;
        });
      
        // Initially activate the first tab and reveal the first tab panel
        tabs[0].removeAttribute('tabindex');
        tabs[0].setAttribute('aria-selected', 'true');
        panels[0].hidden = false;
    
        // Move bar to the first tab.
        moveBar(tabs[0], bar);
      })
    
      // The tab switching function
      const switchTab2 = (oldTab, newTab) => {
        const tabbed = getWrapperElement(newTab, '.tabs');
        const tablist = tabbed.querySelector('.tabs__list');
        const tabs = tablist.querySelectorAll('.tabs__item');
        const panels = tabbed.querySelectorAll('.tabs__panel');
        const bar = tabbed.querySelector('.tabs__bar');
    
        // Get the indices of the new and old tabs to find the correct
        // tab panels to show and hide
        let index = Array.prototype.indexOf.call(tabs, newTab);
        let oldIndex = Array.prototype.indexOf.call(tabs, oldTab);
        
        newTab.focus();
        // Make the active tab focusable by the user (Tab key)
        newTab.removeAttribute('tabindex');
        // Set the selected state
        newTab.setAttribute('aria-selected', 'true');
        // Show tab.
        panels[index].hidden = false;
    
        if (index != oldIndex) {
          oldTab.removeAttribute('aria-selected');
          oldTab.setAttribute('tabindex', '-1');
          panels[oldIndex].hidden = true;
        }
    
        moveBar(newTab, bar);
      }
    
      // Show the tab when the fragment identifier of the URL has changed
      // (the part of the URL beginning with and following the # symbol).
      const locationHashChanged = () => {
        const currentHash = location.hash
        let nextTab = document.querySelector("[href='" + currentHash + "']");
        let tablist = getWrapperElement(nextTab, '.tabs__list');
        let currentTab = tablist.querySelector('[aria-selected]');
    
        if (nextTab !== null) {
          switchTab2(currentTab, nextTab);
        }
      }
    
      // Binds the function to the event.
      if(location.hash != ""){
        window.onhashchange = locationHashChanged;
      }
    })();
  • URL: /components/raw/tabs/tabs.js
  • Filesystem Path: src/components/04-modules/tabs/tabs.js
  • Size: 7.6 KB
  • Content:
    // Structure
    .tabs__list {
      margin: .7rem 0;
      padding: 0;
      display: flex;
      justify-content: center;
      max-width: 100vw;
    }
    
    .tabs__list [aria-selected] {
      position: relative;
    
      @include breakpoint($small-only) {
        position: static;
      }
    }
    
    .tabs__label {
      list-style-type: none;
      display: inline-block;
      min-width: 150px;
      text-align: center;
    
      @include breakpoint($small-only) {
        display: block;
        position: static;
        min-width: 100px;
        font-size: 80%;
      }
    }
    
    .tabs__item {
      display: inline-block;
      text-decoration: none;
      color: inherit;
    
      @include breakpoint($small-only) {
        display: block;
        position: static;
      }
    }
    
    .tabs__item:focus {
      outline: none;
    }
    
    .tabs__bar{
      position: absolute;
      height: 5px;
      margin: 0;
    }
    
    // Visuals
    .tabs__list {
      text-transform: uppercase;
      color: hsla(var(--navy-hsl), 0.3);
      font-weight: bold;
      letter-spacing: 0.6px;
    }
    
    .tabs__list [aria-selected] {
      color: var(--color-navy);
    }
    
    .tabs__bar {
      border: none;
      background-color: var(--color-yellow);
    }
    
    .tabs__panel {
      box-shadow: 0px 8px 32px hsla(var(--black-hsl), .15);
      padding: var(--s1) 0;
      outline: unset;
    }
  • URL: /components/raw/tabs/tabs.scss
  • Filesystem Path: src/components/04-modules/tabs/tabs.scss
  • Size: 1.1 KB

There are no notes for this item.