Skip to content
Caseroom

Blocks · Marketing

FAQ

Questions as native details: keyboard, find-in-page and no JavaScript.

29% of the 2,062 sites in The UI Index have a accordion (590 sites).

Questions

Can't find an answer? Write to us and we reply within a day.

Can I use it in client work?

Yes. Use it in as many personal and commercial projects as you like.

Does it work without React?

Every item comes as React, Astro and plain HTML, with the same classes and the same DOM.

How do I change the colours?

Change the CSS variables in your theme file. The names are the ones shadcn/ui uses, so any shadcn theme works too.

Both previews render the same markup; the HTML tab below is that markup without a framework.

Install

React
npx shadcn@latest add https://www.themes.dev/caseroom/r/faq.json
Astro
npx shadcn@latest add https://www.themes.dev/caseroom/r/astro/faq.json

Or copy the files below into your project. They need clsx, tailwind-merge and the components they import, and the Caseroom theme. The HTML needs only the compiled CSS.

faq.classes.ts
export const faq = {
  root: '@container bg-background px-4 py-20 sm:px-6 sm:py-24',
  inner: 'mx-auto grid max-w-5xl gap-10 @3xl:grid-cols-[1fr_1.6fr]',
  title: 'text-3xl font-semibold tracking-[-0.03em] text-balance',
  description: 'mt-4 text-base text-pretty text-muted-foreground',
  // Questions sit on their own grounds, separated by space, not by rules; an open one deepens.
  list: 'grid gap-2',
  item: 'group rounded-lg bg-muted/60 px-4 @md:px-5 transition-colors open:bg-muted has-[summary:active]:bg-pressed',
  summary:
    '-mx-4 flex cursor-pointer list-none items-center justify-between gap-4 rounded-lg px-4 py-4 text-[15px] font-medium text-pretty @md:-mx-5 @md:px-5 transition-colors hover:bg-accent active:bg-pressed [&::-webkit-details-marker]:hidden',
  icon: 'size-4 shrink-0 text-muted-foreground transition-transform group-open:rotate-45',
  answer: 'pb-5 pr-8 text-sm text-pretty text-muted-foreground',
};

export type FaqContent = { title: string; description?: string; items: { question: string; answer: string }[] };
faq.tsx
import { cn } from '@/lib/utils';
import { faq, type FaqContent } from './faq.classes';

export type FaqProps = FaqContent & { className?: string };

/** Questions as native <details>: they open with the keyboard, in find-in-page and without JavaScript. */
export function Faq({ title, description, items, className }: FaqProps) {
  return (
    <section className={cn(faq.root, className)}>
      <div className={faq.inner}>
        <div>
          <h2 className={faq.title}>{title}</h2>
          {description && <p className={faq.description}>{description}</p>}
        </div>
        <div className={faq.list}>
          {items.map((item) => (
            <details key={item.question} className={faq.item}>
              <summary className={faq.summary}>
                {item.question}
                <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" aria-hidden="true" className={faq.icon}>
                  <path d="M8 3v10M3 8h10" />
                </svg>
              </summary>
              <p className={faq.answer}>{item.answer}</p>
            </details>
          ))}
        </div>
      </div>
    </section>
  );
}