

View Details
FAQ
タブ切り替えFAQ
#ミニマル#インタラクティブ+1
カテゴリごとに分類されたFAQを2カラムで表示する、整理されたアコーディオンセクション
1"use client";23import { useState } from "react";45function CornerDots({ className = "" }: { className?: string }) {6 return (7 <div className={`absolute h-3 w-3 ${className}`}>8 <div className="absolute left-0 top-0 h-1.5 w-1.5 rounded-full bg-muted-foreground/40" />9 <div className="absolute right-0 top-0 h-1.5 w-1.5 rounded-full bg-muted-foreground/40" />10 <div className="absolute bottom-0 left-0 h-1.5 w-1.5 rounded-full bg-muted-foreground/40" />11 <div className="absolute bottom-0 right-0 h-1.5 w-1.5 rounded-full bg-muted-foreground/40" />12 </div>13 );14}1516interface FaqItem {17 question: string;18 answer: string;19}2021interface FaqCategory {22 title: string;23 items: FaqItem[];24}2526export function FaqColumns001() {27 const [openItems, setOpenItems] = useState<Record<string, boolean>>({});2829 const toggleItem = (key: string) => {30 setOpenItems((prev) => ({ ...prev, [key]: !prev[key] }));31 };3233 const categories: FaqCategory[] = [34 {35 title: "はじめに",36 items: [37 {38 question: "アカウント登録に必要なものは何ですか?",39 answer:40 "メールアドレスのみで登録いただけます。クレジットカードの登録は、有料プランへの移行時に必要となります。",41 },42 {43 question: "無料プランの制限を教えてください",44 answer:45 "無料プランでは、3プロジェクトまで作成可能で、1GBのストレージをご利用いただけます。基本的な機能はすべてお使いいただけます。",46 },47 {48 question: "データの移行はサポートされていますか?",49 answer:50 "CSV、JSON形式でのインポートに対応しています。大規模なデータ移行については、専任チームがサポートいたします。",51 },52 ],53 },54 {55 title: "料金・プラン",56 items: [57 {58 question: "支払い方法にはどのようなものがありますか?",59 answer:60 "クレジットカード(Visa、Mastercard、AMEX)および銀行振込に対応しています。年間契約の場合は請求書払いも可能です。",61 },62 {63 question: "プランの変更はいつでもできますか?",64 answer:65 "はい、いつでもアップグレード・ダウングレードが可能です。差額は日割りで計算されます。",66 },67 {68 question: "解約時にデータはどうなりますか?",69 answer:70 "解約後30日間はデータを保持します。その間にエクスポートいただくか、再契約いただければデータは維持されます。",71 },72 ],73 },74 ];7576 return (77 <section className="relative bg-background py-28">78 <CornerDots className="left-6 top-6" />79 <CornerDots className="right-6 top-6" />80 <CornerDots className="bottom-6 left-6" />81 <CornerDots className="bottom-6 right-6" />8283 <div className="mx-auto max-w-5xl px-4 sm:px-6 lg:px-8">84 {/* ヘッダー */}85 <div className="mb-20">86 <p className="text-[10px] uppercase tracking-[0.3em] text-muted-foreground">87 FAQ88 </p>89 <div className="mt-4 h-px w-12 bg-border/40" />90 <h2 className="mt-6 text-2xl font-light tracking-wide text-foreground sm:text-3xl">91 よくあるご質問92 </h2>93 </div>9495 {/* カテゴリ別カラムレイアウト */}96 <div className="grid grid-cols-1 gap-16 lg:grid-cols-2">97 {categories.map((category, catIndex) => (98 <div key={catIndex}>99 <p className="text-xs uppercase tracking-[0.2em] text-muted-foreground">100 {category.title}101 </p>102 <div className="mt-4 h-px bg-border/40" />103104 <div className="mt-6 space-y-0 divide-y divide-border/30">105 {category.items.map((item, itemIndex) => {106 const key = `${catIndex}-${itemIndex}`;107 const isOpen = openItems[key] ?? false;108109 return (110 <div key={itemIndex} className="py-5">111 <button112 className="flex w-full items-start justify-between text-left"113 onClick={() => toggleItem(key)}114 >115 <span className="pr-6 text-sm font-light tracking-wide text-foreground">116 {item.question}117 </span>118 <span className="mt-0.5 flex-shrink-0 text-muted-foreground/60">119 <svg120 className={`h-3.5 w-3.5 transition-transform duration-300 ${isOpen ? "rotate-45" : ""}`}121 fill="none"122 stroke="currentColor"123 viewBox="0 0 24 24"124 >125 <path126 strokeLinecap="round"127 strokeLinejoin="round"128 strokeWidth={1.5}129 d="M12 4v16m8-8H4"130 />131 </svg>132 </span>133 </button>134 <div135 className={`overflow-hidden transition-all duration-300 ${136 isOpen ? "max-h-96 pt-3" : "max-h-0"137 }`}138 >139 <p className="text-sm font-light leading-relaxed text-muted-foreground/70">140 {item.answer}141 </p>142 </div>143 </div>144 );145 })}146 </div>147 </div>148 ))}149 </div>150 </div>151 </section>152 );153}