<div class="js-pill-tabs m-pill-tabs" role="tablist" aria-orientation="horizontal" tabindex="0">
<button type="button" class="m-pill-tabs__item js-pill-tabs__tab is-active" role="tab" aria-selected="true" aria-controls="" data-state="active" id="" tabindex="-1" data-orientation="horizontal">Overview
</button>
<button type="button" class="m-pill-tabs__item js-pill-tabs__tab" role="tab" aria-selected="false" aria-controls="" data-state="inactive" id="" tabindex="-1" data-orientation="horizontal">Analytics
</button>
<button type="button" class="m-pill-tabs__item js-pill-tabs__tab" role="tab" aria-selected="false" aria-controls="" data-state="inactive" id="" tabindex="-1" data-orientation="horizontal">Reports
</button>
<button type="button" class="m-pill-tabs__item js-pill-tabs__tab" role="tab" aria-selected="false" aria-controls="" data-state="inactive" id="" tabindex="-1" data-orientation="horizontal">Settings
</button>
</div>
<div class="js-pill-tabs__panel" role="tabpanel">Overview content</div>
<div class="js-pill-tabs__panel" role="tabpanel" hidden>Analytics content</div>
<div class="js-pill-tabs__panel" role="tabpanel" hidden>Reports content</div>
<div class="js-pill-tabs__panel" role="tabpanel" hidden>Settings content</div>
<style>
body {
background-color: #fff;
}
</style>
{{#if links}}
<nav class="m-pill-tabs" aria-orientation="horizontal">
<a href="#overview" class="m-pill-tabs__item is-active">Overview</a>
<a href="#analytics" class="m-pill-tabs__item">Analytics</a>
<a href="#reports" class="m-pill-tabs__item">Reports</a>
<a href="#settings" class="m-pill-tabs__item">Settings</a>
</nav>
{{else}}
<div class="js-pill-tabs m-pill-tabs" role="tablist" aria-orientation="horizontal" tabindex="0">
<button type="button" class="m-pill-tabs__item js-pill-tabs__tab is-active" role="tab" aria-selected="true" aria-controls=""
data-state="active" id=""
tabindex="-1" data-orientation="horizontal">Overview
</button>
<button type="button" class="m-pill-tabs__item js-pill-tabs__tab" role="tab" aria-selected="false" aria-controls=""
data-state="inactive" id=""
tabindex="-1" data-orientation="horizontal">Analytics
</button>
<button type="button" class="m-pill-tabs__item js-pill-tabs__tab" role="tab" aria-selected="false" aria-controls=""
data-state="inactive" id=""
tabindex="-1" data-orientation="horizontal">Reports
</button>
<button type="button" class="m-pill-tabs__item js-pill-tabs__tab" role="tab" aria-selected="false" aria-controls=""
data-state="inactive" id=""
tabindex="-1" data-orientation="horizontal">Settings
</button>
</div>
<div class="js-pill-tabs__panel" role="tabpanel">Overview content</div>
<div class="js-pill-tabs__panel" role="tabpanel" hidden>Analytics content</div>
<div class="js-pill-tabs__panel" role="tabpanel" hidden>Reports content</div>
<div class="js-pill-tabs__panel" role="tabpanel" hidden>Settings content</div>
{{/if}}
<style>
body {
background-color: #fff;
}
</style>
{
"links": false
}
@charset "UTF-8";
@use '../../configurations/mixins' as mixin;
@use '../../configurations/bem' as bem;
@use '../../configurations/config' as config;
@use '../../configurations/variables' as var;
@use '../../configurations/functions' as func;
@use '../../configurations/colors/colors' as colors;
@use '../../configurations/colors/colors-functions' as colorFunc;
@use '../../vendor/grid/breakpoints' as breakpoint;
@use '../../vendor/grid/grid' as grid;
@include mixin.molecule(pill-tabs) {
background: colors.$color-ash;
border-radius: var.$border-radius;
border: 1px solid colors.$color-concrete;
padding: func.rhythm(0.5);
display: inline-flex;
flex-wrap: wrap;
gap: func.rhythm(0.5);
@include bem.e(item) {
padding: func.rhythm(0.5);
border-radius: var.$border-radius;
font-size: var.$size-medium;
color: colors.$color-cyberspace;
border: 1px solid transparent;
&.is-active {
pointer-events: none;
}
&.is-active,
&:active {
background-color: colors.$color-snow;
border: 1px solid colors.$color-concrete;
color: colors.$color-cyberspace;
text-decoration: none;
}
&:hover,
&:focus {
background-color: colors.$color-granit;
border-color: colors.$color-granit;
color: colors.$color-snow;
text-decoration: none;
}
&:focus:active {
background-color: colors.$color-ash;
border: 1px solid colors.$color-ash;
color: colors.$color-granit;
text-shadow: 0 1px 0 colors.$color-snow;
text-decoration: none;
}
}
}
const TABLIST_SELECTOR = '.js-pill-tabs[role="tablist"]';
const TAB_SELECTOR = '.js-pill-tabs__tab[role="tab"]';
const PANEL_SELECTOR = '.js-pill-tabs__panel[role="tabpanel"]';
let tablistCounter = 0;
function ensureUniqueId(element, fallbackId) {
const preferredId = element.id || fallbackId;
let nextId = preferredId;
let duplicate = document.getElementById(nextId);
let suffix = 0;
while (duplicate && duplicate !== element) {
suffix += 1;
nextId = `${preferredId}-${suffix}`;
duplicate = document.getElementById(nextId);
}
element.id = nextId;
return nextId;
}
function getInitialActiveIndex(tabs) {
let classIndex = -1;
for (let index = 0; index < tabs.length; index += 1) {
const tab = tabs[index];
if (tab.getAttribute('aria-selected') === 'true') {
return index;
}
if (classIndex === -1 && tab.classList.contains('is-active')) {
classIndex = index;
}
}
return classIndex !== -1 ? classIndex : 0;
}
function getPanelsForTablist(tablist, maxPanels) {
const panels = [];
let nextSibling = tablist.nextElementSibling;
while (nextSibling && panels.length < maxPanels) {
if (nextSibling.matches(TABLIST_SELECTOR)) {
break;
}
if (nextSibling.matches(PANEL_SELECTOR)) {
panels.push(nextSibling);
}
nextSibling = nextSibling.nextElementSibling;
}
return panels;
}
function getTabFromEventTarget(target, tablist) {
const tab = target.closest(TAB_SELECTOR);
return tab && tab.parentElement === tablist ? tab : null;
}
function getWrappedIndex(index, length) {
if (index < 0) {
return length - 1;
}
if (index >= length) {
return 0;
}
return index;
}
function setupTablist(tablist) {
const tabs = Array.from(tablist.children)
.filter((element) => element.matches(TAB_SELECTOR));
if (!tabs.length) {
return;
}
tablistCounter += 1;
const tablistId = ensureUniqueId(tablist, `pill-tabs-${tablistCounter}`);
const panels = getPanelsForTablist(tablist, tabs.length);
const panelSet = new Set(panels);
const panelByIndex = [];
let activeIndex = getInitialActiveIndex(tabs);
tablist.removeAttribute('tabindex');
tabs.forEach((tab, index) => {
const tabId = ensureUniqueId(tab, `${tablistId}-tab-${index + 1}`);
let panel = null;
const existingControl = tab.getAttribute('aria-controls');
tab.dataset.pillTabIndex = `${index}`;
if (existingControl) {
const existingPanel = document.getElementById(existingControl);
if (existingPanel && panelSet.has(existingPanel)) {
panel = existingPanel;
}
}
if (!panel) {
panel = panels[index];
}
if (panel) {
const panelId = ensureUniqueId(panel, `${tablistId}-panel-${index + 1}`);
tab.setAttribute('aria-controls', panelId);
panel.setAttribute('aria-labelledby', tabId);
panel.setAttribute('tabindex', '-1');
panelByIndex[index] = panel;
} else {
tab.removeAttribute('aria-controls');
panelByIndex[index] = null;
}
});
function setActiveTab(nextIndex, focusTab = false) {
activeIndex = nextIndex;
tabs.forEach((tab, index) => {
const isActive = index === activeIndex;
const panel = panelByIndex[index];
tab.setAttribute('aria-selected', isActive ? 'true' : 'false');
tab.setAttribute('tabindex', isActive ? '0' : '-1');
tab.classList.toggle('is-active', isActive);
tab.setAttribute('data-state', isActive ? 'active' : 'inactive');
if (panel) {
panel.hidden = !isActive;
}
});
if (focusTab) {
tabs[activeIndex].focus();
}
}
tablist.addEventListener('click', (event) => {
const tab = getTabFromEventTarget(event.target, tablist);
if (!tab) {
return;
}
const nextIndex = Number(tab.dataset.pillTabIndex);
if (Number.isNaN(nextIndex)) {
return;
}
setActiveTab(nextIndex, true);
});
tablist.addEventListener('keydown', (event) => {
const tab = getTabFromEventTarget(event.target, tablist);
if (!tab) {
return;
}
const currentIndex = Number(tab.dataset.pillTabIndex);
const orientation = tablist.getAttribute('aria-orientation') === 'vertical' ? 'vertical' : 'horizontal';
let nextIndex = null;
if (Number.isNaN(currentIndex)) {
return;
}
if ((event.key === 'ArrowRight' && orientation === 'horizontal')
|| (event.key === 'ArrowDown' && orientation === 'vertical')) {
nextIndex = currentIndex + 1;
}
if (event.key === 'Home') {
nextIndex = 0;
}
if (event.key === 'End') {
nextIndex = tabs.length - 1;
}
if ((event.key === 'ArrowLeft' && orientation === 'horizontal')
|| (event.key === 'ArrowUp' && orientation === 'vertical')) {
nextIndex = currentIndex - 1;
}
if (nextIndex !== null) {
event.preventDefault();
setActiveTab(getWrappedIndex(nextIndex, tabs.length), true);
}
});
setActiveTab(activeIndex);
}
document.querySelectorAll(TABLIST_SELECTOR).forEach(setupTablist);
No notes defined.