<section class="tabbed-content" data-module="tabbed-content" data-identifier="contact-us-tabs" aria-labelledby="tabbedContentHeading">
    <div class="tabbed-content__masthead">
        <div class="container tabbed-content__masthead-inner">
            <h2 class="tabbed-content__heading" id="tabbedContentHeading">Contact Us</h2>
            <div class="tabbed-content__tabs-wrapper">
                <div class="tabbed-content__dropdown-wrapper">
                    <select class="tabbed-content__dropdown" aria-label="Select to switch a tab">

                        <option value=tab-1>Select: Regional Contacts</option>

                        <option value=tab-2>Select: Customer Resources</option>

                        <option value=tab-3>Select: Grants and IIT</option>

                        <option value=tab-4>Select: Place an Order</option>

                    </select>
                </div>
                <div role="tablist" class="tabbed-content__tabs" aria-label="Contact us tabs">

                    <button class="tabbed-content__tab btn btn--primary btn--outline" role="tab" aria-selected="true" aria-controls="tab-1-panel" data-index=0 id="tab-1" data-identifier="contact-us-tabs">
                        Regional Contacts
                    </button>

                    <button class="tabbed-content__tab btn btn--primary btn--outline" role="tab" aria-selected="false" aria-controls="tab-2-panel" data-index=1 id="tab-2" tabindex="-1" data-identifier="contact-us-tabs">
                        Customer Resources
                    </button>

                    <button class="tabbed-content__tab btn btn--primary btn--outline" role="tab" aria-selected="false" aria-controls="tab-3-panel" data-index=2 id="tab-3" tabindex="-1" data-identifier="contact-us-tabs">
                        Grants and IIT
                    </button>

                    <button class="tabbed-content__tab btn btn--primary btn--outline" role="tab" aria-selected="false" aria-controls="tab-4-panel" data-index=3 id="tab-4" tabindex="-1" data-identifier="contact-us-tabs">
                        Place an Order
                    </button>

                </div>
            </div>
        </div>
    </div>
    <div class="tabbed-content__panels">

        <div class="tabbed-content__panel" role="tabpanel" id="tab-1-panel" aria-labelledby="tab-1" data-identifier="contact-us-tabs">
            <div class="container">
                <h4 style="font-size: 40px;">Example module for <span style="font-weight: 700;">Regional Contacts</span></h4>
            </div>
        </div>

        <div class="tabbed-content__panel" role="tabpanel" id="tab-2-panel" aria-labelledby="tab-2" aria-hidden="true" data-identifier="contact-us-tabs">
            <div class="container">
                <h4 style="font-size: 40px;">Example module for <span style="font-weight: 700;">Customer Resources</span></h4>
            </div>
        </div>

        <div class="tabbed-content__panel" role="tabpanel" id="tab-3-panel" aria-labelledby="tab-3" aria-hidden="true" data-identifier="contact-us-tabs">
            <div class="container">
                <h4 style="font-size: 40px;">Example module for <span style="font-weight: 700;">Grants and IIT</span></h4>
            </div>
        </div>

        <div class="tabbed-content__panel" role="tabpanel" id="tab-4-panel" aria-labelledby="tab-4" aria-hidden="true" data-identifier="contact-us-tabs">
            <div class="container">
                <h4 style="font-size: 40px;">Example module for <span style="font-weight: 700;">Place an Order</span></h4>
            </div>
        </div>

    </div>
</section>
<section class="tabbed-content" data-module="tabbed-content" data-identifier="{{identifier}}" aria-labelledby="tabbedContentHeading">
  <div class="tabbed-content__masthead">
    <div class="container tabbed-content__masthead-inner">
      <h2 class="tabbed-content__heading" id="tabbedContentHeading">{{heading}}</h2>
      <div class="tabbed-content__tabs-wrapper">
        <div class="tabbed-content__dropdown-wrapper">
          <select class="tabbed-content__dropdown" aria-label="{{tabDropdownLabel}}">
            {{#json tabs}}{{#each this}}
            <option value={{id}}>{{../../tabDropdownPrefix}}: {{label}}</option>
            {{/each}}{{/json}}
          </select>
        </div>
        <div role="tablist" class="tabbed-content__tabs" aria-label="Contact us tabs">
          {{#json tabs}}{{#each this}}
          <button
            class="tabbed-content__tab btn btn--primary btn--outline"
            role="tab"
            aria-selected="{{#if @index}}false{{else}}true{{/if}}"
            aria-controls="{{id}}-panel"
            data-index={{@index}}
            id="{{id}}"
            {{#if @index}}tabindex="-1"{{/if}}
            data-identifier="{{../../identifier}}"
          >
            {{label}}
          </button>
          {{/each}}{{/json}}
        </div>
      </div>
    </div>
  </div>
  <div class="tabbed-content__panels">
    {{#json tabs}}{{#each this}}
    <div
      class="tabbed-content__panel"
      role="tabpanel"
      id="{{id}}-panel"
      aria-labelledby="{{id}}"
      {{#if @index}}aria-hidden="true"{{/if}}
      data-identifier="{{../../identifier}}"
    >
      <div class="container">
        <h4 style="font-size: 40px;">Example module for <span style="font-weight: 700;">{{label}}</span></h4>
      </div>
    </div>
    {{/each}}{{/json}}
  </div>
</section>
{
  "heading": "Contact Us",
  "tabDropdownPrefix": "Select",
  "tabDropdownLabel": "Select to switch a tab",
  "identifier": "contact-us-tabs",
  "tabs": [
    {
      "id": "tab-1",
      "label": "Regional Contacts"
    },
    {
      "id": "tab-2",
      "label": "Customer Resources"
    },
    {
      "id": "tab-3",
      "label": "Grants and IIT"
    },
    {
      "id": "tab-4",
      "label": "Place an Order"
    }
  ]
}
  • Content:
    @import '../theme', '../button/styles', 'variables';
    
    .tabbed-content__masthead {
      padding-top: 40px;
      padding-bottom: 50px;
      background-blend-mode: overlay, normal;
      background-image: linear-gradient(
          140deg,
          rgba(255, 255, 255, 0.5),
          rgba(0, 0, 0, 0.5) 100%
        ),
        linear-gradient(to bottom, $masthead-background, $masthead-background);
    
      @include mq(tablet) {
        padding-top: 150px;
        padding-bottom: 142px;
      }
    }
    
    .tabbed-content__heading {
      font-size: 36px;
      font-weight: 600;
      line-height: 1.11;
      margin-bottom: 20px;
      color: $masthead-color;
    
      @include mq(tablet) {
        margin-bottom: 51px;
        font-size: 60px;
        line-height: 1;
      }
    }
    
    .tabbed-content__dropdown-wrapper {
      position: relative;
      width: 100%;
    
      @include mq(tablet) {
        display: none;
      }
    
      &:before {
        background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMCIgaGVpZ2h0PSI2IiB2aWV3Qm94PSIwIDAgMTAgNiI+CiAgICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGZpbGw9IiM0NTRENTEiPgogICAgICAgICAgICA8Zz4KICAgICAgICAgICAgICAgIDxnPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik04LjMxNSA0LjQ1TDQuNDg0IDAgMy4xODggMS4wOTkgNi41NDYgNSAzLjE4OCA4LjkwMSA0LjQ4NCAxMCA4LjMxNSA1LjU1IDguNzg3IDV6IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTczNSAtODU3KSB0cmFuc2xhdGUoMTUxMCA4NDApIHRyYW5zbGF0ZSgyMjQgMTUpIHJvdGF0ZSg5MCA1Ljk4NyA1KSIvPgogICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4K);
        background-position: center;
        background-repeat: no-repeat;
        background-color: $masthead-color;
        position: absolute;
        content: '';
        top: 1px;
        bottom: 1px;
        right: 1px;
        z-index: 1;
        width: 46px;
      }
    }
    
    .tabbed-content__dropdown {
      height: 40px;
      background-color: $masthead-color;
      font-size: 14px;
      width: 100%;
      padding-right: 17px;
      padding-left: 17px;
      border-width: 1px;
      border-radius: 4px;
      outline: 0;
    
      &:focus {
        border-color: map-get($theme-colors, 'primary-light');
      }
    }
    
    .tabbed-content__tabs {
      display: none;
    
      @include mq(tablet) {
        display: flex;
      }
    }
    
    .tabbed-content__tab.tabbed-content__tab.tabbed-content__tab {
      color: $tab-inactive-color;
      border-color: currentColor;
      position: relative;
      border-color: $tab-inactive-border-color;
    
      &:not(:first-child) {
        margin-left: -1px;
      }
    
      &:hover,
      &:focus,
      &[aria-selected='true'] {
        z-index: 100;
        color: $tab-active-color;
        background-color: $tab-active-background-color;
      }
    }
    
    .tabbed-content__panels {
      padding-top: 40px;
      padding-bottom: 40px;
      background-color: $tab-panel-background;
    
      @include mq(tablet) {
        padding-top: 100px;
        padding-bottom: 100px;
      }
    }
    
    .tabbed-content__panel {
      position: relative;
    
      &[aria-hidden] {
        display: none;
      }
    }
    
  • URL: /components/raw/c-fmt-10-tabbed-content/_styles.scss
  • Filesystem Path: src/components/C-FMT-10-tabbed-content/_styles.scss
  • Size: 2.8 KB
  • Content:
    $masthead-background: map-get($theme-colors, 'primary-dark') !default;
    $masthead-color: #fff;
    $tab-inactive-color: $masthead-color;
    $tab-inactive-border-color: #07b0e3;
    $tab-inactive-background-color: transparent;
    $tab-active-background-color: $tab-inactive-border-color;
    $tab-active-color: $masthead-background;
    $tab-panel-background: rgba(map-get($theme-colors, 'primary'), 0.1);
    
  • URL: /components/raw/c-fmt-10-tabbed-content/_variables.scss
  • Filesystem Path: src/components/C-FMT-10-tabbed-content/_variables.scss
  • Size: 382 Bytes
  • Content:
    import { Component } from '@verndale/core';
    import { keyCode as KEYS } from '../helpers';
    const KEYS_DIRECTION = {
      37: -1,
      38: -1,
      39: 1,
      40: 1
    };
    
    class Module extends Component {
      setupDefaults() {
        const identifier = this.el.dataset.identifier;
    
        this.dom = {
          $tabs: this.el.querySelectorAll(`[data-identifier="${identifier}"][role="tab"]`),
          $panels: this.el.querySelectorAll(`[data-identifier="${identifier}"][role="tabpanel"]`),
          $dropdown: this.el.querySelector('.tabbed-content__dropdown')
        };
    
        this.isVertical = this.el.dataset.isVertical === 'true';
      }
    
      addListeners() {
        this.dom.$tabs.forEach(tab => tab.addEventListener('click', this.onClick.bind(this)));
        this.dom.$tabs.forEach(tab => tab.addEventListener('keydown', this.onKeyDown.bind(this)));
        this.dom.$tabs.forEach(tab => tab.addEventListener('keyup', this.onKeyUp.bind(this)));
        this.dom.$dropdown.addEventListener('change', this.onChange.bind(this));
        this.el.addEventListener('activatetab', this.onActivatetab.bind(this));
        this.el.addEventListener('reset', this.onReset.bind(this));
      }
    
      onChange(e) {
        this.el.querySelector(`#${e.target.value}`).click()
      }
    
      onClick(e) {
        this.activateTab(e.target, false);
      }
    
      onKeyDown(e) {
        switch (e.keyCode) {
          case KEYS.END:
            e.preventDefault();
            this.activateTab(this.dom.$tabs[this.dom.$tabs.length - 1]);
            break;
          case KEYS.HOME:
            e.preventDefault();
            this.activateTab(this.don.$tabs[0]);
            break;
          case KEYS.UP:
          case KEYS.DOWN:
            if (this.isVertical) e.preventDefault();
            break
        }
      }
    
      onKeyUp(e) {
        switch (e.keyCode) {
          case KEYS.LEFT:
          case KEYS.RIGHT:
            if (!this.isVertical) this.switchTabOnArrowPress(e);
            break;
          case KEYS.UP:
          case KEYS.DOWN:
            if (this.isVertical) this.switchTabOnArrowPress(e);
            break;
        }
      }
    
      onActivatetab(e) {
        const tab = document.getElementById(e.detail.id);
        this.activateTab(tab, !!e.detail.setFocus);
      }
    
      onReset() {
        this.deactivateTabs();
        this.dom.$tabs[0].setAttribute('tabindex', '0');
      }
    
      activateTab(tab, setFocus = true) {
        this.deactivateTabs();
        tab.removeAttribute('tabindex');
        tab.setAttribute('aria-selected', 'true');
        const controls = tab.getAttribute('aria-controls');
        document.getElementById(controls).removeAttribute('aria-hidden');
        if (setFocus) {
          tab.focus();
        }
        const event = new CustomEvent('tabchange', { detail: { id: tab.id } });
        this.el.dispatchEvent(event);
      }
    
      deactivateTabs() {
        this.dom.$tabs.forEach(tab => {
          tab.setAttribute('tabindex', '-1');
          tab.setAttribute('aria-selected', 'false');
        });
    
        this.dom.$panels.forEach(panel => {
          panel.setAttribute('aria-hidden', 'true');
        });
      }
    
      switchTabOnArrowPress(e) {
        if (!KEYS_DIRECTION[e.keyCode]) return;
        if (typeof e.target.dataset.index === 'undefined') return;
        const index = parseInt(e.target.dataset.index, 10);
    
        if (this.dom.$tabs[index + KEYS_DIRECTION[e.keyCode]]) {
          this.activateTab(this.dom.$tabs[index + KEYS_DIRECTION[e.keyCode]], true);
          return;
        }
    
        if (this.isVertical) {
          if (e.keyCode === KEYS.UP) {
            this.activateTab(this.dom.$tabs[this.dom.$tabs.length - 1], true);
          } else if (e.keyCode === KEYS.DOWN) {
            this.activateTab(this.dom.$tabs[0], true);
          }
        } else {
          if (e.keyCode === KEYS.LEFT) {
            this.activateTab(this.dom.$tabs[this.dom.$tabs.length - 1], true);
          } else if (e.keyCode === KEYS.RIGHT) {
            this.activateTab(this.dom.$tabs[0], true);
          }
        }
      }
    }
    
    export default Module;
    
  • URL: /components/raw/c-fmt-10-tabbed-content/tabbed-content.js
  • Filesystem Path: src/components/C-FMT-10-tabbed-content/tabbed-content.js
  • Size: 3.7 KB
  • Handle: @c-fmt-10-tabbed-content
  • Preview:
  • Filesystem Path: src/components/C-FMT-10-tabbed-content/C-FMT-10-tabbed-content.hbs

Tabbed Content

THis is a wrapper where a list of modules can be added which can then be shown one ata time based on the current selected tab

THe identifier needs to be added to support nested tabs. Please be sure to correctly match IDs and aria-attributes between tabs and panels

Keyboard interactions

When focus is on a header, the following key commands are available:

  • TAB: When focus moves into the tabs, 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: Move focus and opens previous tab.
  • LEFT: Move focus and opens next tab.
  • SPACE/ENTER/RETURN: Open/close a panel associated with focused header.
  • SHIFT + TAB: Moves focus to the previous focusable element.

Styling

The component will already have a default style/theme set, so it will work out of the box. The button has a set of variables that may be overriden, or you may choose to just override the actual styles in your project. It is recommended to use the overrides if you can as it will be less code in your project.

Below is the list of variables you can override. You may also find these in the Assets tab under _variables.scss

$masthead-background: map-get($theme-colors, 'primary-dark') !default;
$masthead-color: #fff;
$tab-inactive-color: $masthead-color;
$tab-inactive-border-color: #07b0e3;
$tab-inactive-background-color: transparent;
$tab-active-background-color: $tab-inactive-border-color;
$tab-active-color: $masthead-background;
$tab-panel-background: rgba(map-get($theme-colors, 'primary'), 0.1);