Web InnoventixFreeCode

FAQ Two Col Accordion

Original · free

A two-column FAQ accordion layout.

byWeb InnoventixReact + Tailwind
accordionfaqtwocolaccordions
Open

Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.

npx shadcn@latest add https://webinnoventix.com/r/accordion-faq-two-col.json
accordion-faq-two-col.tsx
"use client";

import { useId, useState } from "react";

type FaqItem = {
  q: string;
  a: string;
};

const LEFT_COLUMN: FaqItem[] = [
  {
    q: "How long does a typical website build take?",
    a: "Most marketing sites ship in 3 to 5 weeks. A one-page launch site can go live in about 8 days, while a multi-template site with a CMS and custom animations usually runs 6 to 8 weeks. You get a dated milestone plan on day one so nothing slips quietly.",
  },
  {
    q: "Do I own the code and design files after launch?",
    a: "Yes, fully. On final payment we transfer the Git repository, the Figma source, and every asset license to your accounts. There is no vendor lock-in and no monthly ransom to keep your own site online.",
  },
  {
    q: "Can you work with our existing brand guidelines?",
    a: "Absolutely. Send over your logo, type scale, and color tokens and we build inside those constraints. If your guidelines are thin, we extend them into a small design system so every future page stays on-brand without guesswork.",
  },
  {
    q: "What do you need from me to get started?",
    a: "A short kickoff call, access to your domain and hosting, any existing copy or brand assets, and one decision-maker who can approve milestones. We handle the rest, from wireframes to deployment.",
  },
];

const RIGHT_COLUMN: FaqItem[] = [
  {
    q: "Is the site optimized for search and speed?",
    a: "Every build ships with semantic HTML, image compression, lazy loading, and a Lighthouse pass targeting 90+ on performance and SEO. We wire up meta tags, structured data, and a sitemap before we call it done.",
  },
  {
    q: "Will the site be responsive on phones and tablets?",
    a: "Mobile-first is the default, not an add-on. We design at 375px first and scale up, then test on real devices across iOS and Android so buttons stay tappable and text stays readable everywhere.",
  },
  {
    q: "Do you offer support after the site goes live?",
    a: "Yes. Every project includes 30 days of free bug fixes, and we offer optional monthly care plans covering updates, backups, uptime monitoring, and small content edits so you are never stuck.",
  },
  {
    q: "How does pricing and payment work?",
    a: "You get a fixed quote after the scoping call, split into a 40% deposit and a 60% balance on launch. No hourly surprises. If scope changes mid-project, we flag the cost before doing the work.",
  },
];

function AccordionRow({
  item,
  isOpen,
  onToggle,
}: {
  item: FaqItem;
  isOpen: boolean;
  onToggle: () => void;
}) {
  const baseId = useId();
  const buttonId = `${baseId}-btn`;
  const panelId = `${baseId}-panel`;

  return (
    <div className="overflow-hidden rounded-2xl border border-slate-200 bg-white/70 transition-colors duration-300 hover:border-indigo-300 dark:border-slate-800 dark:bg-slate-900/50 dark:hover:border-indigo-500/60">
      <h3 className="m-0">
        <button
          type="button"
          id={buttonId}
          aria-expanded={isOpen}
          aria-controls={panelId}
          onClick={onToggle}
          className="flex w-full items-center justify-between gap-4 px-5 py-4 text-left text-[0.975rem] font-semibold text-slate-900 outline-none transition-colors duration-200 focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:text-slate-100 dark:focus-visible:ring-offset-slate-950"
        >
          <span>{item.q}</span>
          <span
            aria-hidden="true"
            className={`grid h-7 w-7 shrink-0 place-items-center rounded-full border transition-all duration-300 ${
              isOpen
                ? "rotate-180 border-indigo-500 bg-indigo-500 text-white dark:border-indigo-400 dark:bg-indigo-500"
                : "border-slate-300 bg-transparent text-slate-500 dark:border-slate-700 dark:text-slate-400"
            }`}
          >
            <svg
              width="14"
              height="14"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              strokeWidth="2.5"
              strokeLinecap="round"
              strokeLinejoin="round"
            >
              <path d="m6 9 6 6 6-6" />
            </svg>
          </span>
        </button>
      </h3>
      <div
        id={panelId}
        role="region"
        aria-labelledby={buttonId}
        className="grid transition-[grid-template-rows] duration-300 ease-out"
        style={{ gridTemplateRows: isOpen ? "1fr" : "0fr" }}
      >
        <div className="overflow-hidden">
          <p className="m-0 px-5 pb-5 pt-0 text-[0.9rem] leading-relaxed text-slate-600 dark:text-slate-400">
            {item.a}
          </p>
        </div>
      </div>
    </div>
  );
}

export default function AccordionFaqTwoCol() {
  const styleId = "afqtc-anim";
  const [openKey, setOpenKey] = useState<string | null>("L-0");

  const toggle = (key: string) => {
    setOpenKey((prev) => (prev === key ? null : key));
  };

  return (
    <section className="relative w-full overflow-hidden bg-slate-50 px-6 py-20 sm:py-28 dark:bg-slate-950">
      <style>{`
        @keyframes afqtc-rise {
          0% { opacity: 0; transform: translateY(16px); }
          100% { opacity: 1; transform: translateY(0); }
        }
        .afqtc-rise { animation: afqtc-rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) both; }
        @media (prefers-reduced-motion: reduce) {
          .afqtc-rise { animation: none; }
        }
      `}</style>
      <span id={styleId} className="hidden" aria-hidden="true" />

      <div
        aria-hidden="true"
        className="pointer-events-none absolute left-1/2 top-0 h-72 w-72 -translate-x-1/2 rounded-full bg-indigo-300/25 blur-3xl dark:bg-indigo-600/15"
      />

      <div className="relative mx-auto max-w-5xl">
        <div className="afqtc-rise mx-auto mb-14 max-w-2xl text-center">
          <span className="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-indigo-50 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-indigo-700 dark:border-indigo-500/30 dark:bg-indigo-500/10 dark:text-indigo-300">
            <span className="h-1.5 w-1.5 rounded-full bg-indigo-500" />
            Support
          </span>
          <h2 className="mt-4 text-3xl font-bold tracking-tight text-slate-900 sm:text-4xl dark:text-white">
            Frequently asked questions
          </h2>
          <p className="mt-3 text-base text-slate-600 dark:text-slate-400">
            Everything you need to know before we start building. Still curious?
            Reach out and a human will answer within a day.
          </p>
        </div>

        <div className="grid gap-4 md:grid-cols-2 md:gap-5">
          <div className="flex flex-col gap-4">
            {LEFT_COLUMN.map((item, i) => {
              const key = `L-${i}`;
              return (
                <AccordionRow
                  key={key}
                  item={item}
                  isOpen={openKey === key}
                  onToggle={() => toggle(key)}
                />
              );
            })}
          </div>
          <div className="flex flex-col gap-4">
            {RIGHT_COLUMN.map((item, i) => {
              const key = `R-${i}`;
              return (
                <AccordionRow
                  key={key}
                  item={item}
                  isOpen={openKey === key}
                  onToggle={() => toggle(key)}
                />
              );
            })}
          </div>
        </div>
      </div>
    </section>
  );
}

Dependencies

None (React + Tailwind only).

Licence

Built by Web Innoventix. Free for personal and commercial use, no attribution required.

Built by Web Innoventix

Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.

Get a free quote

Similar components

Browse all →