Now.js Framework Documentation

Now.js Framework Documentation

TabsComponent

EN 15 Dec 2025 08:10

TabsComponent

Overview

TabsComponent is a component for tabs/tab panels in Now.js Framework. It supports ARIA accessibility and keyboard navigation.

When to use:

  • Need tab navigation
  • Need to switch between panels
  • Need tabbed content
  • Need accessible tabs

Why use it:

  • ✅ Full ARIA accessibility
  • ✅ Keyboard navigation (Arrow keys, Home, End)
  • ✅ Declarative HTML setup
  • ✅ Animation support
  • ✅ URL hash sync
  • ✅ Events and callbacks

Basic Usage

HTML Declarative

<div data-component="tabs">
  <!-- Tab buttons -->
  <div data-tabs-list>
    <button data-tab-button="home">Home</button>
    <button data-tab-button="profile">Profile</button>
    <button data-tab-button="settings">Settings</button>
  </div>

  <!-- Tab panels -->
  <div data-tab-panel="home">
    <h2>Home Content</h2>
    <p>Welcome to the home tab!</p>
  </div>
  <div data-tab-panel="profile">
    <h2>Profile Content</h2>
    <p>Your profile information.</p>
  </div>
  <div data-tab-panel="settings">
    <h2>Settings Content</h2>
    <p>Application settings.</p>
  </div>
</div>

With Default Tab

<div data-component="tabs" data-default-tab="profile">
  <div data-tabs-list>
    <button data-tab-button="home">Home</button>
    <button data-tab-button="profile">Profile</button>
  </div>
  <div data-tab-panel="home">...</div>
  <div data-tab-panel="profile">...</div>
</div>

JavaScript API

const tabs = TabsComponent.create(element, {
  defaultTab: 'home',
  keyboard: true,
  onTabChange: (tabId) => {
    console.log('Switched to:', tabId);
  }
});

// Switch tab programmatically
tabs.switchTab('profile');

// Get active tab
const activeId = tabs.getActiveTab();

Data Attributes

Attribute Description
data-component="tabs" Initialize tabs container
data-tabs-list Container for tab buttons
data-tab-button="id" Tab button with unique ID
data-tab-panel="id" Tab panel matching button ID
data-default-tab="id" Default active tab
data-keyboard="true/false" Enable keyboard navigation
data-animation="true/false" Enable animations

Options

TabsComponent.create(element, {
  // Default active tab
  defaultTab: null,  // null = first tab

  // Keyboard navigation
  keyboard: true,

  // Animation
  animation: true,
  animationDuration: 200,

  // URL hash sync
  hashNavigation: false,

  // Callbacks
  onTabChange: (tabId, previousId) => {},
  onInit: (instance) => {}
});

API Reference

TabsComponent.create(element, options)

Create tabs instance

Parameter Type Description
element HTMLElement Tabs container
options object Configuration

Returns: Object - Instance with methods

instance.switchTab(tabId)

Switch tab

Parameter Type Description
tabId string Tab ID
tabs.switchTab('settings');

instance.getActiveTab()

Get active tab ID

Returns: string

instance.refresh()

Refresh tabs (rescan DOM)

instance.destroy()

Destroy instance

TabsComponent.getInstance(element)

Get instance from element

Keyboard Navigation

Key Action
/ Next tab
/ Previous tab
Home First tab
End Last tab
Enter / Space Activate focused tab

Events

Event When Triggered Detail
tabs:change Tab changed {tabId, previousId}
tabs:init Initialize complete {instance}
element.addEventListener('tabs:change', (e) => {
  console.log(`Changed from ${e.detail.previousId} to ${e.detail.tabId}`);
});

CSS Styling

/* Tab list */
[data-tabs-list] {
  display: flex;
  gap: 4px;
  border-bottom: 2px solid #e5e7eb;
}

/* Tab buttons */
[data-tab-button] {
  padding: 12px 24px;
  border: none;
  background: transparent;
  cursor: pointer;
  color: #6b7280;
  border-bottom: 2px solid transparent;
  margin-bottom: -2px;
  transition: all 0.2s;
}

[data-tab-button]:hover {
  color: #3b82f6;
}

[data-tab-button][aria-selected="true"] {
  color: #3b82f6;
  border-bottom-color: #3b82f6;
  font-weight: 500;
}

[data-tab-button]:focus {
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
}

/* Tab panels */
[data-tab-panel] {
  padding: 24px;
}

[data-tab-panel][hidden] {
  display: none;
}

/* Animation */
[data-tab-panel]:not([hidden]) {
  animation: tab-fade-in 0.2s ease-out;
}

@keyframes tab-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

Real-World Examples

Settings Tabs

<div data-component="tabs" class="settings-tabs">
  <div data-tabs-list>
    <button data-tab-button="general">
      <svg>...</svg> General
    </button>
    <button data-tab-button="security">
      <svg>...</svg> Security
    </button>
    <button data-tab-button="notifications">
      <svg>...</svg> Notifications
    </button>
  </div>

  <div data-tab-panel="general">
    <form>
      <!-- General settings form -->
    </form>
  </div>

  <div data-tab-panel="security">
    <form>
      <!-- Security settings form -->
    </form>
  </div>

  <div data-tab-panel="notifications">
    <form>
      <!-- Notification settings form -->
    </form>
  </div>
</div>

Dynamic Tab Content

<div data-component="tabs" id="product-tabs">
  <div data-tabs-list>
    <button data-tab-button="description">Description</button>
    <button data-tab-button="specs">Specifications</button>
    <button data-tab-button="reviews">Reviews</button>
  </div>

  <div data-tab-panel="description" data-src="/api/products/1/description">
    Loading...
  </div>
  <div data-tab-panel="specs" data-src="/api/products/1/specs">
    Loading...
  </div>
  <div data-tab-panel="reviews" data-src="/api/products/1/reviews">
    Loading...
  </div>
</div>

<script>
const tabs = TabsComponent.getInstance(document.getElementById('product-tabs'));

document.getElementById('product-tabs').addEventListener('tabs:change', async (e) => {
  const panel = document.querySelector(`[data-tab-panel="${e.detail.tabId}"]`);
  const src = panel.dataset.src;

  if (src && !panel.dataset.loaded) {
    const response = await fetch(src);
    panel.innerHTML = await response.text();
    panel.dataset.loaded = 'true';
  }
});
</script>

Common Pitfalls

⚠️ 1. Button ID Must Match Panel ID

<!-- ❌ IDs don't match -->
<button data-tab-button="tab1">Tab 1</button>
<div data-tab-panel="tab-1">...</div>

<!-- ✅ IDs match -->
<button data-tab-button="tab1">Tab 1</button>
<div data-tab-panel="tab1">...</div>

⚠️ 2. Must Have data-tabs-list

<!-- ❌ Missing data-tabs-list -->
<div data-component="tabs">
  <button data-tab-button="a">A</button>
</div>

<!-- ✅ Has data-tabs-list -->
<div data-component="tabs">
  <div data-tabs-list>
    <button data-tab-button="a">A</button>
  </div>
</div>