Tab

<div class="tab  " role="widget">
    <button class="tab__title tab__title--active " data-tab="tab-1" id="tab-title-1" aria-controls="tab-1" aria-selected="true" aria-expanded="true">
        Tab title
    </button>

    <div class="
                tab__content
                tab__content--active
                
            " id="tab-1" data-content="tab-1" aria-labelledby="tab-title-1" aria-hidden="false">
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque pharetra ut magna ornare lacinia. Cras sodales elit ac pellentesque aliquet. Nulla nec viverra turpis. Mauris eget quam interdum, viverra enim eget, ultricies purus. Suspendisse eleifend, turpis id pretium consectetur, massa nunc suscipit elit, a porta neque neque sed ex. Curabitur nec ante non urna rhoncus semper. Maecenas id pulvinar erat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque mollis sit amet quam id vestibulum. Nunc faucibus quam non venenatis laoreet. Pellentesque eu feugiat tellus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.</p>
    </div>
    <button class="tab__title  " data-tab="tab-2" id="tab-title-2" aria-controls="tab-2" aria-selected="false" aria-expanded="false">
        Tab title 2
    </button>

    <div class="
                tab__content
                
                
            " id="tab-2" data-content="tab-2" aria-labelledby="tab-title-2" aria-hidden="true">
        <div class="lazyload-wrapper ">
            <img class="image lazyload " src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw&#x3D;&#x3D;" data-src="/images/banner/banner-480_480.png" alt="image alt text">
        </div>

    </div>
    <button class="tab__title  " data-tab="tab-3" id="tab-title-3" aria-controls="tab-3" aria-selected="false" aria-expanded="false">
        Tab title 3
    </button>

    <div class="
                tab__content
                
                
            " id="tab-3" data-content="tab-3" aria-labelledby="tab-title-3" aria-hidden="true">
        <p>Pellentesque velit nisl, posuere ac erat nec, iaculis auctor magna. Donec ut elementum mauris. Sed in scelerisque lorem. Cras pretium laoreet vestibulum. In dui mauris, sagittis vitae rhoncus quis, ornare in enim. Vivamus pellentesque ligula sed quam sollicitudin lobortis. Etiam interdum euismod nisi a interdum. Nunc turpis urna, pharetra nec nunc sit amet, consequat porta orci. Suspendisse sit amet dignissim lacus. Quisque vel est pretium, aliquam lacus id, lobortis neque. Nullam non neque mi. Vivamus quis pulvinar mi, in elementum nulla. Etiam neque diam, suscipit vitae luctus et, sollicitudin in turpis. Proin vitae cursus augue, sit amet vestibulum mi. Mauris feugiat justo eget purus fringilla aliquam vitae a massa.</p>
    </div>
    <button class="tab__title  " data-tab="tab-4" id="tab-title-4" aria-controls="tab-4" aria-selected="false" aria-expanded="false">
        Tab title 4
    </button>

    <div class="
                tab__content
                
                
            " id="tab-4" data-content="tab-4" aria-labelledby="tab-title-4" aria-hidden="true">
        <p>Pellentesque velit nisl, posuere ac erat nec, iaculis auctor magna. Donec ut elementum mauris. Sed in scelerisque lorem. Cras pretium laoreet vestibulum. In dui mauris, sagittis vitae rhoncus quis, ornare in enim. Vivamus pellentesque ligula sed quam sollicitudin lobortis. Etiam interdum euismod nisi a interdum. Nunc turpis urna, pharetra nec nunc sit amet, consequat porta orci. Suspendisse sit amet dignissim lacus. Quisque vel est pretium, aliquam lacus id, lobortis neque. Nullam non neque mi. Vivamus quis pulvinar mi, in elementum nulla. Etiam neque diam, suscipit vitae luctus et, sollicitudin in turpis. Proin vitae cursus augue, sit amet vestibulum mi. Mauris feugiat justo eget purus fringilla aliquam vitae a massa.</p>
    </div>
    <button class="tab__title  " data-tab="tab-5" id="tab-title-5" aria-controls="tab-5" aria-selected="false" aria-expanded="false">
        Tab title 5
    </button>

    <div class="
                tab__content
                
                
            " id="tab-5" data-content="tab-5" aria-labelledby="tab-title-5" aria-hidden="true">
        <p>Pellentesque velit nisl, posuere ac erat nec, iaculis auctor magna. Donec ut elementum mauris. Sed in scelerisque lorem. Cras pretium laoreet vestibulum. In dui mauris, sagittis vitae rhoncus quis, ornare in enim. Vivamus pellentesque ligula sed quam sollicitudin lobortis. Etiam interdum euismod nisi a interdum. Nunc turpis urna, pharetra nec nunc sit amet, consequat porta orci. Suspendisse sit amet dignissim lacus. Quisque vel est pretium, aliquam lacus id, lobortis neque. Nullam non neque mi. Vivamus quis pulvinar mi, in elementum nulla. Etiam neque diam, suscipit vitae luctus et, sollicitudin in turpis. Proin vitae cursus augue, sit amet vestibulum mi. Mauris feugiat justo eget purus fringilla aliquam vitae a massa.</p>
    </div>
</div>

<script src="/components/raw/tab/tab.js"></script>
<div
    class="tab {{#if modifier}}tab--{{modifier}}{{/if}} {{ class }}"
    role="widget"
    {{{ attributes }}}
>
    {{#each tabs as |tab|}}
        <button
            class="tab__title {{#if active}}tab__title--active{{/if}} {{ class.title }}"
            data-tab="{{ tabId }}"
            id="{{ titleId }}"
            aria-controls="{{ tabId }}"
            aria-selected="{{#if active}}true{{else}}false{{/if}}"
            aria-expanded="{{#if active}}true{{else}}false{{/if}}"
        >
            {{ title }}
        </button>

        <div
            class="
                tab__content
                {{#if active}}tab__content--active{{/if}}
                {{ class.content }}
            "
            id="{{ tabId }}"
            data-content="{{ tabId }}"
            aria-labelledby="{{ titleId }}"
            aria-hidden="{{#if active}}false{{else}}true{{/if}}"
        >
            {{#if content}}
                {{{ content }}}
            {{/if}}
            {{#if contentElement}}
                {{ render (component contentElement) contentContext }}
            {{/if}}
        </div>
    {{/each}}
</div>

{{#if script}}
    <script src="{{static 'tab.js' }}"></script>
{{/if}}
{
  "script": true,
  "tabs": [
    {
      "tabId": "tab-1",
      "title": "Tab title",
      "titleId": "tab-title-1",
      "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque pharetra ut magna ornare lacinia. Cras sodales elit ac pellentesque aliquet. Nulla nec viverra turpis. Mauris eget quam interdum, viverra enim eget, ultricies purus. Suspendisse eleifend, turpis id pretium consectetur, massa nunc suscipit elit, a porta neque neque sed ex. Curabitur nec ante non urna rhoncus semper. Maecenas id pulvinar erat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque mollis sit amet quam id vestibulum. Nunc faucibus quam non venenatis laoreet. Pellentesque eu feugiat tellus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.</p>",
      "active": true,
      "class": {
        "content": "",
        "title": ""
      }
    },
    {
      "tabId": "tab-2",
      "title": "Tab title 2",
      "titleId": "tab-title-2",
      "contentElement": "image",
      "class": {
        "content": "",
        "title": ""
      }
    },
    {
      "tabId": "tab-3",
      "title": "Tab title 3",
      "titleId": "tab-title-3",
      "content": "<p>Pellentesque velit nisl, posuere ac erat nec, iaculis auctor magna. Donec ut elementum mauris. Sed in scelerisque lorem. Cras pretium laoreet vestibulum. In dui mauris, sagittis vitae rhoncus quis, ornare in enim. Vivamus pellentesque ligula sed quam sollicitudin lobortis. Etiam interdum euismod nisi a interdum. Nunc turpis urna, pharetra nec nunc sit amet, consequat porta orci. Suspendisse sit amet dignissim lacus. Quisque vel est pretium, aliquam lacus id, lobortis neque. Nullam non neque mi. Vivamus quis pulvinar mi, in elementum nulla. Etiam neque diam, suscipit vitae luctus et, sollicitudin in turpis. Proin vitae cursus augue, sit amet vestibulum mi. Mauris feugiat justo eget purus fringilla aliquam vitae a massa.</p>",
      "class": {
        "content": "",
        "title": ""
      }
    },
    {
      "tabId": "tab-4",
      "title": "Tab title 4",
      "titleId": "tab-title-4",
      "content": "<p>Pellentesque velit nisl, posuere ac erat nec, iaculis auctor magna. Donec ut elementum mauris. Sed in scelerisque lorem. Cras pretium laoreet vestibulum. In dui mauris, sagittis vitae rhoncus quis, ornare in enim. Vivamus pellentesque ligula sed quam sollicitudin lobortis. Etiam interdum euismod nisi a interdum. Nunc turpis urna, pharetra nec nunc sit amet, consequat porta orci. Suspendisse sit amet dignissim lacus. Quisque vel est pretium, aliquam lacus id, lobortis neque. Nullam non neque mi. Vivamus quis pulvinar mi, in elementum nulla. Etiam neque diam, suscipit vitae luctus et, sollicitudin in turpis. Proin vitae cursus augue, sit amet vestibulum mi. Mauris feugiat justo eget purus fringilla aliquam vitae a massa.</p>",
      "class": {
        "content": "",
        "title": ""
      }
    },
    {
      "tabId": "tab-5",
      "title": "Tab title 5",
      "titleId": "tab-title-5",
      "content": "<p>Pellentesque velit nisl, posuere ac erat nec, iaculis auctor magna. Donec ut elementum mauris. Sed in scelerisque lorem. Cras pretium laoreet vestibulum. In dui mauris, sagittis vitae rhoncus quis, ornare in enim. Vivamus pellentesque ligula sed quam sollicitudin lobortis. Etiam interdum euismod nisi a interdum. Nunc turpis urna, pharetra nec nunc sit amet, consequat porta orci. Suspendisse sit amet dignissim lacus. Quisque vel est pretium, aliquam lacus id, lobortis neque. Nullam non neque mi. Vivamus quis pulvinar mi, in elementum nulla. Etiam neque diam, suscipit vitae luctus et, sollicitudin in turpis. Proin vitae cursus augue, sit amet vestibulum mi. Mauris feugiat justo eget purus fringilla aliquam vitae a massa.</p>",
      "class": {
        "content": "",
        "title": ""
      }
    }
  ]
}
  • Content:
    .tab {
        display: flex;
        flex-flow: row wrap;
    
        &__title {
            position: relative;
            display: block;
            width: 100%;
            padding: $spacer--medium;
            border-width: 1px 0 0 0;
            background: $white;
            font-weight: $font-weight-normal;
            color: $gray-darker;
            font-family: $font-family-saira;
            font-size: $font-size-large;
            text-align: left;
            cursor: pointer;
            flex: 1;
            order: -1;
    
            border-top: 1px solid #e6e6e6;
            border-bottom: 1px solid #e6e6e6;
    
            @include mq($screen-l) {
                border-top: none;
            }
    
            &--active {
                background: $white;
                color: $black;
                font-weight: $font-weight-semibold;
                font-family: $font-family-saira-bold;
                z-index: 2;
            }
    
            .counter {
                &:before {
                    content: "(";
                }
    
                &:after {
                    content: ")";
                }
            }
        }
    
        &__content {
            position: relative;
            display: none;
            width: 100%;
            padding: $spacer--1;
            z-index: -1;
            opacity: 0;
            overflow: hidden;
            color: $gray-light;
            font-family: $font-family-saira;
            
            @include mq($screen-l) {
                flex-basis: 100%;
                z-index: 1;
            }
    
            &--active {
                display: block;
                opacity: 1;
                z-index: 1;
    
                &:nth-last-child(1) {
                    border-bottom: none;
                }
            }
        }
    }
    
  • URL: /components/raw/tab/_tab.scss
  • Filesystem Path: build/components/02-elements/tab/_tab.scss
  • Size: 1.6 KB
  • Content:
    'use strict';
    
    (function() { // eslint-disable-line
      const tab                = document.querySelector('.tab'),
            activeTitleClass   = 'tab__title--active',
            activeContentClass = 'tab__content--active',
            children           = Array.from(tab.children),
            titles             = [...tab.querySelectorAll('.tab__title')],
            mqBreakpoint       = window.matchMedia('screen and (min-width: 992px)');
    
      function setActiveTab(elem) {
        const tabTitle = elem.dataset.tab;
    
        children.forEach((item) => {
    
          resetItems(item);
    
          if (
            item.classList.contains('tab__content')
            && tabTitle === item.dataset.content
          ) {
            item.classList.add(activeContentClass);
            item.setAttribute('aria-hidden', false);
          }
    
          elem.classList.add(activeTitleClass);
          elem.setAttribute('aria-expanded', true);
          elem.setAttribute('aria-selected', true);
        });
      }
      function keyboardForward(currentTitleIndex) {
        if (currentTitleIndex === titles.length - 1) {
          titles[0].focus();
        }
        else {
          titles[currentTitleIndex + 1].focus();
        }
      }
    
      function keyboardBackward(currentTitleIndex) {
        if (currentTitleIndex === 0) {
          titles[titles.length - 1].focus();
        }
        else {
          titles[currentTitleIndex - 1].focus();
        }
      }
    
      function resetItems(item) {
        if (item.classList.contains('tab__content')) {
          item.classList.remove(activeContentClass);
          item.setAttribute('aria-hidden', true);
        }
        else {
          item.classList.remove(activeTitleClass);
          item.setAttribute('aria-expanded', false);
          item.setAttribute('aria-selected', false);
        }
      }
    
      function setKeyboardNav(e) {
        let currentTitle = e.target,
            currentTitleIndex = titles.indexOf(currentTitle);
    
        if (mqBreakpoint.matches) {
          if (e.which == 39) {
            e.preventDefault();
            keyboardForward(currentTitleIndex);
          }
          else if (e.which == 37) {
            e.preventDefault();
            keyboardBackward(currentTitleIndex);
          }
        }
        else {
          if (e.which == 38) {
            e.preventDefault();
            keyboardBackward(currentTitleIndex);
          }
          else if (e.which == 40) {
            e.preventDefault();
            keyboardForward(currentTitleIndex);
          }
        }
      }
    
      titles.forEach(item => {
        item.addEventListener('click', (e) => {
          setActiveTab(e.target);
        });
        item.addEventListener('keydown', (e) => {
          setKeyboardNav(e);
        })
      });
    
    })();
    
  • URL: /components/raw/tab/tab.js
  • Filesystem Path: build/components/02-elements/tab/tab.js
  • Size: 2.5 KB

Tabs

Accessibility in tabs

Our components is accordion on mobile and tabs on wider screens, so we have to implement a11y feature to be used in both cases:

  • use role="widget" on tab element
  • use buttons elements as a Labels/headings of tabs
  • Buttons should have following aria attributes:
    • aria-expanded - set to true if tab is open, to false when it’s closed
    • aria-controls where value it’s an id of content tab element
    • aria-selected set to false when tab is closed and to true if it’s open
    • tab title have an id to be binded to tab content
  • Tab content element should:
    • have an id to be binded to tab title
    • have aria-hidden attribute set to true if tab is closed and to false if it’s open
    • aria-labelledby with value of tab title id
  • Keyboard support:
    • accordions - on mobile, down & up arrow keys should move cursor between tab’s titles, from first tab title up arrow should move a user to the last tab title, from last tab title down arrow should move user to first tab title.
    • tabs = on desktop - right & left arrow keys should move cursor between tab’s titles, from first tab title left arrow should move a user to the last tab title, from last tab title right arrow should move user to first tab title.
    • focus management - Focus should be visible and tab’s titles should be focusable with Tab key