Sawana Huang Avatar

Sawana Huang

FAQ

Fri Sep 12 2025

Frequently Asked Questions accordion component with collapsible sections for organizing help content and common inquiries

FAQ Section

An accordion-style FAQ component for organizing frequently asked questions.

Usage

Coming soon - this component is under development.

FAQ Contact Card

交互式的 FAQ 组件,包含联系信息卡片和可展开的问答列表,适用于客户支持页面。

Contact Avatar
Still have questions?
Can't find the answer you're looking for? Please chat to our support.

"use client";import { FAQContactCard, type FAQContactCardData } from "./faq-contact-card";const sampleFAQData: FAQContactCardData = {  contact: {    title: "Still have questions?",    description:      "Can't find the answer you're looking for? Please chat to our support.",    avatarImage: "/avator/sawana-huang-212x212.webp",    button: "Get In Touch",    buttonHref: "/contact",    emailHref: "mailto:[email protected]",    githubHref: "https://github.com/example/support",  },  faqs: [    {      question: "What is this product?",      answer:        "This is a sample product description that explains the main features and benefits of our service.",    },    {      question: "How do I get started?",      answer:        "Getting started is simple. Just sign up for an account and follow our quick setup guide.",    },    {      question: "Is there a free plan?",      answer:        "Yes, we offer a free tier with basic features to help you get started.",    },    {      question: "How can I contact support?",      answer:        "You can reach our support team through email, GitHub, or by using the contact button above.",    },  ],};export function FAQContactCardDemo() {  return (    <div className="min-h-[600px] w-full">      <FAQContactCard data={sampleFAQData} />    </div>  );}

功能特点

  • shadcn/ui 设计: 基于 Card 和 Accordion 组件构建,保持设计系统一致性
  • 响应式布局: 移动端垂直布局,桌面端双栏布局
  • 原生 Accordion: 使用 shadcn Accordion 组件,支持键盘导航和动画
  • 联系信息卡片: 结构化 Card 布局,包含头像、标题、描述和操作区域
  • 社交媒体链接: 可配置的邮件和 GitHub 链接支持
  • 完全可配置: 所有文本内容和链接均通过 props 传入
  • 主题兼容: 支持 Light/Dark 模式切换

Code

@/components/faq/faq-contact-card.tsx
"use client";

import React from "react";
import { Button } from "@/components/ui/button";
import {
  Card,
  CardHeader,
  CardTitle,
  CardDescription,
  CardContent,
  CardFooter,
} from "@/components/ui/card";
import {
  Accordion,
  AccordionItem,
  AccordionTrigger,
  AccordionContent,
} from "@/components/ui/accordion";
import Link from "next/link";
import { cn } from "@/lib/utils";
import { Mail, Github } from "lucide-react";

interface FAQItem {
  question: string;
  answer: string;
}

interface ContactInfo {
  title: string;
  description: string;
  avatarImage: string;
  button: string;
  buttonHref: string;
  emailHref: string;
  githubHref: string;
}

interface FAQContactCardData {
  contact: ContactInfo;
  faqs: FAQItem[];
}

interface FAQContactCardProps {
  data: FAQContactCardData;
  className?: string;
}

export function FAQContactCard({ data, className }: FAQContactCardProps) {
  return (
    <div
      className={cn(
        "flex flex-col items-start lg:flex-row lg:space-x-6",
        className,
      )}
    >
      {/* Contact Card */}
      <div className="w-full flex-none lg:flex-[1_1_500px]">
        <Card className="mb-6 h-full">
          <CardHeader>
            <div className="flex flex-row gap-4">
              <img
                src={data.contact.avatarImage}
                alt="Contact Avatar"
                className="inline-block h-12 w-12 rounded-full object-cover"
                loading="lazy"
              />
              <div className="flex flex-col gap-1.5">
                <CardTitle className="text-xl">{data.contact.title}</CardTitle>
                <CardDescription className="max-w-sm">
                  {data.contact.description}
                </CardDescription>
              </div>
            </div>
          </CardHeader>

          <CardContent>
            <div className="flex items-center space-x-4">
              <Link
                href={data.contact.emailHref}
                aria-label="Email Support"
                target="_blank"
              >
                <div className="bg-primary/10 hover:bg-primary/20 flex h-10 w-10 items-center justify-center rounded-full transition-colors">
                  <Mail className="text-primary h-5 w-5" />
                </div>
              </Link>
              <Link
                href={data.contact.githubHref}
                target="_blank"
                aria-label="GitHub Support"
              >
                <div className="bg-primary/10 hover:bg-primary/20 flex h-10 w-10 items-center justify-center rounded-full transition-colors">
                  <Github className="text-primary h-5 w-5" />
                </div>
              </Link>
            </div>
          </CardContent>

          <CardFooter>
            <Link href={data.contact.buttonHref}>
              <Button className="w-full">{data.contact.button}</Button>
            </Link>
          </CardFooter>
        </Card>
      </div>

      {/* FAQ Cards with Accordion */}
      <div className="w-full flex-none lg:flex-[1_1_500px]">
        <Accordion type="single" collapsible className="w-full space-y-4">
          {data.faqs.map((faq, index) => (
            <AccordionItem
              key={index}
              value={`item-${index}`}
              className="border-0"
            >
              <Card>
                <CardHeader className="pb-3">
                  <AccordionTrigger className="p-0 text-left hover:no-underline [&[data-state=open]>svg]:rotate-180">
                    <CardTitle className="text-base font-semibold">
                      {faq.question}
                    </CardTitle>
                  </AccordionTrigger>
                </CardHeader>
                <AccordionContent className="pt-0">
                  <CardContent className="pt-0">
                    <p className="text-muted-foreground text-sm leading-relaxed">
                      {faq.answer}
                    </p>
                  </CardContent>
                </AccordionContent>
              </Card>
            </AccordionItem>
          ))}
        </Accordion>
      </div>
    </div>
  );
}

export type { FAQContactCardData, FAQItem, ContactInfo };