UIPackage
Menu

Framework

Change language

Boilerplate repo

FAQ — Categorized Tabs

blockmarketing

Question set grouped behind category tabs, each panel holding its own accordion with a count badge, for support pages where one flat list would run past thirty entries.

Also available for Vue ->

Installation

$npx shadcn@latest add https://uipkge.dev/r/react/faq-categorized-tabs.json
Named registry:npx shadcn@latest add @uipkge-react/faq-categorized-tabsInstalls to:components/blocks/

Variants

Loading interactive previews…

Files installed (1)

  • components/blocks/FaqCategorizedTabs.tsx3.5 kB
    'use client'
    
    import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion'
    import { Badge } from '@/components/ui/badge'
    import { Button } from '@/components/ui/button'
    import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
    
    const categories = [
      {
        id: 'security',
        label: 'Security',
        entries: [
          {
            q: 'Where is our data stored?',
            a: 'Definitions and query metadata sit in your chosen region. Warehouse rows are never copied out.',
          },
          {
            q: 'Are you SOC 2 audited?',
            a: 'Type II, audited annually. The report and the subprocessor list are on the trust centre.',
          },
          { q: 'Can we bring our own keys?', a: 'Yes, on the enterprise plan, with rotation handled through your KMS.' },
        ],
      },
      {
        id: 'billing',
        label: 'Billing',
        entries: [
          {
            q: 'How is usage counted?',
            a: 'By query, not by seat. Materialised reads are not billed twice within their freshness window.',
          },
          {
            q: 'Can we cap spend?',
            a: 'Per-team query budgets are enforced at run time, so a runaway dashboard cannot outspend its ceiling.',
          },
          { q: 'Do you offer annual terms?', a: 'Yes, with a discount and an invoiced option above a threshold.' },
        ],
      },
      {
        id: 'setup',
        label: 'Setup',
        entries: [
          {
            q: 'What do you need from us?',
            a: 'A read-only role, your identity provider, and an hour with whoever owns the warehouse.',
          },
          {
            q: 'How long until first value?',
            a: 'Most teams publish a first certified metric on day one and finish the rollout in five weeks.',
          },
        ],
      },
    ]
    
    export function FaqCategorizedTabs() {
      return (
        <section data-slot="faq-categorized-tabs" className="bg-background">
          <div className="mx-auto max-w-4xl px-6 py-20 lg:py-28">
            <div className="max-w-2xl">
              <Badge variant="secondary">Support</Badge>
              <h2 className="mt-4 text-3xl font-semibold tracking-tight sm:text-4xl">Answers by topic</h2>
              <p className="text-muted-foreground mt-3 text-lg">
                Grouped rather than listed, because one flat list of thirty questions is a list nobody reads.
              </p>
            </div>
    
            <Tabs defaultValue={categories[0].id} className="mt-10">
              <TabsList variant="pill" className="flex-nowrap overflow-x-auto">
                {categories.map((category) => (
                  <TabsTrigger key={category.id} value={category.id} variant="pill">
                    {category.label}
                    <span className="text-muted-foreground ml-1.5 font-mono text-xs">{category.entries.length}</span>
                  </TabsTrigger>
                ))}
              </TabsList>
    
              {categories.map((category) => (
                <TabsContent key={category.id} value={category.id} className="mt-6">
                  <Accordion type="single" collapsible className="w-full">
                    {category.entries.map((entry) => (
                      <AccordionItem key={entry.q} value={entry.q}>
                        <AccordionTrigger className="text-left font-medium">{entry.q}</AccordionTrigger>
                        <AccordionContent className="text-muted-foreground leading-relaxed">{entry.a}</AccordionContent>
                      </AccordionItem>
                    ))}
                  </Accordion>
                </TabsContent>
              ))}
            </Tabs>
    
            <Button variant="ghost" className="mt-8">
              Browse the full help centre
            </Button>
          </div>
        </section>
      )
    }
    

Raw manifest:https://uipkge.dev/r/react/faq-categorized-tabs.json