Sadrazam – Tabs Showcase

Four visual variants sharing one JS module, with data-attribute configuration

How It Works

The Tabs module is initialized via Sadrazam.Tabs.listen() which scans for all tab heading containers.

Attribute / Selector Description
div[class*="tab-classic__heading"] Wrapper element containing all tab heads. The JS auto-discovers these.
data-tab-id Placed on each tab head. Its value must match the id of the corresponding panel.
id="tab-{name}" The content panel ID. Must match the data-tab-id value on the head.
.is-default (class) Marks which tab opens by default. Fallback when no hash or session is found. If omitted, the first tab is used.
data-tab-hash Custom hash value. Selecting the tab sets the URL to #value. On page load, a matching hash restores that tab.
data-tab-target Activates a tab from outside the tab group and scrolls to it.
data-tab-persist On heading container. Enables sessionStorage persistence. Without it, the active tab is not remembered across page loads.

Tab Resolution Priority

On initialization, the active tab is determined by the first match in this order:

  1. URL hash → data-tab-hash — Hash matches a tab's data-tab-hash value.
  2. URL hash → element-in-panel — Hash matches an element id inside a tab panel. That tab activates and the element is scrolled into view with a flash effect.
  3. sessionStorage (only if data-tab-persist) — Previously selected tab restored from sdrzm-tab:{pathname}:{groupIndex}.
  4. .is-default — Tab head with the .is-default class.
  5. First tab — Falls back to the first tab head.

tab-classic — Classic Tab

Top-bordered heads, active tab removes bottom border to merge with panel. Container: .tab-classic__container → Heading: .tab-classic__heading → Head: .tab-classic__head → Body: .tab-classic__body → Panel: .tab-classic__panel

  Overview
  Features
  Specifications
This is the Overview panel for V1 classic tabs. The classic variant uses a top border on each head and hides the bottom border on the active tab, creating a visual merge between the head and the panel below.
Features panel content. The heading uses flex-wrap: nowrap with horizontal overflow scroll, making it suitable for narrow viewports with many tabs.
Specifications panel content. Each .tab-classic__head uses flex-grow: 1 so tabs distribute evenly.

tab-card — Border + Underline Accent

Bordered heads with a colored bottom accent line on the active tab. Wraps on smaller screens, no-wrap on XL. Container: .tab-card__container → Heading: .tab-card__heading → Head: .tab-card__head → Body: .tab-card__body → Panel: .tab-card__panel

  Dashboard
  Analytics
  Reports
  Settings
Dashboard panel for V2. This variant has a 3px solid primary-colored bottom border on the active head, and uses flex-wrap: wrap on small screens, switching to nowrap at the XL breakpoint.
Analytics panel. Heads use text-overflow: ellipsis to gracefully handle long labels. Background switches from light gray to white on activation.
Reports panel. The body has a margin-top: 7px gap on small screens that collapses to 0px on XL to achieve a seamless connection.
Settings panel. Heads have a border-radius on top corners and flat bottom corners, giving them a folder-tab appearance.

tab-scroll — Horizontal Scroll + Detached Body

Similar to V2 but with horizontal scroll, a margin gap between heading and body, and wrap at LG breakpoint. Container: .tab-scroll__container → Heading: .tab-scroll__heading → Head: .tab-scroll__head → Body: .tab-scroll__body → Panel: .tab-scroll__panel

  Products
  Orders
  Customers
  Inventory
  Shipping
Products panel for V3. The heading scrolls horizontally on small screens (overflow: auto) and wraps at the LG breakpoint. Notice the margin-bottom on the heading that visually separates it from the body panel.
Orders panel. V3 heads also get a 3px solid primary bottom border when active, just like V2, maintaining visual consistency.
Customers panel. Try resizing the window to see the horizontal scroll behavior on narrow viewports.
Inventory panel. The detached body design is ideal for layouts where the tab headings and content need visual separation.
Shipping panel. With five tabs, this example demonstrates how horizontal scroll accommodates many items without wrapping.

tab-capsule — Capsule / Segment

Modern segment-control style. Active tab gets a filled primary background with shadow. Hover shows primary color text. Container: .tab-capsule__container → Heading: .tab-capsule__heading → Head: .tab-capsule__head → Body: .tab-capsule__body → Panel: .tab-capsule__panel

  Monthly
  Yearly
  Lifetime
Monthly plan panel. V4 uses a pill/segment design: the heading has a light gray background (border-radius: var(--radius-md)), and the active head gets a primary-colored fill with box-shadow and white text. Transition on background-color and color is animated via var(--ease-fast).
Yearly plan panel. Heads use text-align: center and flex-grow: 1 for equal width distribution, making it ideal for pricing toggles or segmented controls.
Lifetime plan panel. The panel has a bordered, rounded container with a subtle background (.tab-capsule__panel).

tab-capsule — Neutral Panel Variant

Uses .tab-capsule__panel--neutral for a borderless, background-free panel. Useful when the content itself provides its own container styling.

All Items
Active
Archived
All Items — This panel uses .tab-capsule__panel--neutral, which has no border, no background, and no padding. The content floats freely beneath the pill heading.
Active items panel. The neutral variant is ideal for cases where the panel content contains cards or other already-styled elements.
Archived items panel. Compare with the bordered .tab-capsule__panel in the section above.

Default Tab Override

By default, the first tab is active. Add .is-default to a tab head to override this. Unlike .is-active, this class has no CSS styling and causes no visual flash on page load.

First Tab
Second Tab (default)
Third Tab
This is the first tab. It is not the default.
This is the second tab. It opens by default because the .is-default class is on its head element.
This is the third tab.

URL Hash Integration

Add data-tab-hash to each tab head. When selected, the browser URL updates to #value via history.replaceState() (no history pollution). Refreshing the page restores the active tab from the hash. In-page links like <a href="#security"> also activate the matching tab. Selecting the first tab clears the hash from the URL.

  General
  Security
  Notifications
General settings. Click each tab and watch the URL hash change in your browser address bar. The value comes from the data-tab-hash attribute: #general, #security, #notifications.
Security settings. Copy the URL with the hash and paste it in a new tab to see the correct panel restored automatically via #determineInitialTab().
Notifications settings. The hash update uses history.replaceState() so it does not pollute browser history or trigger a page reload.

Element-in-Panel Deep Linking

When the URL hash matches an element id inside a tab panel, that tab is automatically activated and the page scrolls to the element with a flash effect. This also works when clicking in-page <a href="#element-id"> links.

Try it Jump to Target Item — clicks <a href="#deep-target-item">, activates the correct tab, scrolls and flashes the element.
Item List
Details
This is the Item List panel. The target element is in the Details tab.

Some content before the target...

Target Item — This element has id="deep-target-item". When you click the link above or visit the page with #deep-target-item in the URL, this tab opens and the element receives a flash highlight via Elem.flash().

External Trigger

Buttons outside the tab group can activate a specific tab using data-tab-target. The panel is automatically scrolled into view.

Trigger Buttons
Red
Green
Blue
Red panel activated. This can be triggered by clicking the tab head above or the external "Show Red" button. The external button uses data-tab-target="tab-ext-red".
Green panel activated. The data-tab-target attribute on the external button activates this tab and scrolls it into view via Elem.scrollToView().
Blue panel activated. External triggers are useful for sidebar navigation, call-to-action buttons, or cross-component communication.

tab-capsule — Inner Panel Variant

The .tab-capsule__panel--inner class creates bordered, rounded sections inside a neutral panel. Stacked inner panels get automatic top margin via the adjacent sibling selector .tab-capsule__panel--inner + .tab-capsule__panel--inner.

  Profile
  Billing
Personal Information

Name, email, and avatar settings. This is the first .tab-capsule__panel--inner block.

Preferences

Language, timezone, and theme preferences. This second inner panel receives automatic spacing from the adjacent sibling rule.

Payment Method

Credit card and billing address configuration.

Invoice History

Past invoices and receipts displayed here.

Session Persistence

Session persistence is opt-in. Add data-tab-persist to the heading container to enable it. When enabled, the active tab is saved to sessionStorage on every tab change. When the page reloads, the previously active tab is restored — unless a URL hash takes higher priority. The storage key format is sdrzm-tab:{pathname}:{groupIndex}, so each tab group on each page has its own independent memory.

Without data-tab-persist, the tab group always starts from the hash, .is-default, or the first tab. Session data persists until the browser tab is closed.

Accessibility

Tabs follow the WAI-ARIA Tabs pattern for screen readers and keyboard navigation. All ARIA attributes are applied automatically on initialization and removed on destroy().

Feature Details
ARIA roles role="tablist" on heading container, role="tab" on each head, role="tabpanel" on each panel.
ARIA attributes aria-selected and aria-controls on tab heads; aria-labelledby on panels.
Focus management Active tab: tabindex="0". Inactive tabs: tabindex="-1". Only the active tab is in the natural tab order.
Keyboard ArrowRight / ArrowDown — next tab (cycles).
ArrowLeft / ArrowUp — previous tab (cycles).
Home — first tab.
End — last tab.
Cleanup destroy() removes all ARIA attributes, roles, and keyboard listeners.

CSS Class Reference

All four variants share the same JS module. Only the CSS class prefix changes.

Variant Prefix Style Key Feature
Classic .tab-classic__* Classic Top border, active removes bottom border
Card .tab-card__* Border + underline accent Responsive wrap, 3px bottom accent on active
Scroll .tab-scroll__* Horizontal scroll Scrollable heading, detached body with margin gap
Capsule .tab-capsule__* Capsule / segment Filled active background, transition animation, shadow

V4 Panel Types .tab-capsule__panel (bordered)   .tab-capsule__panel--neutral (no styles)   .tab-capsule__panel--inner (sub-section card)

JS API Reference

Available via the global Sadrazam.Tabs object.

Initialize Sadrazam.Tabs.listen() Scans the DOM for all tab heading containers and binds events.
Help Sadrazam.Tabs.help() Prints all available config attributes to the console.
getInstance() Sadrazam.Tabs.getInstance(element) Returns the Tabs instance from a tab head or container element.
Activate instance.activateTab(tabHeadElement) Programmatically activate a tab. Access instance via element.__tabs.
destroy() instance.destroy() Removes all click handlers and clears instance references from DOM elements.