<html> <head> <title>Pattern</title> </head> <body data-prismjs-copy-timeout="1500"> <h2>What is it</h2> <p>A tabs component that provides different sections of content that are displayed one at a time when the user selects that information. </p> <h2>When to use it</h2> <p>The tabbed user interface enables users to jump to their target section quickly. Tabs present like logically group information on the same page. Information should </p> <ul> <li>be logically chunked and ordered</li> <li>be arallel in nature</li> <li>show user's context</li> <li>obvious where they begin and end </li> </ul> <p>Users should not need to see content of multiple tabs simultaneously and the user should be able to easily recognise where they are within the content. </p> <h2>How to use it</h2> <p>The structure of the tab set is defined in html. There are two forms supported. Adding a class of <code class="inline">.tab-group</code> to the container element will work in place of the <code class="inline">tabset</code> tag, and the tab panels can be defined using either <code class="inline">tab=""</code> or <code class="inline">data-tab=""</code>. Passing an optional element to the init function will initialise tabs within that element. </p> <tabset id="tabs"> <pre class="language-html" tab="html"> <tabset id="uniqueID"> <div tab="[tab title]"></div> <div tab="[tab title]"></div> </tabset></pre> <pre class="language-pug" tab="pug">tabset#uniqueID div(tab="[tab title]") div(tab="[tab title]") </pre> <pre class="language-css" tab="css">tabset, .tab-group { margin: 2rem 0 1rem 0; } tabset > ul, .tab-group > ul { display: -webkit-box; display: -ms-flexbox; display: flex; margin: 0; padding: 0; } tabset > ul li.separator, .tab-group > ul li.separator { border-bottom: 1px solid #7f7f7f; border-left: 1px solid #7f7f7f; display: inline-block; margin: 0.45rem 0 0 0; width: 100%; } tabset .tab-hidden, .tab-group .tab-hidden { display: none; } tabset [role=tab], .tab-group [role=tab] { background-color: #FFF; border-left: 1px solid #7f7f7f; border-top: 1px solid #7f7f7f; border-radius: 0.5rem 0.5rem 0 0; cursor: pointer; margin: 0; display: inline; padding: 1rem 1.5rem 0.14rem 1.5rem; z-index: 2; } tabset [role=tab]:last-of-type, .tab-group [role=tab]:last-of-type { border-right: 1px solid #7f7f7f; } tabset [role=tab]:not(.selected), .tab-group [role=tab]:not(.selected) { background-color: #f0f0f0; border-bottom: 1px solid #7f7f7f; } tabset [role=tab] span, .tab-group [role=tab] span { display: block; margin: 0 0 0.5rem 0; } tabset [role=tabpanel], .tab-group [role=tabpanel] { background-color: #FFF; border: 1px solid #7f7f7f; border-top: none; padding: 1rem; z-index: 1; } tabset [role=tabpanel]:not(.open), .tab-group [role=tabpanel]:not(.open) { display: none; }</pre> <div tab="scss"> <pre class="language-sass">@import "scss/core/tabs/_tabs"; @include tabs{ // optional content block }; </pre> <pre class="language-sass">// DS2 core (c) 2024 Alexander McIlwraith // Licensed under CC BY-SA 4.0 $tab-border: #7f7f7f !default; $tab-selected: #FFF !default; $tab-notselected: #f0f0f0 !default; @mixin tabs { tabset, .tab-group { margin: 2rem 0 1rem 0; > ul { display: flex; margin: 0; padding: 0; li.separator { border-bottom: 1px solid $tab-border; border-left: 1px solid $tab-border; display: inline-block; margin: .45rem 0 0 0; width: 100%; } } .tab-hidden { display: none; } [role="tab"] { background-color: $tab-selected; border-left: 1px solid $tab-border; border-top: 1px solid $tab-border; border-radius: .5rem .5rem 0 0; cursor:pointer; margin: 0; display: inline; padding: 1rem 1.5rem .14rem 1.5rem; z-index: 2; &:last-of-type { border-right: 1px solid $tab-border; } &:not(.selected) { background-color: $tab-notselected; border-bottom: 1px solid $tab-border; } span { display: block; margin: 0 0 .5rem 0; } } [role="tabpanel"] { background-color: $tab-selected; border: 1px solid $tab-border; border-top: none; padding: 1rem; z-index: 1; &:not(.open) { display: none; } @content; } } }</pre> </div> <div tab="js"> <pre class="language-js">import * as tabs from "./js/core/tabs/_tabs.js"; tabs.init();</pre> <pre class="language-js">/* DS2 core (c) 2024 Alexander McIlwraith import * as tabs from "../pg/patterns/layouts/tabs/_tabs.js"; tabs.init(); */ export function init(p = document) { p.querySelectorAll(".tab-group, tabset").forEach(tabGroup => { if (tabGroup.querySelector("[role=tablist]") === null) { const tabgroup = tabGroup.getAttribute("id"); let tablist = ""; Array.from(tabGroup.children).forEach(child => { const tab = child.getAttribute("tab") || child.getAttribute("data-tab"); if (tab !== null) { const tabID = tab.replace(/\W+/g, "-").toLowerCase(); const tabPanel = document.createElement('div'); tabPanel.id = `tab-panel-${tabgroup}-${tabID}`; tabPanel.className = tablist === "" ? "open" : ""; tabPanel.setAttribute("role", "tabpanel"); tabPanel.setAttribute("tabindex", "0"); tabPanel.setAttribute("aria-labelledby", `tab-${tabgroup}-${tabID}`); tabPanel.appendChild(child.cloneNode(true)); child.parentNode.replaceChild(tabPanel, child); tablist += `<li tabindex="0" role="tab" ${tablist === "" ? "class='selected'" : ""} id="tab-${tabgroup}-${tabID}"><span>${tab}</span></li>`; } else { child.classList.add("tab-hidden"); } }); const ul = document.createElement('ul'); ul.setAttribute("role", "tablist"); ul.innerHTML = `${tablist}<li role="separator" class="separator"></li>`; tabGroup.insertBefore(ul, tabGroup.firstChild); tabGroup.querySelectorAll('[role="tab"]').forEach(tab => { tab.addEventListener("click", () => { const siblings = Array.from(tab.parentNode.children); siblings.forEach(sibling => sibling.classList.remove("selected")); tab.classList.add("selected"); const tabPanels = Array.from(tab.parentNode.parentNode.children) .filter(child => child.getAttribute("role") === "tabpanel"); tabPanels.forEach(panel => panel.classList.remove("open")); const tabPanelId = tab.getAttribute("id").replace("tab", "tab-panel"); document.getElementById(tabPanelId).classList.add("open"); }); }); } }); } </pre> </div> </tabset> </body> </html>