Skip to content

Faq Search

Searchable FAQ: rounded search field plus topic pills filter a list of questions (native details), with a friendly empty state and live result count.

organic-soft/section/faq-search
Open ↗

Source

"use client";

import { useId, useMemo, useState } from "react";
import { Search } from "lucide-react";
import { cn } from "@/lib/utils";

export interface FaqSearchProps {
  title?: string;
  items?: { q: string; a: string; topic: string }[];
  className?: string;
}

const DEFAULT_ITEMS = [
  {
    q: "What are scopes 1, 2 and 3?",
    a: "Scope 1 is what you burn directly, scope 2 the energy you buy, scope 3 everything in your value chain — usually 80%+ of the total.",
    topic: "Basics",
  },
  { q: "How often is my footprint updated?", a: "Every night from connected tools; monthly for uploaded bills.", topic: "Product" },
  { q: "Can I export my data?", a: "Always — CSV, XLSX or via API, at any time.", topic: "Product" },
  { q: "Is Grove audited?", a: "Our methodology is reviewed annually by an independent third party (Carbone 4).", topic: "Trust" },
  { q: "How do you price?", a: "Per employee per month, free under 20 people.", topic: "Billing" },
  { q: "Can we pay yearly?", a: "Yes, with a 15% discount.", topic: "Billing" },
];

/** Searchable FAQ: rounded search field plus topic pills filter a list of questions (native details), with a friendly empty state and live result count. */
export function FaqSearch({ title = "How can we help?", items = DEFAULT_ITEMS, className }: FaqSearchProps) {
  const id = useId();
  const [q, setQ] = useState("");
  const [topic, setTopic] = useState("All");
  const topics = ["All", ...new Set(items.map((i) => i.topic))];
  const list = useMemo(
    () => items.filter((i) => (topic === "All" || i.topic === topic) && (i.q + i.a).toLowerCase().includes(q.trim().toLowerCase())),
    [items, q, topic],
  );
  return (
    <section aria-labelledby={`${id}-t`} className={cn("da-section bg-da-surface-2 text-da-fg", className)}>
      <div className="da-container max-w-3xl">
        <h2 id={`${id}-t`} className="text-center font-da-display text-[clamp(2.25rem,5vw,3.5rem)] tracking-da-display">
          {title}
        </h2>
        <label className="mx-auto mt-10 flex h-14 items-center gap-3 rounded-da-pill bg-da-surface px-6 shadow-da-md focus-within:ring-3 focus-within:ring-da-ring">
          <Search aria-hidden className="size-5 text-da-muted-fg" />
          <span className="sr-only">Search questions</span>
          <input
            type="search"
            value={q}
            onChange={(e) => setQ(e.target.value)}
            placeholder="Search e.g. “scope 3”"
            className="min-w-0 flex-1 bg-transparent text-lg outline-none placeholder:text-da-muted-fg"
          />
        </label>
        <div role="group" aria-label="Topics" className="mt-5 flex flex-wrap justify-center gap-2">
          {topics.map((t) => (
            <button
              key={t}
              type="button"
              aria-pressed={t === topic}
              onClick={() => setTopic(t)}
              className={cn(
                "da-focus da-transition rounded-da-pill px-4 py-1.5 text-sm",
                t === topic ? "bg-da-primary text-da-primary-fg" : "bg-da-surface hover:bg-da-muted",
              )}
            >
              {t}
            </button>
          ))}
        </div>
        <p role="status" className="mt-8 text-sm text-da-muted-fg">
          {list.length} {list.length === 1 ? "answer" : "answers"}
        </p>
        {list.length ? (
          <div className="mt-3 grid gap-2">
            {list.map((it) => (
              <details key={it.q} className="group rounded-da-md bg-da-surface text-da-surface-fg">
                <summary className="da-focus flex cursor-pointer list-none items-center justify-between gap-4 rounded-da-md px-6 py-4 font-medium [&::-webkit-details-marker]:hidden">
                  {it.q}
                  <span className="shrink-0 rounded-da-pill bg-da-surface-2 px-2.5 py-0.5 text-xs text-da-muted-fg">{it.topic}</span>
                </summary>
                <p className="px-6 pb-5 text-da-muted-fg">{it.a}</p>
              </details>
            ))}
          </div>
        ) : (
          <p className="mt-3 rounded-da-lg bg-da-surface p-8 text-center text-da-muted-fg">
            Nothing yet for “{q}”.{" "}
            <a href="mailto:hello@grove.eco" className="da-focus rounded-da-sm font-medium text-da-primary underline underline-offset-4">
              Ask a human
            </a>
          </p>
        )}
      </div>
    </section>
  );
}

export default FaqSearch;

modules/organic-soft/section/faq-search/index.tsx

Props

PropTypeDefaultDescription
titlestring—Heading.
items{ q: string; a: string; topic: string }[]—Content items (see the exported types).
classNamestring—Extra classes on the root.

Other faq variants in Organic Soft

FAQ in other art directions

FAQ Accordion

Two-column FAQ: sticky title + support CTA on the left, numbered accordion cards on the right with a yellow plus that rotates into an ink cross when open. Built on Radix Accordion.

BrutalistFAQ

FAQ Categories

Help-center style FAQ: topic buttons with counts on the left (stacked on desktop, wrapped on mobile), and a ruled accordion on the right whose open question floods yellow.

BrutalistFAQ

FAQ Grid

Always-open FAQ grid: ruled cells (1/2/3 columns) with a yellow question-mark square, uppercase question and answer, closed by a full-width ink contact row. No interaction needed.

BrutalistFAQ

FAQ Search

Searchable FAQ: a huge ruled search field that presses in on focus, live result count, and native <details> cards with tags; a pink empty state points to support.

BrutalistFAQ

Faq Cards

All-open FAQ: a 3-column grid of glass cards, each with a gradient number badge, question and answer, followed by a glass contact pill with an ink button.

GlassFAQ

Faq Chat

FAQ as a chat: suggested question chips on the left; picking one adds a gradient user bubble and, after a typing indicator, a glass answer bubble in the conversation panel.

GlassFAQ

FAQ Glass

Centered FAQ with stacked frosted accordion cards; the plus button spins into a gradient cross with a slight overshoot and answers expand smoothly. Built on Radix Accordion.

GlassFAQ

Faq Search

Help-center FAQ: large glass search pill filtering a frosted Radix accordion live, with category tags, result count and highlighted matches; empty state.

GlassFAQ

Faq Grid

All answers visible at once: a three-column grid of short questions and answers under a header, followed by two support link cards (documentation, talk to us).

MinimalFAQ

Faq List

Centered single-column FAQ built on native details/summary (zero JS): hairline dividers, a plus icon that rotates into a cross when open, and a contact line underneath.

MinimalFAQ

FAQ Split

Two-column FAQ: title, description and support link on the left, hairline-divided Radix accordion on the right with a rotating chevron and a smooth height + fade expand.

MinimalFAQ

Faq Tabs

FAQ grouped by category behind an underline tab bar (Radix Tabs, with counts); the active category shows every question and answer in a two-column definition list.

MinimalFAQ

Faq Inline

Ultra-compact FAQ: short questions and one-line answers flowing as a 3-column grid of mono question labels over large answers — scannable in seconds.

Mono CleanFAQ

Faq List

Numbered accordion FAQ in a ruled list: mono index + question, a +/− glyph that swaps, answer indented under the question; left column holds the label and title.

Mono CleanFAQ

Faq Split

Always-open FAQ as a two-column definition list (question left in medium weight, answer right in muted text) on hairline rows, followed by a mono contact line.

Mono CleanFAQ

Faq Tabs

FAQ grouped by topic with text tabs (Radix Tabs) underlined in ink when active, and each topic’s answers as a ruled definition list.

Mono CleanFAQ

Faq Accordion

Two-column FAQ: heading + description + contact link on the left, bordered Radix accordion with plus icons rotating to × on the right.

Neo CorporateFAQ

Faq Security

Security & compliance FAQ: dark-tinted certification cards (SOC 2, ISO 27001, GDPR, PCI) with shield icons, then security questions as native <details> disclosure rows.

Neo CorporateFAQ

Faq Sidebar

Help-center style FAQ: a vertical category nav (left, becomes horizontal scroll chips on mobile) and the selected category’s questions as an open definition list.

Neo CorporateFAQ

Faq Support

Compact 2×3 grid of short Q&As followed by three bordered support channel cards (docs, chat, dedicated support).

Neo CorporateFAQ

Faq Columns

All-visible FAQ: sticky intro with a peach “Chat with us” pill on the left, six Q&As in a 2-column grid with round lavender number badges on the right.

Soft FlatFAQ

Faq Help

Help-center FAQ: lavender panel with a friendly greeting, big white search pill and popular-search chips; matching answers show as white cards with a peach empty state.

Soft FlatFAQ

Faq Pills

Single-column FAQ of rounded white blocks on native details/summary; the open item turns lavender and its round chevron button flips and fills periwinkle.

Soft FlatFAQ

Faq Tinted

FAQ by topic: three big pastel topic buttons (emoji + label) switch the list of white Q&A cards below, which fades in on change.

Soft FlatFAQ