Now.js Framework Documentation
TabsComponent - Tabs Component for Now.js
TabsComponent - Tabs Component for Now.js
This document describes TabsComponent, a production-ready tabs component with full ARIA accessibility support and keyboard navigation.
📋 Table of Contents
- Overview
- Installation and Import
- Basic Usage
- HTML Attributes
- Configuration Options
- Keyboard Navigation
- Accessibility
- Events
- JavaScript API
- Usage Examples
- API Reference
- Best Practices
- Common Pitfalls
Overview
TabsComponent is a production-ready tabs component with complete ARIA support and keyboard navigation capabilities.
Key Features
- ✅ Auto-initialization: Configure via
data-component="tabs"instantly - ✅ Full ARIA Support: Complete attributes for screen readers
- ✅ Keyboard Navigation: Supports Arrow keys, Home, End
- ✅ Data Attribute Configuration: Set all options via HTML
- ✅ Event Callbacks: Lifecycle event callbacks
- ✅ Animations: Transition animation support
- ✅ Dynamic Tab Management: Add/remove tabs in real-time
- ✅ Horizontal and Vertical: Choose display orientation
When to Use TabsComponent
✅ Use TabsComponent when:
- You need to organize content into switchable categories
- You want to save screen space by hiding some content
- You need users to view data from different perspectives
- You require fully accessible UI
- You need tabs that work on both desktop and mobile
❌ Don't use TabsComponent when:
- You have minimal content that fits on one page
- You want users to see all content simultaneously (use accordion instead)
- You have sequential steps (use stepper/wizard instead)
Installation and Import
TabsComponent is loaded with Now.js Framework and ready to use via window object:
// No import needed - ready to use
console.log(window.TabsComponent); // TabsComponent objectDependencies
- Now.js Core – Main framework
- CSS (tabs.css) – Tab styles (imported via main.css)
Basic Usage
1. HTML Usage (Recommended)
<div data-component="tabs">
<div class="tab-buttons" role="tablist">
<button class="tab-button" data-tab="overview" role="tab">Overview</button>
<button class="tab-button" data-tab="features" role="tab">Features</button>
<button class="tab-button" data-tab="usage" role="tab">Usage</button>
</div>
<div class="tab-content">
<div class="tab-pane" data-tab="overview" role="tabpanel">
<h3>Overview</h3>
<p>Overview content...</p>
</div>
<div class="tab-pane" data-tab="features" role="tabpanel">
<h3>Features</h3>
<p>Feature list...</p>
</div>
<div class="tab-pane" data-tab="usage" role="tabpanel">
<h3>Usage</h3>
<p>How to use...</p>
</div>
</div>
</div>2. JavaScript Usage
const element = document.querySelector('#my-tabs');
const instance = TabsComponent.create(element, {
defaultTab: 'features',
keyboard: true,
animation: true,
onTabChange: (tabId, previousTab) => {
console.log(`Changed from ${previousTab} to ${tabId}`);
}
});3. Auto-initialization
TabsComponent automatically creates instances for elements with data-component="tabs":
<!-- Automatically created when page loads -->
<div data-component="tabs">
<!-- Tab content -->
</div>HTML Attributes
TabsComponent supports these data attributes for configuration:
Basic Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
data-default-tab |
string | null |
ID of the default active tab |
data-keyboard |
boolean | true |
Enable/disable keyboard navigation |
data-animation |
boolean | true |
Enable/disable tab change animations |
data-animation-duration |
number | 300 |
Animation duration (milliseconds) |
data-orientation |
string | 'horizontal' |
Display orientation (horizontal or vertical) |
data-aria-label |
string | 'Tabs' |
ARIA label for tablist |
data-lazy |
boolean | false |
Enable lazy loading of tab content |
data-debug |
boolean | false |
Enable debug mode (show console logs) |
Example Attribute Usage
<div data-component="tabs"
data-default-tab="settings"
data-keyboard="true"
data-animation="true"
data-orientation="horizontal">
<!-- Tab content -->
</div>Configuration Options
When creating an instance via JavaScript, all options can be configured:
const instance = TabsComponent.create(element, {
// Basic settings
defaultTab: null, // Default tab (null = first tab)
keyboard: true, // Enable keyboard navigation
animation: true, // Enable animations
animationDuration: 300, // Animation duration (ms)
// Behavior
destroyOnHidden: false, // Destroy instance when element is removed
lazy: false, // Lazy load content
// Accessibility
ariaLabel: 'Tabs', // ARIA label for tablist
orientation: 'horizontal', // 'horizontal' or 'vertical'
// Event callbacks
onInit: (instance) => {
console.log('Tabs initialized');
},
beforeTabChange: (newTabId, oldTabId) => {
// return false to cancel tab change
return true;
},
onTabChange: (newTabId, oldTabId) => {
console.log(`Changed from ${oldTabId} to ${newTabId}`);
},
onDestroy: (instance) => {
console.log('Tabs destroyed');
},
// Debug
debug: false // Enable console logging
});Keyboard Navigation
TabsComponent supports keyboard navigation following ARIA standards:
Supported Keys
| Key | Action |
|---|---|
→ (Arrow Right) |
Next tab (wraps to first tab at end) |
← (Arrow Left) |
Previous tab (wraps to last tab at start) |
↓ (Arrow Down) |
Next tab (for vertical orientation) |
↑ (Arrow Up) |
Previous tab (for vertical orientation) |
Home |
First tab |
End |
Last tab |
Tab |
Move focus to tab panel |
Usage Example
<!-- Click on tab then use Arrow keys to navigate -->
<div data-component="tabs" data-keyboard="true">
<div class="tab-buttons" role="tablist">
<button class="tab-button" data-tab="tab1">Tab 1</button>
<button class="tab-button" data-tab="tab2">Tab 2</button>
<button class="tab-button" data-tab="tab3">Tab 3</button>
</div>
<!-- panels -->
</div>Disable Keyboard Navigation
<div data-component="tabs" data-keyboard="false">
<!-- Tabs without keyboard support -->
</div>Accessibility
TabsComponent follows ARIA standards completely.
ARIA Attributes Used
On Tablist Container
role="tablist"- Identifies as tab listaria-orientation="horizontal|vertical"- Tab directionaria-label="..."- Label for screen readers
On Tab Buttons
role="tab"- Identifies as tab buttonaria-selected="true|false"- Selection statearia-controls="panel-id"- Links to paneltabindex="0|-1"- Focus managementid="tab-id"- Unique ID
On Tab Panels
role="tabpanel"- Identifies as tab content areaaria-labelledby="tab-id"- Links to tab buttontabindex="0"- Focusablehidden- Hide/show panelid="panel-id"- Unique ID
Complete HTML Example
<div data-component="tabs">
<!-- Tablist -->
<div class="tab-buttons" role="tablist" aria-orientation="horizontal" aria-label="Product Information">
<button class="tab-button active"
data-tab="description"
role="tab"
aria-selected="true"
aria-controls="panel-description"
tabindex="0"
id="tab-description">
Description
</button>
<button class="tab-button"
data-tab="specs"
role="tab"
aria-selected="false"
aria-controls="panel-specs"
tabindex="-1"
id="tab-specs">
Specs
</button>
</div>
<!-- Tab Panels -->
<div class="tab-content">
<div class="tab-pane active"
data-tab="description"
role="tabpanel"
aria-labelledby="tab-description"
tabindex="0"
id="panel-description">
Description content...
</div>
<div class="tab-pane"
data-tab="specs"
role="tabpanel"
aria-labelledby="tab-specs"
tabindex="0"
id="panel-specs"
hidden>
Specs content...
</div>
</div>
</div>Events
TabsComponent dispatches events when changes occur.
Event Types
| Event | When | Details |
|---|---|---|
tabs:tabchange |
Tab changed successfully | {instance, tabId, previousTab, button, panel} |
Listening to Events
const element = document.querySelector('[data-component="tabs"]');
element.addEventListener('tabs:tabchange', (e) => {
console.log('Tab changed to:', e.detail.tabId);
console.log('Previous tab:', e.detail.previousTab);
console.log('Tab button:', e.detail.button);
console.log('Tab panel:', e.detail.panel);
console.log('Instance:', e.detail.instance);
});Event Callbacks
const instance = TabsComponent.create(element, {
// Called when component is created
onInit: function() {
console.log('Tabs initialized', this);
},
// Called before tab change (can cancel)
beforeTabChange: function(newTabId, oldTabId) {
if (newTabId === 'restricted') {
alert('Cannot access this tab');
return false; // Cancel tab change
}
return true; // Allow tab change
},
// Called after successful tab change
onTabChange: function(newTabId, oldTabId) {
console.log(`Changed from ${oldTabId} to ${newTabId}`);
// Save current tab to localStorage
localStorage.setItem('lastTab', newTabId);
},
// Called when component is destroyed
onDestroy: function() {
console.log('Tabs destroyed');
}
});JavaScript API
TabsComponent provides methods for JavaScript control.
Create Instance
const instance = TabsComponent.create(element, options);Parameters:
element(HTMLElement) - Element to create tabs onoptions(Object) - Configuration options
Returns: Instance object
Get Instance
// From element directly
const instance = TabsComponent.getInstance(element);
// From instance property
const instance = element.tabsInstance;Switch Tab
// Via instance method
instance.switchTab('tab-id');
// Or via static method
TabsComponent.switchTab(instance, 'tab-id');Parameters:
tabId(string) - ID of tab to switch to
Returns: boolean - true if successful, false if failed
Get Active Tab
const activeTab = instance.getActiveTab();
console.log('Current tab:', activeTab); // 'tab-id'Returns: string - ID of currently active tab
Refresh Instance
// Re-scan for buttons and panels
instance.refresh();Use when dynamically adding/removing tabs.
Destroy Instance
// Destroy instance and clean up resources
instance.destroy();Removes all event listeners and ARIA attributes.
Usage Examples
1. Basic Tabs
<div data-component="tabs">
<div class="tab-buttons">
<button class="tab-button" data-tab="home">Home</button>
<button class="tab-button" data-tab="about">About</button>
<button class="tab-button" data-tab="contact">Contact</button>
</div>
<div class="tab-content">
<div class="tab-pane" data-tab="home">
<h2>Home</h2>
<p>Welcome...</p>
</div>
<div class="tab-pane" data-tab="about">
<h2>About Us</h2>
<p>We are...</p>
</div>
<div class="tab-pane" data-tab="contact">
<h2>Contact Us</h2>
<p>Phone: 02-xxx-xxxx</p>
</div>
</div>
</div>2. Set Default Tab
<div data-component="tabs" data-default-tab="profile">
<div class="tab-buttons">
<button class="tab-button" data-tab="dashboard">Dashboard</button>
<button class="tab-button" data-tab="profile">Profile</button>
<button class="tab-button" data-tab="settings">Settings</button>
</div>
<div class="tab-content">
<!-- panels -->
</div>
</div>3. Vertical Tabs
<div data-component="tabs" data-orientation="vertical" style="display: flex;">
<div class="tab-buttons" style="flex-direction: column; border-right: 1px solid #ddd;">
<button class="tab-button" data-tab="general">General</button>
<button class="tab-button" data-tab="security">Security</button>
<button class="tab-button" data-tab="privacy">Privacy</button>
</div>
<div class="tab-content" style="flex: 1; padding-left: 20px;">
<!-- panels -->
</div>
</div>4. JavaScript Control
<div id="my-tabs" data-component="tabs">
<!-- tabs content -->
</div>
<button onclick="switchToTab('settings')">Go to Settings</button>
<script>
function switchToTab(tabId) {
const instance = TabsComponent.getInstance('#my-tabs');
if (instance) {
instance.switchTab(tabId);
}
}
</script>5. Save Current Tab to localStorage
const tabs = TabsComponent.create(element, {
onInit: function() {
// Load saved tab
const savedTab = localStorage.getItem('currentTab');
if (savedTab && this.tabIds.includes(savedTab)) {
this.switchTab(savedTab);
}
},
onTabChange: function(newTabId) {
// Save current tab
localStorage.setItem('currentTab', newTabId);
}
});6. Confirm Before Tab Change
const tabs = TabsComponent.create(element, {
beforeTabChange: function(newTabId, oldTabId) {
if (oldTabId === 'form' && formHasUnsavedChanges()) {
return confirm('You have unsaved changes. Do you want to leave this page?');
}
return true;
}
});7. Lazy Loading Content
const tabs = TabsComponent.create(element, {
lazy: true,
onTabChange: async function(tabId) {
const panel = this.panels.find(p => p.dataset.tab === tabId);
// Check if already loaded
if (!panel.dataset.loaded) {
panel.innerHTML = '<p>Loading...</p>';
// Load data from API
const response = await fetch(`/api/tabs/${tabId}`);
const html = await response.text();
panel.innerHTML = html;
panel.dataset.loaded = 'true';
}
}
});8. Dynamic Tabs
<div id="dynamic-tabs" data-component="tabs">
<div class="tab-buttons"></div>
<div class="tab-content"></div>
</div>
<button onclick="addTab()">Add Tab</button>
<script>
function addTab() {
const instance = TabsComponent.getInstance('#dynamic-tabs');
const newTabId = `tab-${Date.now()}`;
// Add tab button
const button = document.createElement('button');
button.className = 'tab-button';
button.dataset.tab = newTabId;
button.textContent = `Tab ${instance.tabIds.length + 1}`;
instance.element.querySelector('.tab-buttons').appendChild(button);
// Add panel
const panel = document.createElement('div');
panel.className = 'tab-pane';
panel.dataset.tab = newTabId;
panel.textContent = `Content for ${newTabId}`;
instance.element.querySelector('.tab-content').appendChild(panel);
// Refresh instance
instance.refresh();
// Switch to new tab
instance.switchTab(newTabId);
}
</script>API Reference
TabsComponent.create(element, options)
Create a new tabs instance.
Parameters:
element(HTMLElement) - Container elementoptions(Object) - Configuration options
Returns: Instance object
TabsComponent.getInstance(element)
Get existing instance.
Parameters:
element(HTMLElement | string) - Element or selector
Returns: Instance object or null
TabsComponent.switchTab(instance, tabId, initial)
Switch to specified tab.
Parameters:
instance(Object) - Tabs instancetabId(string) - Tab IDinitial(boolean) - Is initial activation
Returns: boolean
TabsComponent.refresh(instance)
Re-scan for buttons and panels.
Parameters:
instance(Object) - Tabs instance
TabsComponent.destroy(instance)
Destroy instance.
Parameters:
instance(Object) - Tabs instance
Instance Methods
instance.switchTab(tabId)
Switch tab.
Parameters:
tabId(string) - Tab ID
Returns: boolean
instance.getActiveTab()
Get current tab ID.
Returns: string
instance.refresh()
Refresh instance.
instance.destroy()
Destroy instance.
Best Practices
✅ Do
-
Use correct ARIA attributes
<div class="tab-buttons" role="tablist"> <button class="tab-button" data-tab="tab1" role="tab">Tab 1</button> </div> -
Use meaningful IDs
<button data-tab="user-profile">Profile</button> <button data-tab="user-settings">Settings</button> -
Group related content
- Tabs should contain related content
- Don't have too many tabs (recommend 3-7 tabs)
-
Enable keyboard navigation
<div data-component="tabs" data-keyboard="true"> -
Save tab state
onTabChange: (tabId) => { localStorage.setItem('activeTab', tabId); }
❌ Don't
-
Don't nest tabs multiple levels
<!-- ❌ Bad --> <div data-component="tabs"> <div class="tab-pane"> <div data-component="tabs"> <!-- Nested tabs - confusing --> </div> </div> </div> -
Don't use tabs for main navigation
<!-- ❌ Bad - use menu instead --> <div data-component="tabs"> <button data-tab="home">Home</button> <button data-tab="products">Products</button> <button data-tab="contact">Contact</button> </div> -
Don't forget role attributes
<!-- ❌ Bad - missing ARIA --> <div data-component="tabs"> <div class="tab-buttons"> <button data-tab="tab1">Tab 1</button> </div> </div> <!-- ✅ Good --> <div data-component="tabs"> <div class="tab-buttons" role="tablist"> <button class="tab-button" data-tab="tab1" role="tab">Tab 1</button> </div> </div>
Common Pitfalls
⚠️ Common Mistakes
-
Forgetting data-tab attribute
<!-- ❌ Won't work --> <button class="tab-button">Tab 1</button> <!-- ✅ Correct --> <button class="tab-button" data-tab="tab1">Tab 1</button> -
Mismatched button and panel IDs
<!-- ❌ Won't work --> <button data-tab="profile">Profile</button> <div data-tab="user-profile">...</div> <!-- ✅ Correct --> <button data-tab="profile">Profile</button> <div data-tab="profile">...</div> -
Missing .tab-button or .tab-pane class
<!-- ❌ Won't work --> <button data-tab="tab1">Tab 1</button> <div data-tab="tab1">Content</div> <!-- ✅ Correct --> <button class="tab-button" data-tab="tab1">Tab 1</button> <div class="tab-pane" data-tab="tab1">Content</div>
💡 Tips
-
Use CSS transitions for smooth animations
.tab-pane { opacity: 0; transition: opacity 0.3s ease; } .tab-pane.active { opacity: 1; } -
Use beforeTabChange for validation
beforeTabChange: (newTab, oldTab) => { if (oldTab === 'form' && !isFormValid()) { alert('Please fill in all required fields'); return false; } return true; } -
Use refresh() after adding/removing tabs
// Add new tab addNewTab(); // Refresh instance instance.refresh();