FAQ Two Column
FAQ в две колонки с закреплённым вступлением и нативными аккордеонами <details> без JS.
Frequently asked questions
Everything you need to know about the kit. Can’t find an answer? Our team replies within a day.
Contact supportDo I need to install a package?
No. Every component is a standalone .astro file. Copy it into your project, or install the package if you prefer imports.
Do the charts need a JavaScript library?
No. The SVG charts render at build time. A tiny shared script only powers hover tooltips, and every chart includes a data table fallback.
Is it accessible?
Components target WCAG 2.2 AA: semantic HTML, visible focus states, keyboard support and contrast of at least 4.5:1 for text.
Can I use it in commercial projects?
Yes. The kit is MIT licensed. Use it for client work, SaaS products and anything else.
How do I change the colors?
Edit the CSS custom properties in global.css. Every component reads from the same tokens, so one change updates everything.
Использование
Скопируйте и вставьте этот код на вашу страницу Astro:
---
import { FaqTwoColumn } from 'astro-component-kit/sections';
---
<FaqTwoColumn
items={[
{ q: 'Is it free?', a: 'Yes, MIT licensed.' },
{ q: 'Do I need JS?', a: 'No.' },
]}
exclusive
/> ---
import { FaqTwoColumn } from 'astro-component-kit/sections';
---
<FaqTwoColumn
items={[
{ q: 'Is it free?', a: 'Yes, MIT licensed.' },
{ q: 'Do I need JS?', a: 'No.' },
]}
exclusive
/> Ручная установка
Если вы не используете пакет npm, создайте файл и вставьте следующий код: src/components/sections/FAQTwoColumn.astro:
---
/**
* FaqTwoColumn — FAQ section with a sticky intro column (heading + support CTA)
* and native <details> accordions on the right. Works without JavaScript.
*
* @param {string} title - Section heading.
* @param {string} subtitle - Intro copy.
* @param {Array<{q: string, a: string}>} items - Questions and answers.
* @param {{label: string, href: string}} support - Support link.
* @param {boolean} exclusive - Only one item open at a time (uses the `name` attribute).
* @param {string} variant - Item style (bordered|cards|plain).
* @param {string} size - Vertical rhythm (sm|md|lg).
* @param {string} className - Extra CSS class.
*/
interface Props {
title?: string;
subtitle?: string;
items?: Array<{ q: string; a: string }>;
support?: { label: string; href: string };
exclusive?: boolean;
variant?: 'bordered' | 'cards' | 'plain';
size?: 'sm' | 'md' | 'lg';
className?: string;
}
const {
title = 'Frequently asked questions',
subtitle = 'Everything you need to know about the kit. Can’t find an answer? Our team replies within a day.',
items = [
{ q: 'Do I need to install a package?', a: 'No. Every component is a standalone .astro file. Copy it into your project, or install the package if you prefer imports.' },
{ q: 'Do the charts need a JavaScript library?', a: 'No. The SVG charts render at build time. A tiny shared script only powers hover tooltips, and every chart includes a data table fallback.' },
{ q: 'Is it accessible?', a: 'Components target WCAG 2.2 AA: semantic HTML, visible focus states, keyboard support and contrast of at least 4.5:1 for text.' },
{ q: 'Can I use it in commercial projects?', a: 'Yes. The kit is MIT licensed. Use it for client work, SaaS products and anything else.' },
{ q: 'How do I change the colors?', a: 'Edit the CSS custom properties in global.css. Every component reads from the same tokens, so one change updates everything.' },
],
support = { label: 'Contact support', href: '#' },
exclusive = true,
variant = 'bordered',
size = 'md',
className = '',
} = Astro.props;
const uid = Math.random().toString(36).slice(2, 8);
const id = `ftc-${uid}`;
---
<div class="cq-faq-two-column-cq">
<section class={`ftc ftc--${variant} ftc--${size} ${className}`} aria-labelledby={id}>
<div class="ftc__inner">
<div class="ftc__intro">
<h2 id={id} class="ftc__title">{title}</h2>
<p class="ftc__subtitle">{subtitle}</p>
{support && (
<a class="ftc__support" href={support.href}>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" /></svg>
{support.label}
</a>
)}
</div>
<div class="ftc__list">
{items.map((it, i) => (
<details class="ftc__item" name={exclusive ? `faq-${uid}` : undefined} open={i === 0}>
<summary class="ftc__q">
<span>{it.q}</span>
<svg class="ftc__chev" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
</summary>
<div class="ftc__a"><p>{it.a}</p></div>
</details>
))}
</div>
</div>
</section>
</div>
<style>
.cq-faq-two-column-cq { container-type: inline-size; }
.ftc {
--ftc-py: 5.5rem;
padding: var(--ftc-py) var(--sp-6, 1.5rem);
background: var(--c-bg, #050505);
}
.ftc--sm { --ftc-py: 3.5rem; }
.ftc--lg { --ftc-py: 7.5rem; }
.ftc__inner { max-width: 1120px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1.6fr; gap: clamp(2rem, 6vw, 5rem); align-items: start; }
.ftc__intro { position: sticky; top: 6rem; }
.ftc__title { margin: 0 0 1rem; font-size: clamp(1.9rem, 4vw, 2.5rem); font-weight: 800; letter-spacing: -0.03em; line-height: 1.12; color: var(--c-text-1, #fff); }
.ftc__subtitle { margin: 0 0 1.75rem; font-size: 1rem; line-height: 1.65; color: var(--c-text-2, #a3a3a3); }
.ftc__support { display: inline-flex; align-items: center; gap: 0.5rem; min-height: 46px; padding: 0 1.25rem; border-radius: var(--r-md, 12px); border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.14); color: var(--c-text-1, #fff); font-weight: 600; text-decoration: none; transition: border-color 0.2s ease; }
.ftc__support:hover { border-color: rgb(var(--ink-rgb, 255 255 255) / 0.32); color: var(--c-text-1, #fff); }
.ftc__support:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 2px; }
.ftc__list { display: grid; }
.ftc--cards .ftc__list { gap: 0.75rem; }
.ftc--bordered .ftc__item { border-bottom: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08); }
.ftc--bordered .ftc__item:first-child { border-top: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08); }
.ftc--cards .ftc__item { border-radius: var(--r-md, 14px); background: rgb(var(--ink-rgb, 255 255 255) / 0.025); border: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.07)); padding-inline: 1.25rem; }
.ftc__q {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
min-height: 64px;
padding: 1rem 0;
cursor: pointer;
list-style: none;
font-size: 1.0625rem;
font-weight: 600;
color: var(--c-text-1, #fff);
}
.ftc__q::-webkit-details-marker { display: none; }
.ftc__q:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 2px; border-radius: 6px; }
.ftc__chev { flex-shrink: 0; color: var(--c-text-2, #a3a3a3); transition: transform 0.25s ease; }
.ftc__item[open] .ftc__chev { transform: rotate(45deg); color: var(--c-primary-light, #d4a017); }
.ftc__a { padding: 0 2.5rem 1.25rem 0; }
.ftc__a p { margin: 0; font-size: 0.9875rem; line-height: 1.7; color: var(--c-text-2, #a3a3a3); }
.ftc__item[open] .ftc__a { animation: ftc-in 0.25s ease; }
@keyframes ftc-in { from { opacity: 0; transform: translateY(-4px); } }
@container (max-width: 820px) {
.ftc__inner { grid-template-columns: 1fr; }
.ftc__intro { position: static; }
}
@media (prefers-reduced-motion: reduce) { .ftc__chev { transition: none; } .ftc__item[open] .ftc__a { animation: none; } }
</style> ---
/**
* FaqTwoColumn — FAQ section with a sticky intro column (heading + support CTA)
* and native <details> accordions on the right. Works without JavaScript.
*
* @param {string} title - Section heading.
* @param {string} subtitle - Intro copy.
* @param {Array<{q: string, a: string}>} items - Questions and answers.
* @param {{label: string, href: string}} support - Support link.
* @param {boolean} exclusive - Only one item open at a time (uses the `name` attribute).
* @param {string} variant - Item style (bordered|cards|plain).
* @param {string} size - Vertical rhythm (sm|md|lg).
* @param {string} className - Extra CSS class.
*/
interface Props {
title?: string;
subtitle?: string;
items?: Array<{ q: string; a: string }>;
support?: { label: string; href: string };
exclusive?: boolean;
variant?: 'bordered' | 'cards' | 'plain';
size?: 'sm' | 'md' | 'lg';
className?: string;
}
const {
title = 'Frequently asked questions',
subtitle = 'Everything you need to know about the kit. Can’t find an answer? Our team replies within a day.',
items = [
{ q: 'Do I need to install a package?', a: 'No. Every component is a standalone .astro file. Copy it into your project, or install the package if you prefer imports.' },
{ q: 'Do the charts need a JavaScript library?', a: 'No. The SVG charts render at build time. A tiny shared script only powers hover tooltips, and every chart includes a data table fallback.' },
{ q: 'Is it accessible?', a: 'Components target WCAG 2.2 AA: semantic HTML, visible focus states, keyboard support and contrast of at least 4.5:1 for text.' },
{ q: 'Can I use it in commercial projects?', a: 'Yes. The kit is MIT licensed. Use it for client work, SaaS products and anything else.' },
{ q: 'How do I change the colors?', a: 'Edit the CSS custom properties in global.css. Every component reads from the same tokens, so one change updates everything.' },
],
support = { label: 'Contact support', href: '#' },
exclusive = true,
variant = 'bordered',
size = 'md',
className = '',
} = Astro.props;
const uid = Math.random().toString(36).slice(2, 8);
const id = `ftc-${uid}`;
---
<div class="cq-faq-two-column-cq">
<section class={`ftc ftc--${variant} ftc--${size} ${className}`} aria-labelledby={id}>
<div class="ftc__inner">
<div class="ftc__intro">
<h2 id={id} class="ftc__title">{title}</h2>
<p class="ftc__subtitle">{subtitle}</p>
{support && (
<a class="ftc__support" href={support.href}>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" /></svg>
{support.label}
</a>
)}
</div>
<div class="ftc__list">
{items.map((it, i) => (
<details class="ftc__item" name={exclusive ? `faq-${uid}` : undefined} open={i === 0}>
<summary class="ftc__q">
<span>{it.q}</span>
<svg class="ftc__chev" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
</summary>
<div class="ftc__a"><p>{it.a}</p></div>
</details>
))}
</div>
</div>
</section>
</div>
<style>
.cq-faq-two-column-cq { container-type: inline-size; }
.ftc {
--ftc-py: 5.5rem;
padding: var(--ftc-py) var(--sp-6, 1.5rem);
background: var(--c-bg, #050505);
}
.ftc--sm { --ftc-py: 3.5rem; }
.ftc--lg { --ftc-py: 7.5rem; }
.ftc__inner { max-width: 1120px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1.6fr; gap: clamp(2rem, 6vw, 5rem); align-items: start; }
.ftc__intro { position: sticky; top: 6rem; }
.ftc__title { margin: 0 0 1rem; font-size: clamp(1.9rem, 4vw, 2.5rem); font-weight: 800; letter-spacing: -0.03em; line-height: 1.12; color: var(--c-text-1, #fff); }
.ftc__subtitle { margin: 0 0 1.75rem; font-size: 1rem; line-height: 1.65; color: var(--c-text-2, #a3a3a3); }
.ftc__support { display: inline-flex; align-items: center; gap: 0.5rem; min-height: 46px; padding: 0 1.25rem; border-radius: var(--r-md, 12px); border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.14); color: var(--c-text-1, #fff); font-weight: 600; text-decoration: none; transition: border-color 0.2s ease; }
.ftc__support:hover { border-color: rgb(var(--ink-rgb, 255 255 255) / 0.32); color: var(--c-text-1, #fff); }
.ftc__support:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 2px; }
.ftc__list { display: grid; }
.ftc--cards .ftc__list { gap: 0.75rem; }
.ftc--bordered .ftc__item { border-bottom: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08); }
.ftc--bordered .ftc__item:first-child { border-top: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08); }
.ftc--cards .ftc__item { border-radius: var(--r-md, 14px); background: rgb(var(--ink-rgb, 255 255 255) / 0.025); border: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.07)); padding-inline: 1.25rem; }
.ftc__q {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
min-height: 64px;
padding: 1rem 0;
cursor: pointer;
list-style: none;
font-size: 1.0625rem;
font-weight: 600;
color: var(--c-text-1, #fff);
}
.ftc__q::-webkit-details-marker { display: none; }
.ftc__q:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 2px; border-radius: 6px; }
.ftc__chev { flex-shrink: 0; color: var(--c-text-2, #a3a3a3); transition: transform 0.25s ease; }
.ftc__item[open] .ftc__chev { transform: rotate(45deg); color: var(--c-primary-light, #d4a017); }
.ftc__a { padding: 0 2.5rem 1.25rem 0; }
.ftc__a p { margin: 0; font-size: 0.9875rem; line-height: 1.7; color: var(--c-text-2, #a3a3a3); }
.ftc__item[open] .ftc__a { animation: ftc-in 0.25s ease; }
@keyframes ftc-in { from { opacity: 0; transform: translateY(-4px); } }
@container (max-width: 820px) {
.ftc__inner { grid-template-columns: 1fr; }
.ftc__intro { position: static; }
}
@media (prefers-reduced-motion: reduce) { .ftc__chev { transition: none; } .ftc__item[open] .ftc__a { animation: none; } }
</style>