Tabs live

Open the above preview in a new window: With layout Open Component only Open

Tabs Component

npm version

About

The ever-useful tabs. This component works best with the included JS, but you can use the CSS styling on other tab sets, like Bootstrap tabs.

Install

This component is distributed with npm. After installing npm, you can install the vf-tabs with this command.

$ yarn add --dev @visual-framework/vf-tabs

The source files included are written in Sass(scss). You can point your Sass include-path at your node_modules directory and import it like this.

@import "@visual-framework/vf-tabs/index.scss";

Make sure you import Sass requirements along with the modules.

Component information

  • Handle: @vf-tabs
  • Filesystem path: components/vf-tabs/vf-tabs.njk

Code example

<div class="vf-tabs">
    <ul class="vf-tabs__list" data-vf-js-tabs>
        <li class="vf-tabs__item">
            <a class="vf-tabs__link" href="#vf-tabs__section--1">Section</a>
        </li>
        <li class="vf-tabs__item">
            <a class="vf-tabs__link" href="#vf-tabs__section--2">A Short Section</a>
        </li>
        <li class="vf-tabs__item">
            <a class="vf-tabs__link" href="#vf-tabs__section--3">A Rather Long Section</a>
        </li>
        <li class="vf-tabs__item">
            <a class="vf-tabs__link" href="#vf-tabs__section--4">Nested Tabs</a>
        </li>
    </ul>
</div>

<div class="vf-tabs-content" data-vf-js-tabs-content>
    <section class="vf-tabs__section" id="vf-tabs__section--1">
        <h2>Section 1</h2>
        <p>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 class="vf-link" 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 class="vf-tabs__section" id="vf-tabs__section--2">
        <h2>Section 2</h2>
        <p>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 class="vf-tabs__section" id="vf-tabs__section--3">
        <h2>Section 3</h2>
        <p>Phasellus ac tristique orci. Nulla maximus <a class="vf-link" 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 class="vf-link" href="#">pellentesque ac ante</a>. Aliquam in commodo ligula, sit amet mollis neque. Vestibulum at facilisis massa.</p>
    </section>
    <section class="vf-tabs__section" id="vf-tabs__section--4">
        <h2>Section 4</h2>
        <p>
            <div class="vf-tabs">
                <ul class="vf-tabs__list" data-vf-js-tabs>
                    <li class="vf-tabs__item">
                        <a class="vf-tabs__link" href="#vf-tabs__section-nested--1">Nested tab 1</a>
                    </li>
                    <li class="vf-tabs__item">
                        <a class="vf-tabs__link" href="#vf-tabs__section-nested--2">Nested tab 2</a>
                    </li>
                </ul>
            </div>
            <div class="vf-tabs-content" data-vf-js-tabs-content>
                <section class="vf-tabs__section" id="vf-tabs__section-nested--1">
                    <p>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 class="vf-tabs__section" id="vf-tabs__section-nested--2">
                    <p>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>
            </div>
        </p>
    </section>
</div>

Resources and files

          # Change Log

All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

# 1.0.0 (2019-12-17)

* Initial stable release

        

File information

          // setup files required

// sass-lint:disable clean-import-paths
@import 'vf-variables';
@import 'vf-functions';
@import 'vf-mixins';

// component specific styles

@import 'vf-tabs.scss';

        

File information

          {
  "version": "1.0.0",
  "name": "@visual-framework/vf-tabs",
  "description": "vf-tabs component",
  "homepage": "https://visual-framework.github.io/vf-core/",
  "author": "VF",
  "license": "Apache 2.0",
  "style": "vf-tabs.css",
  "sass": "index.scss",
  "main": "vf-tabs.js",
  "test": "echo \"Error: no test specified\" && exit 1",
  "publishConfig": {
    "access": "public"
  },
  "repo": "https://github.com/visual-framework/vf-core/tree/master/components/blocks/vf-tabs",
  "bugs": {
    "url": "https://github.com/visual-framework/vf-core/issues/new"
  },
  "keywords": [
    "fractal",
    "component"
  ],
  "gitHead": "7bb3e8605bb5e604a55e7480e8e0d7620cee1b7f"
}

        

File information

          // vf-tabs

/**
 * Finds all tabs on a page and activates them
 * @param {object} [scope] - the html scope to process, optional, defaults to `document`
 * @example vfTabs(document.querySelectorAll('.vf-component__container')[0]);
 */
function vfTabs(scope) {
  var scope = scope || document;
  // Get relevant elements and collections
  const tablist = scope.querySelectorAll('[data-vf-js-tabs]');
  const panelsList = scope.querySelectorAll('[data-vf-js-tabs-content]');
  const panels = scope.querySelectorAll('[data-vf-js-tabs-content] [id^="vf-tabs__section"]');
  const tabs = scope.querySelectorAll('[data-vf-js-tabs] .vf-tabs__link');
  if (!tablist || !panels || !tabs) {
    // exit: either tabs or tabbed content not found
    return;
  }
  if (tablist.length == 0 || panels.length == 0 || tabs.length == 0) {
    // exit: either tabs or tabbed content not found
    return;
  }

  // The tab switching function
  const switchTab = (newTab) => {

    // get the parent ul of the clicked tab
    let parentTabSet = newTab.closest(".vf-tabs__list");
    let oldTab = parentTabSet.querySelector('[aria-selected]');
    if (oldTab) {
      oldTab.removeAttribute('aria-selected');
      oldTab.setAttribute('tabindex', '-1');
      oldTab.classList.remove('is-active');
      let oldIndex = Array.prototype.indexOf.call(tabs, oldTab);
      panels[oldIndex].hidden = true;
    }

    newTab.focus();
    // Make the active tab focusable by the user (Tab key)
    newTab.removeAttribute('tabindex');
    // Set the selected state
    newTab.setAttribute('aria-selected', 'true');
    newTab.classList.add('is-active');
    // Get the indices of the new tab to find the correct
    // tab panel to show
    let index = Array.prototype.indexOf.call(tabs, newTab);
    panels[index].hidden = false;
  }

  // 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('data-tabs__item', 'tab' + (i + 1));
    tab.setAttribute('tabindex', '-1');
    tab.parentNode.setAttribute('role', 'presentation');

    // Reset any active tabs from a previous JS call
    tab.removeAttribute('aria-selected');
    tab.setAttribute('tabindex', '-1');
    tab.classList.remove('is-active');

    // Handle clicking of tabs for mouse users
    tab.addEventListener('click', e => {
      e.preventDefault();
      switchTab(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(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;
  });

  // Add the tablist role to the first <ul> in the .tabbed container
  Array.prototype.forEach.call(tablist, (tablistset, i) => {
    tablistset.setAttribute('role', 'tablist');
    // Initially activate the first tab
    let firstTab = tablistset.querySelectorAll('.vf-tabs__link')[0];
    firstTab.removeAttribute('tabindex');
    firstTab.setAttribute('aria-selected', 'true');
    firstTab.classList.add('is-active');
  });
  Array.prototype.forEach.call(panelsList, (panel, i) => {
    // Initially reveal the first tab panel
    let firstPanel = panel.querySelectorAll('.vf-tabs__section')[0];
    firstPanel.hidden = false;
  });
}

export { vfTabs };

        

File information

          // vf-tabs 

@import 'package.variables.scss';
// Debug information from component's `package.json`:
// ---
/*!
 * Component: #{map-get($componentInfo, 'name')}
 * Version: #{map-get($componentInfo, 'version')}
 * Location: #{map-get($componentInfo, 'location')}
 */

$vf-tabs__link-text--color: set-color(vf-color--grey--dark) !default;
$vf-tabs__link-text--color-hover: set-ui-color(vf-ui-color--white) !default;
$vf-tabs__link-background--color: set-color(vf-color--grey--lightest) !default;
$vf-tabs__link-background--hover: set-color(vf-color--grey--dark) !default;
$vf-tabs__list-border--color: set-ui-color(vf-ui-color--black) !default;

$vf-tabs__link--active-text--color: set-ui-color(vf-ui-color--black);
$vf-tabs__link--active-background--color: set-ui-color(vf-ui-color--off-white);

$vf-tabs__list--active-border--color: set-ui-color(vf-ui-color--white);

.vf-tabs__list {
  border-bottom: 1px solid $vf-tabs__list-border--color;
  margin: 0;
  padding: 0 0 0px 16px;
  position: relative;

  // @media (min-width: $vf-breakpoint--xl) {
  //   &::before {
  //     border-bottom: 1px solid $vf-tabs__list-border--color;
  //     bottom: -1px;
  //     content: '';
  //     height: 1px;
  //     position: absolute;
  //     transform: translateX(-50%);
  //     width: 100vw;
  //     z-index: 5150;
  //   }
  //
  //   &::after {
  //     border-bottom: 1px solid $vf-tabs__list-border--color;
  //     bottom: -1px;
  //     content: '';
  //     height: 1px;
  //     position: absolute;
  //     transform: translateX(-50%);
  //     width: 100vw;
  //     z-index: 5150;
  //   }
  // }
}

.vf-tabs__item,
.vf-tabs__link {
  display: inline-block;
  position: relative;
}

.vf-tabs__link {
  background: $vf-tabs__link-background--color;
  border-color: transparent transparent $vf-tabs__list-border--color transparent;
  border-style: solid;
  border-width: 1px;
  color: $vf-tabs__link-text--color;
  outline: 0;
  padding: 12px 16px;
  text-decoration: none;
  top: 1px;

  &:hover {
    background-color: $vf-tabs__link-background--hover;
    color: $vf-tabs__link-text--color-hover;
  }
}

.vf-tabs__link.is-active {
  background: $vf-tabs__link--active-background--color;
  border-bottom-color: $vf-tabs__list--active-border--color;
  color: $vf-tabs__link--active-text--color;
}

.vf-tabs__section {
  padding: 8px 16px;
}

@media (max-width: 550px) {
  .vf-tabs__list {
    border-bottom: 0;
    display: flex;
    flex-wrap: wrap;
    padding: 0;
  }
  .vf-tabs__item {
    display: block;
    flex-basis: auto; /* default value */
    flex-grow: 1;
    margin: 2px;
    position: static;
  }

  .vf-tabs__link {
    border-color: transparent;
    display: block;
    top: unset;
  }

  .vf-tabs__link.is-active {
    border-color: $vf-tabs__list-border--color;
    position: static;
  }
  .vf-tabs__section {
    padding: 8px;
  }
}

        

File information

All the components

Grids

Put stuff in columns.

Grid

Page Grid

Inlay


Elements

The micro elements of the component library.

Badges

Blockquote

Button

Collapse

Divider

Explainer

Favicon

Figure

Headings

Link

Lists

Logo

No JS

Text


Blocks

Simple components like sections headers, galleries and so on.

Activity List

Article Meta Information

Box

Breadcrumbs

Card

Code Example

Discussion

Embed

Footer

Global Header

Lede

Links List

Masthead

Navigation

Page Header

Pagination

Search

Section Header

Social icons

Summary

Table

Tabs

Tree

Video

Video Teaser


Container

More complex components that sometimes have specific layout, like page intros, mastheads, news sections and so on.

EBI Header and Footer

Acivity List Group

Banner

Card Container

Content

Forms

Header

Hero

Intro

News Container

Summary Container

Video Container


Boilerplates

Whole-page templates that are a collection of many components.

VF Boilerplate


Utilities

Utility classes to help where neeed.

EBI-VF 1.x Compatibility Component

EMBL Notifications

Google Analytics enhancements

Visual Framework Componet Rollup

Design Tokens

Plex Mono Font

Plex Sans Font

JS Polyfill

Sass config

Full Bleed

Utility classes


EMBL Grids

EMBLs way to put stuff in columns.

EMBL Grid


EMBL Elements

EMBLs micro elements of the component library.

EMBL Conditional Edit

EMBL Favicon

EMBL Logo


EMBL Blocks

Simple components from EMBL like sections headers, galleries and so on.

EMBL Breadcrumbs

EMBL Content meta properties


EMBL Containers

More complex EMBL components that sometimes have specific layout, like page intros, mastheads, news sections and so on.

EMBL ContentHub Loader


EMBL Boilerplates

Whole-page templates that are a collection of many components.


Deprecated

These components are no longer maintained.

Deprecated pattern

Sass utilities