Jolty home Jolty Documentation

Tablist (accordion and tabs)

A universal component that allows creating accordions and tabs, without the need to specify unique ids, with accessibility support and keyboard navigation.

Accordion example

<div data-ui-tablist class="accordion">
  <div data-ui-tablist-item>
    <button data-ui-tablist-tab class="accordion-tab">
      Accordion button #1
      <svg
        xmlns="http://www.w3.org/2000/svg"
        width="24"
        height="24"
        viewBox="0 0 24 24"
        stroke-width="2.5"
        stroke="currentColor"
      >
        <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path>
      </svg>
    </button>
    <div data-ui-tablist-tabpanel data-ui-transition-name="collapse" hidden>
      <div class="accordion-panel">Accordion panel #1</div>
    </div>
  </div>
  <div data-ui-tablist-item>
    <button data-ui-tablist-tab class="accordion-tab">
      Accordion button #2
      <svg
        xmlns="http://www.w3.org/2000/svg"
        width="24"
        height="24"
        viewBox="0 0 24 24"
        stroke-width="2.5"
        stroke="currentColor"
      >
        <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path>
      </svg>
    </button>
    <div data-ui-tablist-tabpanel data-ui-transition-name="collapse" hidden>
      <div class="accordion-panel">Accordion panel #2</div>
    </div>
  </div>
  <div data-ui-tablist-item>
    <button data-ui-tablist-tab class="accordion-tab">
      Accordion button #3
      <svg
        xmlns="http://www.w3.org/2000/svg"
        width="24"
        height="24"
        viewBox="0 0 24 24"
        stroke-width="2.5"
        stroke="currentColor"
      >
        <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path>
      </svg>
    </button>
    <div data-ui-tablist-tabpanel data-ui-transition-name="collapse" hidden>
      <div class="accordion-panel">Accordion panel #3</div>
    </div>
  </div>
</div>

Tabs example

<div data-ui-tablist="ui-tabs" class="tabs">
  <button data-ui-tablist-tab="log-in" class="tabs-tab">Log in</button>
  <button data-ui-tablist-tab="sign-up" class="tabs-tab">Sign up</button>
</div>
<div id="log-in" class="tabs-tabpanel auth-form">
  <label class="field-wrapper">
    <span class="field-label">Email</span>
    <input type="email" class="field field--white" value="name@domain.com" />
  </label>
  <label class="field-wrapper">
    <span class="field-label">Password:</span>
    <input type="password" class="field field--white" value="password" />
  </label>
  <div class="auth-form-footer">
    <a href="#" class="auth-form-link">Forgot password</a>
    <button class="btn btn--md btn--white">Log in</button>
  </div>
</div>
<div id="sign-up" class="tabs-tabpanel auth-form" hidden>
  <label class="field-wrapper">
    <span class="field-label">Login or email</span>
    <input type="text" class="field field--white" value="name@domain.com" />
  </label>
  <label class="field-wrapper">
    <span class="field-label">Password:</span>
    <input type="password" class="field field--white" value="password" />
  </label>
  <div class="auth-form-footer">
    <a href="#" class="auth-form-link">I already have an account</a>
    <button class="btn btn--md btn--white">Sign up</button>
  </div>
</div>

Getting started

Let’s start by creating a simple accordion that, when clicking on a button, will open a new panel and close the previous one.

html
<div data-ui-tablist>
  <div data-ui-tablist-item>
    <button data-ui-tablist-tab>Accordion button #1 <span></span></button>
    <div data-ui-tablist-tabpanel hidden>Some content #1</div>
  </div>
  <div data-ui-tablist-item>
    <button data-ui-tablist-tab>Accordion button #2 <span></span></button>
    <div data-ui-tablist-tabpanel hidden>Some content #2</div>
  </div>
</div>

The [data-ui-tablist-item] element is optional; it is needed as a wrapper for tab and tabpanel, so that when a tab is opened, the .ui-active class is added to it.

Then, initialize all elements with the data-ui-tablist attribute.

js
import { Tablist } from "jolty";
Tablist.initAll();

If you want to make a gap between toggler and collapse, then an element or ::before pseudo-element should be added inside collapse.

css
.accordion-button::before {
  content: "";
  display: block;
  height: 0.5rem;
}

Also, let’s toggle the arrow’s state inside the tab using the CSS class .ui-active.

css
.accordion-button.ui-active span {
  scale: 1 -1;
}

Animation

To animate the tabpanel, you can use special CSS classes added during the entering and leaving state.

Since we are animating the height of the element, it is important to ensure that the tabpanel has overflow: hidden; property and doesn’t have padding.

If we want to use padding, then this element needs to be placed inside tabpanel.

html
<div data-ui-tablist-tabpanel class="accordion-panel" hidden>
  <div class="accordion-panel-inner">...</div>
</div>
css
.accordion-panel-inner {
  padding: 1.25rem;
}

Also, if you want to make a gap between tab and tabpanel, then an element or ::before pseudo-element should be added inside tabpanel.

css
.accordion-panel::before {
  content: "";
  display: block;
  height: 0.25rem;
}

Animation can be created in many ways, here are just a few examples:

<div data-ui-tablist-tabpanel class="accordion-panel" hidden>...</div>
 
<style>
  .accordion-panel.ui-enter-active,
  .accordion-panel.ui-leave-active {
    transition-duration: 200ms;
    transition-property: height, opacity;
    transition-timing-function: ease-in-out;
    overflow: hidden;
  }
  .accordion-panel.ui-enter-to,
  .accordion-panel.ui-leave-from {
    height: var(--ui-transition-height);
  }
  .accordion-panel.ui-enter-from,
  .accordion-panel.ui-leave-to {
    height: 0;
    opacity: 0;
  }
</style>

Hash navigation

If you want the tabpanel to open when the URL contains a hash with its id, you can assign it a unique id, and for tab set the data-ui-tablist-tab="{id}" attribute.

If we link tab with tabpanel through a unique id, specifying the data-ui-tablist-tabpanel attribute is unnecessary.

html
<div data-ui-tablist>
  <div data-ui-tablist-item>
    <button data-ui-tablist-tab="my-tabpanel">Tab #1 <span></span></button>
    <div id="my-tabpanel" hidden>Some content #1</div>
  </div>
  ...
</div>

Tabs

By default, Tablist has accordion settings; to change them to tabs, you need to add the value 'ui-tabs' to the data-ui-tablist attribute or set the data:'ui-tabs' option.

html
<div data-ui-tablist="ui-tabs">
  <button data-ui-tablist-tab="tabpanel-1">Tab #1</button>
  <button data-ui-tablist-tab="tabpanel-2">Tab #2</button>
  <button data-ui-tablist-tab="tabpanel-3">Tab #2</button>
</div>
<div id="tabpanel-1">Tabpanel with id:</div>
<div id="tabpanel-2" hidden>Tabpanel with id:</div>
<div id="tabpanel-3" hidden>Tabpanel with id:</div>
Tabpanel with id: 

Below are two sets of settings; by default, Tablist has settings from the 'ui-accordion' list:

Defaults for the 'ui-tabs' value:

js
{
  alwaysExpanded: true,
  horizontal: true,
  arrowActivation: true,
  awaitPrevious: true,
  a11y: 'tabs'
}

Defaults for the 'ui-accordion' value:

js
{
  alwaysExpanded: false,
  horizontal: false,
  arrowActivation: false,
  awaitPrevious: false,
  a11y: 'accordion'
}

Data

But what if we do not want to specify unique ids or want to use the same settings for other tablists? There is a solution for this - use the static method Tablist.data().

html
<div data-ui-tablist="tabs">
  <button data-ui-tablist-tab>Tab #1</button>
  <button data-ui-tablist-tab>Tab #2</button>
  <button data-ui-tablist-tab>Tab #3</button>
</div>
<div>
  <div>Tabpanel with id:</div>
  <div hidden>Tabpanel with id:</div>
  <div hidden>Tabpanel with id:</div>
</div>
js
Tablist.data("tabs", (tablistElem) => {
  return {
    data: "ui-tabs",
    tabpanel: tablistElem.nextElementSibling.children,
  };
});
Tablist.initAll();

We also use data: "ui-tabs" to import the default settings for tabs.

Tabpanel with id: 

Options

NameTypeDefaultDescription
initBooleantrueShould the instance be automatically initialized when an instance is created? If false, you’ll need to manually initiate it by calling tablist.init().
destroyBooleanfalseAllows you to destroy an instance at a specific breakpoint.
dataString''Allows you to use the default options that have been added through the Tablist.data() static method. Attribute: data-ui-tablist
onObjectnullUsed to register event handlers.
appearBooleannullIf you want to apply a transition upon initialization as well, you can set this option to true. Attribute: data-ui-appear
eventPrefixString'ui-tablist:'Prefix for events dispatched on the tablist element.
eventDispatchBoolean, EventName[]trueDefines if events are dispatched or used only within options.
eventBubbleBoolean, EventName[]trueDefines if events should bubble up the DOM.
breakpointsObjectnullDefines custom options for specific breakpoints.
shownid, index, id[], index[], functionnullAccepts id, index or an array of them, there is also an option to set function shown: (Tab) => Boolean.
siblingsBooleantrueShould the tab element look for its tabpanel among the following elements?
alwaysExpandedBooleanfalseShould at least one tab always be open? Attribute: data-ui-always-expanded
multiExpandBooleanfalseCan more than one tab be opened at the same time? Attribute: data-ui-multi-expand
awaitAnimationBooleanfalseCan the tab state be switched if the tablist is in the process of animation?
awaitPreviousBooleanfalseShould the next tab wait for the previous animation to end?
keyboardBooleantrueEnables tabs field via arrow keys.
arrowActivationBooleanfalseShould the tab be turned on as soon as it was focused?
rtlBooleanfalseCreates inversion in switching tabs through the keyboard.
focusFilterFunctionnullDetermines whether the tab element can be focused from the keyboard or not. Accepts a function: (Tab) => Boolean.
horizontalBooleanfalseDetermines in which direction the keyboard field is performed. Attribute: data-ui-horizontal
tabCSSSelector, Element[], Function'[data-ui-tablist-tab]'Defines a selector for searching the tab, also accepts a list of tabs in an array or a function tab: (Tablist) => Element[] or CSSSelector.
itemCSSSelector, Function'[data-ui-tablist-item]'Defines a selector to search for item among the parents of the tab, also accepts a list of HTMLElements or a function: ({tablist, tab, index}) => Element.
tabpanelCSSSelector, Function, Element[]'[data-ui-tablist-tabpanel]'Defines a selector for searching for item elemen among the parents of tab, also accepts a function tabpanel: ({tablist, tab, index}) => Element.
tabClassActiveString'ui-active'CSS class for the tab element when it’s expanded.
itemClassActiveString'ui-active'CSS class for the item element when it’s expanded.
tabpanelClassActiveString'ui-active'CSS class for the tabpanel element when it’s expanded.
hashNavigationBooleantrueDefines whether to show the tab during initialization if the URL hash is equal to the id. Attribute: data-ui-hash-navigation
dismissBoolean, CSSSelectorfalseAllows a tabpanel to be hide when clicked on the button. By default, it’s true, meaning the hide() method will be called when '[data-ui-dismiss=""],[data-ui-dismiss="tablist"]' is clicked.
hideModeString'hidden'Accepts one of the next values 'hidden','hidden-until-found','inert','class-hidden','class-shown','remove' Attribute: data-ui-hide-mode
keepPlaceBooleantrueDetermines if the tabpanels space is preserved when removed by hideMode: 'remove'.

Hide mode

The tabpanel can be hidden in several ways using the hideMode option.

ModeHidden stateDescription
'hidden'[hidden]Default mode, hides through display: none;
'hidden-until-found'[hidden="until-found"]The element will show when a match is found in the site search. Read more
'inert'[inert]Makes the element non-interactive, but remains visible
'class-hidden'.ui-hiddenThe element remains fully interactive,
adds the .ui-hidden class when hidden
'class-shown'-The element remains fully interactive,
adds the .ui-shown class when shown
'remove'-The element is removed from the DOM.
Add the hidden attribute to hide by default

Transition

AttributeTypeDefaultDescription
transitionString, Boolean, TransitionOptionstrueObject with transition options or string to set the name option for the transition.
nameString'ui'Defines the name of the transition that will be used to apply CSS rules. Attribute: data-ui-transition-name
cssBooleantrueDefines if the transition should be applied using CSS rules.
cssVariablesBooleantrueAdds special CSS variables during the transition to the tabpanel element and removes them after completion.
enterFunctionnullAccepts a function enter(el, done){}. call the done() callback to indicate transition end
enterActiveString, Object''CSS classes or styles applied during the entire entering phase. Attribute: data-ui-enter-active
enterFromString, Object''CSS classes or styles applied at the start of the entering phase. Attribute: data-ui-enter-from
enterToString, Object''CSS classes or styles applied at the end of the entering phase. Attribute: data-ui-enter-to
leaveFunctionnullAccepts a function leave(el, done){}. call the done() callback to indicate transition end
leaveActiveString, Object''CSS classes or styles applied during the entire leaving phase. Attribute: data-ui-leave-active
leaveFromString, Object''CSS classes or styles applied at the start of the leaving phase. Attribute: data-ui-leave-from
leaveToString, Object''CSS classes or styles applied at the end of the leaving phase. Attribute: data-ui-leave-to
durationNumbernullDefines the duration of the transition. Should be used only when the animation occurs on child elements.

If the cssVariables option is enabled, special CSS variables are added during the transition to tabpanel and are removed after its completion.

NameTypeDescription
--ui-transition-widthNumberThe width of the tabpanel element at the start of the transition.
--ui-transition-heightNumberThe height of the tabpanel element at the start of the transition.

Teleport

AttributeTypeDefaultDescription
teleportCSSSelector, Element, TeleportOptionsnullObject with TeleportOptions or CSSSelector, Element to set the to option for the teleport. Attribute: data-ui-teleport
toCSSSelector, ElementnullDefines where to move the tabpanel element.
positionString'beforeend'Defines the position for the collapse element once it has been moved. Accepts one of the next values 'beforebegin','afterbegin','beforeend' or 'afterend'
keepPlaceBooleanfalseDefines if the tabpanel element keeps its original place after being destroyed.

Accessibility (a11y)

NameTypeDefaultDescription
a11yBoolean, String, A11yOptions'accordion'Object with A11yOptions or true to enable with default options. You can also use one of the following values: 'accordion' or 'tabs'.
roleStringnullAdds the role="tablist" attribute to the tablist element.
tabRoleString'button'Adds the role="tab" attribute to the tab element.
tabpanelRoleString'region'Adds the role="tabpanel" attribute to the tabpanel element.
ariaOrientationBooleantrueAdds the aria-orientation attribute to the tablist element.
stateAttributeString'aria-expanded'You can chose which attribute will be used to indicate the state of the tab.
tabindexBooleanfalseAdds the tabindex attribute to the tab element.
tabpanelTabindexBooleanfalseAdds the tabindex="0" attribute to the tabpanel element.

Defaults for the a11y:'accordion' value:

{
  role: null,
  tabRole: 'button',
  tabpanelRole: "region",
  ariaOrientation: true,
  stateAttribute: 'aria-expanded',
  tabindex: false,
  tabpanelTabindex: false,
}

Defaults for the a11y:'tabs' value:

{
  role: 'tablist',
  tabRole: 'tab',
  tabpanelRole: "tabpanel",
  ariaOrientation: true,
  stateAttribute: 'aria-selected',
  tabindex: true,
  tabpanelTabindex: true,
}

Methods

NameReturnDescription
init(options)instanceInitializes the component.
destroy(destroyOptions)instanceDestroys the component. Accepts an object as options { remove: false, keepInstance: false, keepState: false }.
update(options)instanceAccepts options as an argument and updates the component.
toggle(tab,toggleOptions,force)promiseToggles the tabs’s visibility state between shown and hidden. Accepts true or false as params which sets the animated option or an object { animated: true, silent: false }.
show(tab,toggleOptions)promiseShows the tab. Accepts the same options as toggle().
hide(tab,toggleOptions)promiseHides the tab. Accepts the same options as toggle().
initTabs()tabInstanceSearches for all elements specified in the tab option and initializes them.
initTab(Element)tabInstanceAccepts a tab element and initializes it.
getTab(Element)tabInstanceIn the list of initiated tabs, finds a tab by the HTMLElement, CSSSelector or index.

Tab Instance Methods

NameReturnDescription
destroy(destroyOptions)instance, nullDestroys the tab. Accepts an object { cleanStyles: true, remove: false, keepState: false } where clean option removes all generated attributes and classes, and the remove removes the tab from the DOM.
toggle(tab,toggleOptions,force)promiseToggles the tab’s visibility state between shown and hidden. Toggles the tabs’s visibility state between shown and hidden. Accepts true or false as params which sets the animated option or an object { animated: true, silent: false }.
show(tab, toggleOptions)promiseShows the tab. Accepts the same options as toggle() method.
hide(tab, toggleOptions)promiseHides the tab. Accepts the same options as toggle() method.
toggleDisabled(force)instanceToggle disable attribute on tab.

Class Methods

NameReturnDescription
data(name?, callback)ClassAllows you to set default options for components that have the property data:'name' or through the attribute data-tablist="name".
updateDefault(options)defaultUpdates default options.
initAll(root)instanceSearches for elements in root, which defaults to document with the attribute data-ui-tablist and initializes them.
get(id or elem)instanceSearches for an instance by id or element (checks the base property).
getOrCreate(id or elem, options)instanceSearches for an instance by id or element (checks the base property), if not found - creates a new instance with specified options.
init(options)instanceInitializes the component.
destroy(destroyOptions)instanceDestroys the component. Accepts an object as options { remove: false, keepInstance: false, keepState: false }.
update(options)instanceAccepts options as an argument and updates the component.
toggle(tab,toggleOptions,force)promiseToggles the tabs’s visibility state between shown and hidden. Accepts true or false as params which sets the animated option or an object { animated: true, silent: false }.
show(tab,toggleOptions)promiseShows the tab. Accepts the same options as toggle().
hide(tab,toggleOptions)promiseHides the tab. Accepts the same options as toggle().
initTabs()tabInstanceSearches for all elements specified in the tab option and initializes them.
initTab(Element)tabInstanceAccepts a tab element and initializes it.
getTab(Element)tabInstanceIn the list of initiated tabs, finds a tab by the HTMLElement, CSSSelector or index.

Properties

NameTypeDescription
idStringThe id of the base element.
isInitBooleanIndicates whether the instance is already initialized.
optsObjectContains the currently applied options for the current breakpoint.
baseOptsObjectContains all options, including the breakpoints property.
base, tablistElementAll components have a base property, which refers to the element through which the component is initialized and where events are fired. tablist is a synonym for base.
lastShownTabtabInstanceThe last tab that was shown.
tabstabInstance[]Returns an array of all tabs.
shownTabstabInstance[]Returns an array of all shown tabs.

Tab Instance Properties

NameTypeDescription
idStringThe id of the tabpanel element.
tabElementReference to the tab element.
itemElementReference to the item element.
tabpanelElementReference to the tabpanel element.
indexNumberThe order number of the tab.
isOpenBooleanIndicates whether the tab is currently shown.
isDisabledBooleanIndicates whether the tab is currently disabled.
initialPlaceNodeNodeReference to the node that is in the place of the tabpanel element before it was moved or removed.

Class Properties

NameTypeDescription
DefaultObjectContains the default options for the component.
DefaultA11yObjectContains the default options for the a11y option.
instancesMapA Map that contains all instances of the Tablist class.

Events

By default, events that are listened to directly through an element have the prefix 'ui-tablist:'. This can be changed through the eventPrefix property. Additionally, you can disable the bubbling of these events using eventBubble or turn them off entirely via eventDispatch.

Tablist.data("my-tablist", {
  on: {
    shown(instance, tabInstance, { trigger, event }) {
      // do something...
    },
    any(eventName, instance, tabInstance, { trigger, event }) {
      if (eventName === "shown") {
        // do something...
      }
    },
  },
});
 
// or directly on the tablist element
const tablistElem = document.querySelector(".my-tablist");
tablistElem.addEventListener("ui-tablist:show", (e) => {
  const [instance, tabInstance, { trigger, event }] = e.detail;
  // do something...
});
 
// or on the document
document.addEventListener("ui-tablist:show", (e) => {
  const [instance, tabInstance, { trigger, event }] = e.detail;
  // do something...
});
NameArgumentsDescription
beforeInitinstanceEvent will fired right before initialization.
initinstanceFires when initialization has been completed.
beforeShowinstance, tabInstance, {trigger, event}Fires immediately when the show() method is called.
showinstance, tabInstance, {trigger, event}Fires when the element becomes visible, but the CSS transition hasn’t started yet.
showninstance, tabInstance, {trigger, event}Fires when the CSS transition hasn’t been completed.
beforeHideinstance, tabInstance, {trigger, event}Fires immediately when the hide() method is called.
hideinstance, tabInstance, {trigger, event}Fires just before the CSS transition starts.
hiddeninstance, tabInstance, {trigger, event}Fires when the CSS transition has been completed.
beforeDestroyinstanceFires before the instance is destroyed.
destroyinstanceFires immediately when the destroy() method is called.
breakpointinstance, breakpoint, prevBreakpointFires when the breakpoint has been changed.
anyeventName, instanceFires on any event occurrence. The first argument contains the name of the event.
2023 © A Project by Jolty Labs 🇪🇸 🇺🇦