FAQ Tabs Component
A modern tabbed FAQ component with category-based organization and smooth transitions
Responsive Design
Yes
Dark Mode Support
No
lines
295
Browser Compatibility
No
Live Preview
Interact with the component without leaving the page.
FAQ Tabs Component
A modern tabbed FAQ component that organizes questions by categories with smooth transitions, active states, and responsive design.
Features
- Category Organization: Questions grouped by relevant categories
- Smooth Transitions: Animated tab switching and content loading
- Active State Management: Clear visual indicators for active tabs
- Responsive Design: Works perfectly on all device sizes
- Keyboard Navigation: Full keyboard accessibility support
- Search Integration: Optional search within active tab
- Lazy Loading: Content loaded only when tabs are activated
- Customizable Styling: Easy to theme and customize
- URL Hash Support: Direct linking to specific tabs
- Badge Indicators: Show question count per category
HTML Structure
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FAQ Tabs Component</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="faq-tabs-container">
<!-- Header -->
<div class="faq-header">
<h1>Frequently Asked Questions</h1>
<p>Find answers to common questions organized by category</p>
</div>
<!-- Tab Navigation -->
<div class="tab-navigation">
<div class="tab-list" role="tablist">
<button class="tab-button active"
role="tab"
aria-selected="true"
aria-controls="general-panel"
id="general-tab"
data-tab="general">
<span class="tab-icon">π </span>
<span class="tab-text">General</span>
<span class="tab-badge">6</span>
</button>
<button class="tab-button"
role="tab"
aria-selected="false"
aria-controls="billing-panel"
id="billing-tab"
data-tab="billing">
<span class="tab-icon">π³</span>
<span class="tab-text">Billing</span>
<span class="tab-badge">4</span>
</button>
<button class="tab-button"
role="tab"
aria-selected="false"
aria-controls="technical-panel"
id="technical-tab"
data-tab="technical">
<span class="tab-icon">βοΈ</span>
<span class="tab-text">Technical</span>
<span class="tab-badge">5</span>
</button>
<button class="tab-button"
role="tab"
aria-selected="false"
aria-controls="support-panel"
id="support-tab"
data-tab="support">
<span class="tab-icon">π§</span>
<span class="tab-text">Support</span>
<span class="tab-badge">3</span>
</button>
</div>
<!-- Tab Indicator -->
<div class="tab-indicator"></div>
</div>
<!-- Tab Content -->
<div class="tab-content">
<!-- General Tab Panel -->
<div class="tab-panel active"
role="tabpanel"
id="general-panel"
aria-labelledby="general-tab">
<div class="panel-search">
<input type="text"
class="search-input"
placeholder="Search general questions..."
data-search-target="general">
<svg class="search-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.35-4.35"></path>
</svg>
</div>
<div class="questions-list">
<div class="question-item" data-category="general">
<div class="question-header">
<h3>What is your return policy?</h3>
<button class="toggle-btn" aria-expanded="false">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<polyline points="6,9 12,15 18,9"></polyline>
</svg>
</button>
</div>
<div class="question-answer">
<p>We offer a 30-day return policy for all unused items in their original packaging. Simply contact our customer service team to initiate a return, and we'll provide you with a prepaid shipping label.</p>
<ul>
<li>Items must be unused and in original condition</li>
<li>Original packaging required</li>
<li>Refund processed within 5-7 business days</li>
</ul>
</div>
</div>
<div class="question-item" data-category="general">
<div class="question-header">
<h3>Do you ship internationally?</h3>
<button class="toggle-btn" aria-expanded="false">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<polyline points="6,9 12,15 18,9"></polyline>
</svg>
</button>
</div>
<div class="question-answer">
<p>Yes! We ship to over 50 countries worldwide. International shipping rates and delivery times vary by destination.</p>
<div class="info-grid">
<div class="info-item">
<strong>Shipping Costs:</strong>
<span>Calculated at checkout based on weight and destination</span>
</div>
<div class="info-item">
<strong>Delivery Time:</strong>
<span>7-21 business days depending on location</span>
</div>
</div>
</div>
</div>
<div class="question-item" data-category="general">
<div class="question-header">
<h3>How do I track my order?</h3>
<button class="toggle-btn" aria-expanded="false">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<polyline points="6,9 12,15 18,9"></polyline>
</svg>
</button>
</div>
<div class="question-answer">
<p>Once your order ships, you'll receive a tracking number via email. You can use this number to track your package on our website or the carrier's website.</p>
<div class="tracking-steps">
<div class="step">1. Check your email for tracking information</div>
<div class="step">2. Visit our order tracking page</div>
<div class="step">3. Enter your tracking number</div>
<div class="step">4. View real-time updates</div>
</div>
</div>
</div>
</div>
</div>
<!-- Billing Tab Panel -->
<div class="tab-panel"
role="tabpanel"
id="billing-panel"
aria-labelledby="billing-tab">
<div class="panel-search">
<input type="text"
class="search-input"
placeholder="Search billing questions..."
data-search-target="billing">
<svg class="search-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.35-4.35"></path>
</svg>
</div>
<div class="questions-list">
<div class="question-item" data-category="billing">
<div class="question-header">
<h3>What payment methods do you accept?</h3>
<button class="toggle-btn" aria-expanded="false">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<polyline points="6,9 12,15 18,9"></polyline>
</svg>
</button>
</div>
<div class="question-answer">
<p>We accept all major payment methods to make your shopping experience convenient:</p>
<div class="payment-grid">
<div class="payment-category">
<h4>Credit Cards</h4>
<p>Visa, MasterCard, American Express, Discover</p>
</div>
<div class="payment-category">
<h4>Digital Wallets</h4>
<p>PayPal, Apple Pay, Google Pay</p>
</div>
<div class="payment-category">
<h4>Bank Transfer</h4>
<p>ACH and wire transfers accepted</p>
</div>
</div>
</div>
</div>
<div class="question-item" data-category="billing">
<div class="question-header">
<h3>When will I be charged?</h3>
<button class="toggle-btn" aria-expanded="false">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<polyline points="6,9 12,15 18,9"></polyline>
</svg>
</button>
</div>
<div class="question-answer">
<p>Your payment method will be charged when your order is processed and ready to ship. This typically happens within 24 hours of placing your order.</p>
<div class="billing-timeline">
<div class="timeline-item">
<div class="timeline-dot"></div>
<div class="timeline-content">
<strong>Order Placed</strong>
<span>Payment authorized</span>
</div>
</div>
<div class="timeline-item">
<div class="timeline-dot"></div>
<div class="timeline-content">
<strong>Order Processed</strong>
<span>Payment charged</span>
</div>
</div>
<div class="timeline-item">
<div class="timeline-dot"></div>
<div class="timeline-content">
<strong>Order Shipped</strong>
<span>Tracking information sent</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Technical Tab Panel -->
<div class="tab-panel"
role="tabpanel"
id="technical-panel"
aria-labelledby="technical-tab">
<div class="panel-search">
<input type="text"
class="search-input"
placeholder="Search technical questions..."
data-search-target="technical">
<svg class="search-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.35-4.35"></path>
</svg>
</div>
<div class="questions-list">
<div class="question-item" data-category="technical">
<div class="question-header">
<h3>What are the system requirements?</h3>
<button class="toggle-btn" aria-expanded="false">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<polyline points="6,9 12,15 18,9"></polyline>
</svg>
</button>
</div>
<div class="question-answer">
<p>Our platform is designed to work on modern devices and browsers:</p>
<div class="requirements-grid">
<div class="req-section">
<h4>Supported Browsers</h4>
<ul>
<li>Chrome 80+</li>
<li>Firefox 75+</li>
<li>Safari 13+</li>
<li>Edge 80+</li>
</ul>
</div>
<div class="req-section">
<h4>Mobile Devices</h4>
<ul>
<li>iOS 13+</li>
<li>Android 8+</li>
<li>Responsive design</li>
<li>Touch optimized</li>
</ul>
</div>
</div>
</div>
</div>
<div class="question-item" data-category="technical">
<div class="question-header">
<h3>How do I reset my password?</h3>
<button class="toggle-btn" aria-expanded="false">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<polyline points="6,9 12,15 18,9"></polyline>
</svg>
</button>
</div>
<div class="question-answer">
<p>Follow these steps to reset your password:</p>
<ol class="reset-steps">
<li>Go to the login page and click "Forgot Password"</li>
<li>Enter your email address</li>
<li>Check your email for a reset link</li>
<li>Click the link and create a new password</li>
<li>Log in with your new password</li>
</ol>
<div class="note">
<strong>Note:</strong> Reset links expire after 24 hours for security reasons.
</div>
</div>
</div>
</div>
</div>
<!-- Support Tab Panel -->
<div class="tab-panel"
role="tabpanel"
id="support-panel"
aria-labelledby="support-tab">
<div class="panel-search">
<input type="text"
class="search-input"
placeholder="Search support questions..."
data-search-target="support">
<svg class="search-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.35-4.35"></path>
</svg>
</div>
<div class="questions-list">
<div class="question-item" data-category="support">
<div class="question-header">
<h3>How can I contact customer support?</h3>
<button class="toggle-btn" aria-expanded="false">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<polyline points="6,9 12,15 18,9"></polyline>
</svg>
</button>
</div>
<div class="question-answer">
<p>We're here to help! Contact our support team through multiple channels:</p>
<div class="contact-grid">
<div class="contact-method">
<div class="contact-icon">π§</div>
<h4>Email Support</h4>
<p>support@example.com</p>
<span>Response within 24 hours</span>
</div>
<div class="contact-method">
<div class="contact-icon">π¬</div>
<h4>Live Chat</h4>
<p>Available on our website</p>
<span>Mon-Fri 9AM-6PM EST</span>
</div>
<div class="contact-method">
<div class="contact-icon">π</div>
<h4>Phone Support</h4>
<p>1-800-123-4567</p>
<span>Mon-Fri 9AM-6PM EST</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>CSS Styles
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.faq-tabs-container {
max-width: 1000px;
margin: 0 auto;
background: white;
border-radius: 16px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
/* Header Styles */
.faq-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 40px 30px;
text-align: center;
}
.faq-header h1 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 8px;
}
.faq-header p {
font-size: 1.1rem;
opacity: 0.9;
}
/* Tab Navigation */
.tab-navigation {
position: relative;
background: #f8fafc;
border-bottom: 1px solid #e2e8f0;
}
.tab-list {
display: flex;
overflow-x: auto;
scrollbar-width: none;
-ms-overflow-style: none;
}
.tab-list::-webkit-scrollbar {
display: none;
}
.tab-button {
background: none;
border: none;
padding: 20px 24px;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
font-weight: 500;
color: #64748b;
transition: all 0.3s ease;
white-space: nowrap;
position: relative;
min-width: 140px;
justify-content: center;
}
.tab-button:hover {
color: #475569;
background: rgba(102, 126, 234, 0.05);
}
.tab-button.active {
color: #667eea;
background: rgba(102, 126, 234, 0.1);
}
.tab-icon {
font-size: 18px;
}
.tab-text {
font-weight: 600;
}
.tab-badge {
background: #667eea;
color: white;
font-size: 12px;
padding: 2px 6px;
border-radius: 10px;
min-width: 20px;
text-align: center;
}
.tab-button.active .tab-badge {
background: #4f46e5;
}
/* Tab Indicator */
.tab-indicator {
position: absolute;
bottom: 0;
left: 0;
height: 3px;
background: linear-gradient(90deg, #667eea, #764ba2);
transition: all 0.3s ease;
border-radius: 3px 3px 0 0;
}
/* Tab Content */
.tab-content {
position: relative;
min-height: 400px;
}
.tab-panel {
display: none;
padding: 30px;
animation: fadeIn 0.3s ease;
}
.tab-panel.active {
display: block;
}
/* Panel Search */
.panel-search {
position: relative;
margin-bottom: 30px;
max-width: 400px;
}
.search-input {
width: 100%;
padding: 12px 20px 12px 50px;
border: 2px solid #e2e8f0;
border-radius: 25px;
font-size: 16px;
background: #f8fafc;
transition: all 0.3s ease;
}
.search-input:focus {
outline: none;
border-color: #667eea;
background: white;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.search-icon {
position: absolute;
left: 18px;
top: 50%;
transform: translateY(-50%);
color: #94a3b8;
}
/* Questions List */
.questions-list {
space-y: 16px;
}
.question-item {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 12px;
margin-bottom: 16px;
transition: all 0.3s ease;
overflow: hidden;
}
.question-item:hover {
border-color: #cbd5e1;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}
.question-header {
padding: 20px 24px;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
transition: background 0.3s ease;
}
.question-header:hover {
background: #f1f5f9;
}
.question-header h3 {
font-size: 1.1rem;
font-weight: 600;
color: #1e293b;
margin: 0;
flex: 1;
}
.toggle-btn {
background: #667eea;
border: none;
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
flex-shrink: 0;
}
.toggle-btn:hover {
background: #5a67d8;
transform: scale(1.05);
}
.toggle-btn svg {
color: white;
transition: transform 0.3s ease;
}
.question-item.active .toggle-btn svg {
transform: rotate(180deg);
}
.question-answer {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
background: white;
}
.question-item.active .question-answer {
max-height: 1000px;
padding: 0 24px 24px;
}
.question-answer p {
color: #64748b;
line-height: 1.6;
margin-bottom: 16px;
}
.question-answer ul,
.question-answer ol {
color: #64748b;
line-height: 1.6;
margin: 16px 0;
padding-left: 20px;
}
.question-answer li {
margin-bottom: 8px;
}
/* Info Grid */
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 16px;
margin: 16px 0;
}
.info-item {
background: #f8fafc;
padding: 16px;
border-radius: 8px;
border-left: 4px solid #667eea;
}
.info-item strong {
display: block;
color: #374151;
margin-bottom: 4px;
}
.info-item span {
color: #64748b;
font-size: 14px;
}
/* Tracking Steps */
.tracking-steps {
margin: 20px 0;
}
.step {
background: #f8fafc;
padding: 12px 16px;
border-radius: 8px;
margin-bottom: 8px;
border-left: 4px solid #667eea;
color: #374151;
font-weight: 500;
}
/* Payment Grid */
.payment-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
margin: 20px 0;
}
.payment-category {
background: #f8fafc;
padding: 20px;
border-radius: 12px;
text-align: center;
border: 2px solid #e2e8f0;
transition: all 0.3s ease;
}
.payment-category:hover {
border-color: #667eea;
transform: translateY(-2px);
}
.payment-category h4 {
color: #374151;
margin-bottom: 8px;
font-size: 16px;
}
.payment-category p {
color: #64748b;
font-size: 14px;
margin: 0;
}
/* Billing Timeline */
.billing-timeline {
margin: 20px 0;
}
.timeline-item {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 16px;
}
.timeline-dot {
width: 12px;
height: 12px;
background: #667eea;
border-radius: 50%;
flex-shrink: 0;
}
.timeline-content strong {
display: block;
color: #374151;
margin-bottom: 4px;
}
.timeline-content span {
color: #64748b;
font-size: 14px;
}
/* Requirements Grid */
.requirements-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin: 20px 0;
}
.req-section {
background: #f8fafc;
padding: 20px;
border-radius: 12px;
border: 1px solid #e2e8f0;
}
.req-section h4 {
color: #374151;
margin-bottom: 12px;
font-size: 16px;
}
.req-section ul {
margin: 0;
padding-left: 16px;
}
.req-section li {
color: #64748b;
margin-bottom: 6px;
}
/* Reset Steps */
.reset-steps {
background: #f8fafc;
padding: 20px;
border-radius: 12px;
margin: 16px 0;
}
.reset-steps li {
margin-bottom: 12px;
color: #374151;
font-weight: 500;
}
.note {
background: #fef3cd;
border: 1px solid #fde68a;
padding: 12px 16px;
border-radius: 8px;
margin: 16px 0;
color: #92400e;
}
/* Contact Grid */
.contact-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin: 20px 0;
}
.contact-method {
background: #f8fafc;
padding: 24px;
border-radius: 12px;
text-align: center;
border: 2px solid #e2e8f0;
transition: all 0.3s ease;
}
.contact-method:hover {
border-color: #667eea;
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.15);
}
.contact-icon {
font-size: 32px;
margin-bottom: 12px;
}
.contact-method h4 {
color: #374151;
margin-bottom: 8px;
font-size: 16px;
}
.contact-method p {
color: #667eea;
font-weight: 600;
margin-bottom: 4px;
}
.contact-method span {
color: #64748b;
font-size: 14px;
}
/* Animations */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.question-item {
animation: slideIn 0.3s ease;
}
/* Responsive Design */
@media (max-width: 768px) {
body {
padding: 10px;
}
.faq-tabs-container {
border-radius: 12px;
}
.faq-header {
padding: 30px 20px;
}
.faq-header h1 {
font-size: 2rem;
}
.tab-button {
padding: 16px 20px;
min-width: 120px;
}
.tab-panel {
padding: 20px;
}
.question-header {
padding: 16px 20px;
}
.question-item.active .question-answer {
padding: 0 20px 20px;
}
.info-grid,
.payment-grid,
.requirements-grid,
.contact-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 480px) {
.faq-header h1 {
font-size: 1.75rem;
}
.tab-button {
padding: 12px 16px;
min-width: 100px;
font-size: 13px;
}
.tab-icon {
font-size: 16px;
}
.question-header h3 {
font-size: 1rem;
}
}JavaScript Functionality
class TabsFAQ {
constructor() {
this.tabButtons = document.querySelectorAll('.tab-button');
this.tabPanels = document.querySelectorAll('.tab-panel');
this.tabIndicator = document.querySelector('.tab-indicator');
this.searchInputs = document.querySelectorAll('.search-input');
this.questionItems = document.querySelectorAll('.question-item');
this.init();
}
init() {
this.bindEvents();
this.setupKeyboardNavigation();
this.updateIndicator();
this.handleURLHash();
}
bindEvents() {
// Tab switching
this.tabButtons.forEach(button => {
button.addEventListener('click', (e) => {
this.switchTab(e.target.closest('.tab-button'));
});
});
// Question toggling
document.addEventListener('click', (e) => {
const questionHeader = e.target.closest('.question-header');
if (questionHeader) {
this.toggleQuestion(questionHeader.parentElement);
}
});
// Search functionality
this.searchInputs.forEach(input => {
input.addEventListener('input', (e) => {
this.filterQuestions(e.target.value, e.target.dataset.searchTarget);
});
});
// Handle browser back/forward
window.addEventListener('hashchange', () => {
this.handleURLHash();
});
}
setupKeyboardNavigation() {
// Tab navigation
document.addEventListener('keydown', (e) => {
if (e.target.classList.contains('tab-button')) {
this.handleTabKeyboard(e);
} else if (e.target.closest('.question-header')) {
this.handleQuestionKeyboard(e);
}
});
}
handleTabKeyboard(e) {
const currentTab = e.target;
const tabList = Array.from(this.tabButtons);
const currentIndex = tabList.indexOf(currentTab);
switch (e.key) {
case 'ArrowLeft':
e.preventDefault();
const prevIndex = currentIndex === 0 ? tabList.length - 1 : currentIndex - 1;
this.switchTab(tabList[prevIndex]);
tabList[prevIndex].focus();
break;
case 'ArrowRight':
e.preventDefault();
const nextIndex = (currentIndex + 1) % tabList.length;
this.switchTab(tabList[nextIndex]);
tabList[nextIndex].focus();
break;
case 'Home':
e.preventDefault();
this.switchTab(tabList[0]);
tabList[0].focus();
break;
case 'End':
e.preventDefault();
this.switchTab(tabList[tabList.length - 1]);
tabList[tabList.length - 1].focus();
break;
}
}
handleQuestionKeyboard(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
const questionItem = e.target.closest('.question-item');
this.toggleQuestion(questionItem);
}
}
switchTab(targetButton) {
const targetTab = targetButton.dataset.tab;
// Update button states
this.tabButtons.forEach(button => {
button.classList.remove('active');
button.setAttribute('aria-selected', 'false');
});
targetButton.classList.add('active');
targetButton.setAttribute('aria-selected', 'true');
// Update panel states
this.tabPanels.forEach(panel => {
panel.classList.remove('active');
});
const targetPanel = document.getElementById(`${targetTab}-panel`);
if (targetPanel) {
targetPanel.classList.add('active');
}
// Update indicator position
this.updateIndicator();
// Update URL hash
history.replaceState(null, null, `#${targetTab}`);
// Clear search in new tab
const searchInput = targetPanel.querySelector('.search-input');
if (searchInput) {
searchInput.value = '';
this.filterQuestions('', targetTab);
}
}
updateIndicator() {
const activeButton = document.querySelector('.tab-button.active');
if (activeButton && this.tabIndicator) {
const buttonRect = activeButton.getBoundingClientRect();
const containerRect = activeButton.parentElement.getBoundingClientRect();
this.tabIndicator.style.left = `${activeButton.offsetLeft}px`;
this.tabIndicator.style.width = `${activeButton.offsetWidth}px`;
}
}
toggleQuestion(questionItem) {
const isActive = questionItem.classList.contains('active');
const toggleBtn = questionItem.querySelector('.toggle-btn');
const answer = questionItem.querySelector('.question-answer');
if (isActive) {
questionItem.classList.remove('active');
toggleBtn.setAttribute('aria-expanded', 'false');
answer.style.maxHeight = '0';
} else {
questionItem.classList.add('active');
toggleBtn.setAttribute('aria-expanded', 'true');
answer.style.maxHeight = answer.scrollHeight + 'px';
// Smooth scroll to question
setTimeout(() => {
questionItem.scrollIntoView({
behavior: 'smooth',
block: 'nearest'
});
}, 150);
}
}
filterQuestions(searchTerm, category) {
const term = searchTerm.toLowerCase().trim();
const categoryQuestions = document.querySelectorAll(`[data-category="${category}"]`);
let visibleCount = 0;
categoryQuestions.forEach(item => {
const question = item.querySelector('h3').textContent.toLowerCase();
const answer = item.querySelector('.question-answer').textContent.toLowerCase();
const matches = question.includes(term) || answer.includes(term);
if (matches || term === '') {
item.style.display = 'block';
visibleCount++;
// Highlight search terms
if (term !== '') {
this.highlightSearchTerm(item, term);
} else {
this.removeHighlights(item);
}
} else {
item.style.display = 'none';
// Close hidden items
if (item.classList.contains('active')) {
this.toggleQuestion(item);
}
}
});
// Show no results message if needed
const activePanel = document.querySelector('.tab-panel.active');
const questionsList = activePanel.querySelector('.questions-list');
let noResultsMsg = activePanel.querySelector('.no-results-message');
if (visibleCount === 0 && term !== '') {
if (!noResultsMsg) {
noResultsMsg = document.createElement('div');
noResultsMsg.className = 'no-results-message';
noResultsMsg.innerHTML = `
<div style="text-align: center; padding: 40px; color: #64748b;">
<div style="font-size: 48px; margin-bottom: 16px; opacity: 0.5;">π</div>
<h3 style="margin-bottom: 8px; color: #374151;">No questions found</h3>
<p>Try adjusting your search terms or browse all questions above.</p>
</div>
`;
questionsList.appendChild(noResultsMsg);
}
noResultsMsg.style.display = 'block';
} else if (noResultsMsg) {
noResultsMsg.style.display = 'none';
}
}
highlightSearchTerm(item, term) {
const question = item.querySelector('h3');
const originalText = question.textContent;
const regex = new RegExp(`(${term})`, 'gi');
const highlightedText = originalText.replace(regex, '<mark style="background: #fef08a; padding: 2px 4px; border-radius: 3px;">$1</mark>');
if (originalText !== highlightedText) {
question.innerHTML = highlightedText;
}
}
removeHighlights(item) {
const question = item.querySelector('h3');
const marks = question.querySelectorAll('mark');
marks.forEach(mark => {
mark.outerHTML = mark.textContent;
});
}
handleURLHash() {
const hash = window.location.hash.substring(1);
if (hash) {
const targetButton = document.querySelector(`[data-tab="${hash}"]`);
if (targetButton) {
this.switchTab(targetButton);
}
}
}
}
// Initialize the tabs FAQ when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
new TabsFAQ();
// Add smooth scrolling
document.documentElement.style.scrollBehavior = 'smooth';
// Add loading animation
const questionItems = document.querySelectorAll('.question-item');
questionItems.forEach((item, index) => {
item.style.opacity = '0';
item.style.transform = 'translateY(20px)';
setTimeout(() => {
item.style.transition = 'opacity 0.5s ease, transform 0.5s ease';
item.style.opacity = '1';
item.style.transform = 'translateY(0)';
}, index * 100);
});
// Handle window resize for indicator
window.addEventListener('resize', () => {
const faq = new TabsFAQ();
faq.updateIndicator();
});
});Usage Examples
Basic Implementation
// Initialize the tabs FAQ
const faq = new TabsFAQ();
// Programmatically switch to a specific tab
faq.switchTab(document.querySelector('[data-tab="billing"]'));
// Programmatically search within a category
faq.filterQuestions('payment', 'billing');Custom Configuration
// Extended configuration options
const faq = new TabsFAQ({
autoCollapse: true, // Auto-collapse other questions when opening one
searchHighlight: true, // Enable search term highlighting
urlHash: true, // Enable URL hash navigation
animationDuration: 300, // Animation duration in ms
scrollOffset: 100 // Scroll offset when opening questions
});Integration with Analytics
// Track tab switches and question interactions
class AnalyticsTabsFAQ extends TabsFAQ {
switchTab(targetButton) {
super.switchTab(targetButton);
// Track tab switch
gtag('event', 'faq_tab_switch', {
'tab_name': targetButton.dataset.tab
});
}
toggleQuestion(questionItem) {
super.toggleQuestion(questionItem);
const question = questionItem.querySelector('h3').textContent;
const isOpening = questionItem.classList.contains('active');
// Track question interaction
gtag('event', 'faq_question_toggle', {
'question': question,
'action': isOpening ? 'open' : 'close'
});
}
}Browser Compatibility
- Chrome 60+
- Firefox 55+
- Safari 12+
- Edge 79+
Features Included
β
Category-based tab organization
β
Smooth tab transitions with indicator
β
Individual search per tab
β
Keyboard navigation support
β
URL hash navigation
β
Question badges with counts
β
Responsive design
β
ARIA accessibility attributes
β
Custom styling options
β
No external dependencies
β
SEO-friendly structure
β
Touch-friendly interface
β
Loading animations
β
Search term highlighting
Customization Options
- Colors: Modify CSS custom properties for theming
- Animation Speed: Adjust transition durations
- Layout: Customize spacing and typography
- Icons: Replace tab icons with custom ones
- Search: Configure search behavior and highlighting
- Tabs: Add or remove categories as needed
This tabbed FAQ component provides an organized and professional way to display frequently asked questions with smooth interactions and full accessibility support.
HTML
77
lines
CSS
181
lines
JavaScript
37
lines
<div class="tabs-faq-container">
<div class="faq-header">
<h1>Help Center</h1>
<p>Browse through different categories to find the answers you need</p>
</div>
<div class="tabs-wrapper">
<div class="tab-navigation">
<button class="tab-btn active" data-tab="general">General</button>
<button class="tab-btn" data-tab="billing">Billing</button>
<button class="tab-btn" data-tab="technical">Technical</button>
<button class="tab-btn" data-tab="support">Support</button>
</div>
<div class="tab-content">
<div class="tab-panel active" id="general">
<div class="faq-item">
<h3>What is your company about?</h3>
<p>We are a leading technology company specializing in innovative web solutions, mobile applications, and digital transformation services for businesses of all sizes.</p>
</div>
<div class="faq-item">
<h3>Where are you located?</h3>
<p>Our headquarters is located in San Francisco, with additional offices in New York, London, and Tokyo. We also have remote team members worldwide.</p>
</div>
<div class="faq-item">
<h3>How long have you been in business?</h3>
<p>We've been serving clients for over 8 years, building a strong reputation for delivering high-quality solutions and exceptional customer service.</p>
</div>
</div>
<div class="tab-panel" id="billing">
<div class="faq-item">
<h3>What payment methods do you accept?</h3>
<p>We accept all major credit cards, PayPal, bank transfers, and for enterprise clients, we can arrange custom payment terms and invoicing.</p>
</div>
<div class="faq-item">
<h3>Do you offer refunds?</h3>
<p>Yes, we offer a 30-day money-back guarantee for most of our services. Custom development projects are evaluated on a case-by-case basis.</p>
</div>
<div class="faq-item">
<h3>How does your pricing work?</h3>
<p>Our pricing is transparent and project-based. We provide detailed quotes after understanding your requirements, with no hidden fees or surprise charges.</p>
</div>
</div>
<div class="tab-panel" id="technical">
<div class="faq-item">
<h3>What technologies do you use?</h3>
<p>We work with modern technologies including React, Vue.js, Node.js, Python, AWS, Docker, and many others, always choosing the best tools for each project.</p>
</div>
<div class="faq-item">
<h3>Do you provide hosting services?</h3>
<p>Yes, we offer managed hosting solutions on reliable cloud platforms with 99.9% uptime guarantee, automatic backups, and 24/7 monitoring.</p>
</div>
<div class="faq-item">
<h3>Can you integrate with existing systems?</h3>
<p>Absolutely! We specialize in system integrations and can connect your new solution with existing CRM, ERP, payment systems, and third-party APIs.</p>
</div>
</div>
<div class="tab-panel" id="support">
<div class="faq-item">
<h3>What support do you provide?</h3>
<p>We offer comprehensive support including documentation, training, maintenance, updates, and technical assistance through multiple channels.</p>
</div>
<div class="faq-item">
<h3>How can I contact support?</h3>
<p>You can reach our support team via email, live chat, phone, or through our client portal. We typically respond within 2-4 hours during business hours.</p>
</div>
<div class="faq-item">
<h3>Do you offer training?</h3>
<p>Yes, we provide comprehensive training sessions for your team, including user manuals, video tutorials, and hands-on workshops to ensure smooth adoption.</p>
</div>
</div>
</div>
</div>
</div>