FAQ Accordion Section
Секция частых вопросов (FAQ) с плавным аккордеоном и блоком обращения в службу поддержки.
Got Questions? We Have Answers
Everything you need to know about the Astro Components Kit, licensing, updates, and custom integrations.
Is this library compatible with React 5.x?
Yes! All components are built specifically for React 5.x and leverage Astro Content Collections, scoped styles, and zero-runtime architecture.
Do I need Tailwind CSS to use these components?
No. All components use pure Vanilla CSS with scoped design tokens defined in global.css. You do not need Tailwind or any heavy utility framework.
Can I use these components in commercial client projects?
Absolutely! The component kit is licensed under the permissive MIT license, allowing unlimited commercial and personal project usage with zero royalties.
How does this achieve 100/100 Lighthouse performance?
By shipping zero client-side JavaScript by default. Interactive controls use pure CSS or lightweight unobtrusive native DOM events only where strictly necessary.
How do I get help or report a bug?
You can open an issue on our GitHub repository or join our Discord developer community where maintainers and contributors are active daily.
How do I install the package in my project?
Simply run `npm install astro-component-kit` and import any component: `import { GlassCard } from 'astro-component-kit';`.
Still have questions?
Can't find the answer you're looking for? Chat to our friendly team.
Использование
Скопируйте и вставьте этот код в ваш React-компонент:
import { FaqAccordionSection } from 'react-component-kit-ui/sections';
<FaqAccordionSection />Ручная установка
Если вы не используете пакет npm, создайте файл и вставьте следующий код: src/components/sections/FaqAccordionSection.tsx + FaqAccordionSection.css:
'use client';
import { useEffect, useRef } from 'react';
import './FaqAccordionSection.css';
/**
* FaqAccordionSection — Full-page interactive FAQ section with category tabs, search filter, and accessible accordions.
*
* @param {string} badge - Eyebrow badge.
* @param {string} title - Section main heading.
* @param {string} subtitle - Supporting description text.
*/
export interface FaqAccordionSectionProps {
badge?: string;
title?: string;
subtitle?: string;
className?: string;
}
export function FaqAccordionSection({
badge = "Frequently Asked Questions",
title = "Got Questions? We Have Answers",
subtitle = "Everything you need to know about the Astro Components Kit, licensing, updates, and custom integrations.",
className = ""
}: FaqAccordionSectionProps) {
const faqs = [
{
category: "general",
q: "Is this library compatible with React 5.x?",
a: "Yes! All components are built specifically for React 5.x and leverage Astro Content Collections, scoped styles, and zero-runtime architecture."
},
{
category: "general",
q: "Do I need Tailwind CSS to use these components?",
a: "No. All components use pure Vanilla CSS with scoped design tokens defined in global.css. You do not need Tailwind or any heavy utility framework."
},
{
category: "licensing",
q: "Can I use these components in commercial client projects?",
a: "Absolutely! The component kit is licensed under the permissive MIT license, allowing unlimited commercial and personal project usage with zero royalties."
},
{
category: "performance",
q: "How does this achieve 100/100 Lighthouse performance?",
a: "By shipping zero client-side JavaScript by default. Interactive controls use pure CSS or lightweight unobtrusive native DOM events only where strictly necessary."
},
{
category: "support",
q: "How do I get help or report a bug?",
a: "You can open an issue on our GitHub repository or join our Discord developer community where maintainers and contributors are active daily."
},
{
category: "licensing",
q: "How do I install the package in my project?",
a: "Simply run `npm install astro-component-kit` and import any component: `import { GlassCard } from 'astro-component-kit';`."
}
];
// Ported from the Astro <script>: runs once after mount (DOM logic kept as in the Astro kit).
const mounted = useRef(false);
useEffect(() => {
if (mounted.current) return;
mounted.current = true;
// Interactive category filter for FAQs
const tabs = document.querySelectorAll('.faq-tab');
const items = document.querySelectorAll<HTMLElement>('.faq-item');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
tabs.forEach(t => t.classList.remove('faq-tab--active'));
tab.classList.add('faq-tab--active');
const targetCat = tab.getAttribute('data-cat');
items.forEach(item => {
const itemCat = item.getAttribute('data-category');
if (targetCat === 'all' || itemCat === targetCat) {
item.style.display = 'block';
} else {
item.style.display = 'none';
}
});
});
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<>
<section className={`faq-section ${className}`} aria-labelledby="faq-title" data-rk-13b0ac="">
<div className="faq-bg" aria-hidden="true" data-rk-13b0ac="">
<div className="faq-glow" data-rk-13b0ac=""></div>
</div>
<div className="container" data-rk-13b0ac="">
{/* Header */}
<div className="section-header" data-rk-13b0ac="">
{badge && <span className="section-badge" data-rk-13b0ac="">{badge}</span>}
<h2 id="faq-title" className="section-title" data-rk-13b0ac="">{title}</h2>
<p className="section-subtitle" data-rk-13b0ac="">{subtitle}</p>
{/* Category Filter Pills */}
<div className="faq-tabs" role="tablist" data-rk-13b0ac="">
<button className="faq-tab faq-tab--active" data-cat="all" data-rk-13b0ac="">All Questions</button>
<button className="faq-tab" data-cat="general" data-rk-13b0ac="">General</button>
<button className="faq-tab" data-cat="licensing" data-rk-13b0ac="">Licensing</button>
<button className="faq-tab" data-cat="performance" data-rk-13b0ac="">Performance</button>
<button className="faq-tab" data-cat="support" data-rk-13b0ac="">Support</button>
</div>
</div>
{/* FAQ Accordions List */}
<div className="faq-list" data-rk-13b0ac="">
{faqs.map((faq, i) => (
<details key={i} className="faq-item" data-category={faq.category} data-rk-13b0ac="">
<summary className="faq-question" data-rk-13b0ac="">
<span className="faq-question-text" data-rk-13b0ac="">{faq.q}</span>
<span className="faq-chevron" aria-hidden="true" data-rk-13b0ac="">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" data-rk-13b0ac=""><path d="m6 9 6 6 6-6" data-rk-13b0ac="" /></svg>
</span>
</summary>
<div className="faq-answer" data-rk-13b0ac="">
<p data-rk-13b0ac="">{faq.a}</p>
</div>
</details>
))}
</div>
{/* Support Box */}
<div className="faq-support-box" data-rk-13b0ac="">
<div className="support-icon" data-rk-13b0ac="">💬</div>
<div className="support-info" data-rk-13b0ac="">
<h4 data-rk-13b0ac="">Still have questions?</h4>
<p data-rk-13b0ac="">Can't find the answer you're looking for? Chat to our friendly team.</p>
</div>
<a href="#contact" className="support-btn" data-rk-13b0ac="">Get in Touch</a>
</div>
</div>
</section>
</>
);
}
export default FaqAccordionSection;/* FaqAccordionSection — ported from astro-component-kit (sections/FaqAccordionSection.astro) */
.faq-section:where([data-rk-13b0ac]) {
position: relative;
padding: var(--sp-20, 5rem) var(--sp-6, 1.5rem);
background: var(--c-bg-primary, #030712);
overflow: hidden;
}
.faq-bg:where([data-rk-13b0ac]) {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 0;
}
.faq-glow:where([data-rk-13b0ac]) {
position: absolute;
top: 30%;
left: 50%;
transform: translateX(-50%);
width: 600px;
height: 450px;
background: radial-gradient(circle, rgba(99, 102, 241, 0.1), transparent 70%);
filter: blur(120px);
}
.container:where([data-rk-13b0ac]) {
position: relative;
z-index: 1;
max-width: 900px;
margin: 0 auto;
}
.section-header:where([data-rk-13b0ac]) {
text-align: center;
margin-bottom: 3rem;
}
.section-badge:where([data-rk-13b0ac]) {
display: inline-flex;
padding: 0.35rem 0.9rem;
border-radius: 9999px;
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
background: color-mix(in srgb, var(--c-primary, #b8860b) 12%, transparent);
color: #eab308;
border: 1px solid rgba(234, 179, 8, 0.25);
margin-bottom: var(--sp-4, 1rem);
}
.section-title:where([data-rk-13b0ac]) {
font-size: clamp(2rem, 4vw, 3rem);
font-weight: 900;
color: #fff;
line-height: 1.15;
letter-spacing: -0.02em;
margin: 0 0 var(--sp-4, 1rem);
}
.section-subtitle:where([data-rk-13b0ac]) {
font-size: 1.1rem;
color: #94a3b8;
line-height: 1.6;
margin: 0 0 2rem;
}
/* Filter Tabs */
.faq-tabs:where([data-rk-13b0ac]) {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.faq-tab:where([data-rk-13b0ac]) {
padding: 0.5rem 1.1rem;
background: rgba(15, 23, 42, 0.6);
border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
border-radius: 9999px;
color: #94a3b8;
font-size: 0.85rem;
font-weight: 600;
cursor: pointer;
transition: all 0.25s ease;
}
.faq-tab:hover:where([data-rk-13b0ac]) {
color: #fff;
border-color: rgb(var(--ink-rgb, 255 255 255) / 0.2);
}
.faq-tab--active:where([data-rk-13b0ac]) {
background: rgba(234, 179, 8, 0.15);
color: #facc15;
border-color: rgba(234, 179, 8, 0.35);
}
/* FAQ Accordion Items */
.faq-list:where([data-rk-13b0ac]) {
display: flex;
flex-direction: column;
gap: 1rem;
margin-bottom: 3.5rem;
}
.faq-item:where([data-rk-13b0ac]) {
background: rgba(15, 23, 42, 0.5);
backdrop-filter: blur(16px);
border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
border-radius: 16px;
overflow: hidden;
transition: border-color 0.3s ease, background 0.3s ease;
}
.faq-item:hover:where([data-rk-13b0ac]) {
border-color: rgb(var(--ink-rgb, 255 255 255) / 0.18);
}
.faq-item[open]:where([data-rk-13b0ac]) {
background: rgba(20, 25, 50, 0.7);
border-color: rgba(99, 102, 241, 0.3);
}
.faq-question:where([data-rk-13b0ac]) {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.35rem 1.75rem;
font-size: 1.1rem;
font-weight: 700;
color: #fff;
cursor: pointer;
user-select: none;
list-style: none;
}
.faq-question:where([data-rk-13b0ac])::-webkit-details-marker {
display: none;
}
.faq-chevron:where([data-rk-13b0ac]) {
color: #94a3b8;
display: flex;
align-items: center;
transition: transform 0.3s ease, color 0.3s ease;
}
.faq-item[open]:where([data-rk-13b0ac]) .faq-chevron:where([data-rk-13b0ac]) {
transform: rotate(180deg);
color: #eab308;
}
.faq-answer:where([data-rk-13b0ac]) {
padding: 0 1.75rem 1.5rem;
color: #cbd5e1;
font-size: 0.95rem;
line-height: 1.7;
}
.faq-answer:where([data-rk-13b0ac]) p:where([data-rk-13b0ac]) {
margin: 0;
}
/* Support Box */
.faq-support-box:where([data-rk-13b0ac]) {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1.5rem;
padding: 1.75rem 2rem;
background: rgba(15, 23, 42, 0.6);
border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.1);
border-radius: 20px;
flex-wrap: wrap;
}
.support-icon:where([data-rk-13b0ac]) {
font-size: 2rem;
}
.support-info:where([data-rk-13b0ac]) {
flex: 1;
min-width: 240px;
}
.support-info:where([data-rk-13b0ac]) h4:where([data-rk-13b0ac]) {
font-size: 1.15rem;
font-weight: 800;
color: #fff;
margin: 0 0 0.25rem;
}
.support-info:where([data-rk-13b0ac]) p:where([data-rk-13b0ac]) {
font-size: 0.88rem;
color: #94a3b8;
margin: 0;
}
.support-btn:where([data-rk-13b0ac]) {
padding: 0.75rem 1.4rem;
background: rgb(var(--ink-rgb, 255 255 255) / 0.08);
border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.15);
border-radius: 10px;
color: #fff;
font-size: 0.9rem;
font-weight: 700;
text-decoration: none;
transition: all 0.25s ease;
}
.support-btn:hover:where([data-rk-13b0ac]) {
background: #eab308;
color: #000;
border-color: #eab308;
}