Skip to main content
PolyUI/docs

布局组件

Accordion 手风琴

可展开/收起的内容面板,常用于 FAQ、设置项等场景。基于 Base UI Accordion 构建,支持单选和多选展开模式。

安装

bash
npx polyui add accordion

基础

tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionBasic() {
  return (
    <div className="flex flex-col gap-8 w-full max-w-lg">
      <Accordion className="w-full">
        {faqItems.map((item) => (
          <AccordionItem key={item.value} value={item.value}>
            <AccordionTrigger>{item.title}</AccordionTrigger>
            <AccordionContent className="text-muted-foreground">{item.content}</AccordionContent>
          </AccordionItem>
        ))}
      </Accordion>
    </div>
  )
}

拆分

tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionSplit() {
  return (
    <div className="flex flex-col gap-8 w-full max-w-lg">
      <Accordion className="w-full space-y-2">
        {faqItems.map((item) => (
          <AccordionItem
            key={item.value}
            value={item.value}
            className="bg-card rounded-md border-b-0 shadow-md data-open:shadow-lg"
          >
            <AccordionTrigger className="px-5">{item.title}</AccordionTrigger>
            <AccordionContent className="text-muted-foreground px-5">{item.content}</AccordionContent>
          </AccordionItem>
        ))}
      </Accordion>
    </div>
  )
}

带图标

tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionWithIcon() {
  return (
    <div className="flex flex-col gap-8 w-full max-w-lg">
      <Accordion className="w-full">
        {iconItems.map((item) => (
          <AccordionItem key={item.value} value={item.value}>
            <AccordionTrigger>
              <span className="flex items-center gap-4">
                <item.icon className="text-muted-foreground size-4 shrink-0" />
                <span>{item.title}</span>
              </span>
            </AccordionTrigger>
            <AccordionContent className="text-muted-foreground">{item.content}</AccordionContent>
          </AccordionItem>
        ))}
      </Accordion>
    </div>
  )
}

Plus 图标

You can track your order by logging into your account and visiting the "Orders" section. You'll receive tracking information via email once your order ships. For real-time updates, you can also use the tracking number provided in your shipping confirmation email.

tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
import { PlusIcon } from "lucide-react"
export function AccordionPlusIcon() {
  return (
    <div className="flex flex-col gap-8 w-full max-w-lg">
      <Accordion className="w-full" defaultValue={["item-1"]}>
        {iconItems.map((item) => (
          <AccordionItem key={item.value} value={item.value}>
            <div className="flex">
              <AccordionTrigger
                data-slot="accordion-trigger"
                className="focus-visible:border-ring focus-visible:ring-ring/50 group/accordion-trigger flex flex-1 items-start justify-between gap-4 rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-expanded:[&>svg.plus-icon]:rotate-45"
              >
                <span className="flex items-center gap-4">
                  <item.icon className="size-4 shrink-0" />
                  <span>{item.title}</span>
                </span>
                <PlusIcon className="plus-icon text-muted-foreground pointer-events-none size-4 shrink-0 transition-transform duration-200" />
              </AccordionTrigger>
            </div>
            <AccordionContent className="text-muted-foreground">{item.content}</AccordionContent>
          </AccordionItem>
        ))}
      </Accordion>
    </div>
  )
}

图标副标题

You can track your order by logging into your account and visiting the "Orders" section. You'll receive tracking information via email once your order ships. For real-time updates, you can also use the tracking number provided in your shipping confirmation email.

tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
import { PlusIcon } from "lucide-react"
export function AccordionIconSubtitle() {
  return (
    <div className="flex flex-col gap-8 w-full max-w-lg">
      <Accordion className="w-full" defaultValue={["item-1"]}>
        {iconItems.map((item) => (
          <AccordionItem key={item.value} value={item.value}>
            <div className="flex">
              <AccordionTrigger
                data-slot="accordion-trigger"
                className="focus-visible:border-ring focus-visible:ring-ring/50 group/accordion-trigger flex flex-1 items-center justify-between gap-4 rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-expanded:[&>svg.plus-icon]:rotate-45"
              >
                <span className="flex items-center gap-4">
                  <span
                    className="flex size-10 shrink-0 items-center justify-center rounded-full border"
                    aria-hidden="true"
                  >
                    <item.icon className="size-4" />
                  </span>
                  <span className="flex flex-col space-y-0.5">
                    <span>{item.title}</span>
                    <span className="text-muted-foreground font-normal">{item.subtitle}</span>
                  </span>
                </span>
                <PlusIcon className="plus-icon text-muted-foreground pointer-events-none size-4 shrink-0 transition-transform duration-200" />
              </AccordionTrigger>
            </div>
            <AccordionContent className="text-muted-foreground">{item.content}</AccordionContent>
          </AccordionItem>
        ))}
      </Accordion>
    </div>
  )
}

高亮激活

You can track your order by logging into your account and visiting the "Orders" section. You'll receive tracking information via email once your order ships.

tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionActiveHighlight() {
  return (
    <div className="flex flex-col gap-8 w-full max-w-lg">
      <Accordion className="w-full" defaultValue={["item-1"]}>
        {faqItems.map((item) => (
          <AccordionItem
            key={item.value}
            value={item.value}
            className="data-open:border-amber-600 not-last:data-open:border-b-2 dark:data-open:border-amber-400"
          >
            <AccordionTrigger className="data-open:text-amber-600 dark:data-open:text-amber-400">
              {item.title}
            </AccordionTrigger>
            <AccordionContent className="text-muted-foreground">{item.content}</AccordionContent>
          </AccordionItem>
        ))}
      </Accordion>
    </div>
  )
}

带头像

Richard Payne is a remarkable individual known for his exceptional skills and expertise in various fields.

tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
import { Avatar, AvatarImage, AvatarFallback } from "@polyui/react/avatar"
export function AccordionWithAvatar() {
  return (
    <div className="flex flex-col gap-8 w-full max-w-lg">
      <Accordion className="w-full" defaultValue={["item-1"]}>
        {avatarItems.map((item) => (
          <AccordionItem key={item.value} value={item.value}>
            <AccordionTrigger className="items-center hover:no-underline">
              <span className="flex items-center gap-4">
                <Avatar className="size-10 rounded-sm">
                  <AvatarImage src={item.src} alt={item.name} className="rounded-sm" />
                  <AvatarFallback className="rounded-sm text-xs">
                    {item.name.split(/\s/).reduce((acc, word) => acc + word.slice(0, 1), "")}
                  </AvatarFallback>
                </Avatar>
                <span className="flex flex-col space-y-0.5">
                  <span>{item.name}</span>
                  <span className="text-muted-foreground font-normal">{item.email}</span>
                </span>
              </span>
            </AccordionTrigger>
            <AccordionContent className="text-muted-foreground">{item.content}</AccordionContent>
          </AccordionItem>
        ))}
      </Accordion>
    </div>
  )
}

描边分隔

You can track your order by logging into your account and visiting the "Orders" section. You'll receive tracking information via email once your order ships. For real-time updates, you can also use the tracking number provided in your shipping confirmation email.

tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionOutlineSeparated() {
  return (
    <div className="flex flex-col gap-8 w-full max-w-lg">
      <Accordion className="w-full space-y-2" defaultValue={["item-1"]}>
        {faqItemsFull.map((item) => (
          <AccordionItem key={item.value} value={item.value} className="rounded-md border!">
            <AccordionTrigger className="px-5">{item.title}</AccordionTrigger>
            <AccordionContent className="text-muted-foreground px-5">{item.content}</AccordionContent>
          </AccordionItem>
        ))}
      </Accordion>
    </div>
  )
}

盒子样式

You can track your order by logging into your account and visiting the "Orders" section. You'll receive tracking information via email once your order ships. For real-time updates, you can also use the tracking number provided in your shipping confirmation email.

tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionBox() {
  return (
    <div className="flex flex-col gap-8 w-full max-w-lg">
      <Accordion className="w-full rounded-md border" defaultValue={["item-1"]}>
        {faqItemsFull.map((item) => (
          <AccordionItem key={item.value} value={item.value}>
            <AccordionTrigger className="px-5">{item.title}</AccordionTrigger>
            <AccordionContent className="text-muted-foreground px-5">{item.content}</AccordionContent>
          </AccordionItem>
        ))}
      </Accordion>
    </div>
  )
}

标签页

You can track your order by logging into your account and visiting the "Orders" section. You'll receive tracking information via email once your order ships. For real-time updates, you can also use the tracking number provided in your shipping confirmation email.

tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionTabs() {
  return (
    <div className="flex flex-col gap-8 w-full max-w-lg">
      <Accordion className="w-full" defaultValue={["item-1"]}>
        {faqItemsFull.map((item) => (
          <AccordionItem
            key={item.value}
            value={item.value}
            className="data-open:bg-accent rounded-md border-none px-5 transition-colors duration-200"
          >
            <AccordionTrigger>{item.title}</AccordionTrigger>
            <AccordionContent className="text-muted-foreground">{item.content}</AccordionContent>
          </AccordionItem>
        ))}
      </Accordion>
    </div>
  )
}

描边标签页

You can track your order by logging into your account and visiting the "Orders" section. You'll receive tracking information via email once your order ships. For real-time updates, you can also use the tracking number provided in your shipping confirmation email.

tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
import { PlusIcon } from "lucide-react"
export function AccordionTabsOutline() {
  return (
    <div className="flex flex-col gap-8 w-full max-w-lg">
      <Accordion className="w-full" defaultValue={["item-1"]}>
        {faqItemsFull.map((item) => (
          <AccordionItem
            key={item.value}
            value={item.value}
            className="data-open:border-border rounded-md border border-transparent px-5 transition-colors duration-200 data-open:border data-open:shadow-md"
          >
            <div className="flex">
              <AccordionTrigger
                data-slot="accordion-trigger"
                className="focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-center justify-between gap-4 rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-expanded:[&>svg.plus-icon]:rotate-45"
              >
                {item.title}
                <PlusIcon className="plus-icon text-muted-foreground pointer-events-none size-4 shrink-0 transition-transform duration-200" />
              </AccordionTrigger>
            </div>
            <AccordionContent className="text-muted-foreground">{item.content}</AccordionContent>
          </AccordionItem>
        ))}
      </Accordion>
    </div>
  )
}

带媒体

You'll receive tracking information via email once your order ships.

How do I track my order?

tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionWithMedia() {
  return (
    <div className="flex flex-col gap-8 w-full max-w-lg">
      <Accordion className="w-full" defaultValue={["item-1"]}>
        {mediaItems.map((item) => (
          <AccordionItem key={item.value} value={item.value}>
            <AccordionTrigger>
              <span className="flex items-center gap-4">
                <item.icon className="size-4 shrink-0" />
                <span>{item.title}</span>
              </span>
            </AccordionTrigger>
            <AccordionContent className="space-y-4">
              <p className="text-muted-foreground">{item.content}</p>
              <img src={item.media} alt={item.title} className="w-full rounded-md" />
            </AccordionContent>
          </AccordionItem>
        ))}
      </Accordion>
    </div>
  )
}

填充头部

You can track your order by logging into your account and visiting the "Orders" section. You'll receive tracking information via email once your order ships. For real-time updates, you can also use the tracking number provided in your shipping confirmation email.

tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionFilledHeader() {
  return (
    <div className="flex flex-col gap-8 w-full max-w-lg">
      <Accordion className="w-full space-y-2" defaultValue={["item-1"]}>
        {faqItemsFull.map((item) => (
          <AccordionItem key={item.value} value={item.value} className="rounded-md border!">
            <AccordionTrigger className="bg-accent px-5 data-open:rounded-b-none">{item.title}</AccordionTrigger>
            <AccordionContent className="text-muted-foreground px-5 pt-4">{item.content}</AccordionContent>
          </AccordionItem>
        ))}
      </Accordion>
    </div>
  )
}

多层级

You can track your order by logging into your account and visiting the "Orders" section.

tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@polyui/react/collapsible"
import { PlusIcon, ChevronDownIcon } from "lucide-react"
export function AccordionMultilevel() {
  return (
    <div className="flex flex-col gap-8 w-full max-w-lg">
      <Accordion className="w-full rounded-md border" defaultValue={["item-1"]}>
        {multilevelItems.map((item) => (
          <AccordionItem
            key={item.value}
            value={item.value}
            className="outline-none first:rounded-t-md last:rounded-b-md"
          >
            <div className="flex">
              <AccordionTrigger
                data-slot="accordion-trigger"
                className="focus-visible:border-ring focus-visible:ring-ring/50 flex w-full flex-1 items-start justify-between gap-4 rounded-lg px-5 py-4 text-left text-sm font-medium transition-all outline-none hover:underline disabled:pointer-events-none disabled:opacity-50 aria-expanded:[&>svg.plus-icon]:rotate-45"
              >
                <span className="flex items-center gap-4">
                  <item.icon className="size-4 shrink-0" />
                  <span>{item.category}</span>
                </span>
                <PlusIcon className="plus-icon text-muted-foreground pointer-events-none size-4 shrink-0 transition-transform duration-200" />
              </AccordionTrigger>
            </div>
            <AccordionContent className="pb-0">
              {item.faqs.map((faq, i) => (
                <Collapsible key={i} className="bg-accent border-t px-5" defaultOpen={i === 0}>
                  <CollapsibleTrigger className="focus-visible:ring-ring/50 flex w-full items-center gap-4 rounded-sm py-4 font-medium outline-none focus-visible:z-10 focus-visible:ring-[3px]">
                    <ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0" />
                    {faq.title}
                  </CollapsibleTrigger>
                  <CollapsibleContent className="text-muted-foreground overflow-hidden pb-4">
                    {faq.content}
                  </CollapsibleContent>
                </Collapsible>
              ))}
            </AccordionContent>
          </AccordionItem>
        ))}
      </Accordion>
    </div>
  )
}

多层级 Plus

You can track your order by logging into your account and visiting the "Orders" section.

tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@polyui/react/collapsible"
import { PlusIcon } from "lucide-react"
export function AccordionMultilevelPlus() {
  return (
    <div className="flex flex-col gap-8 w-full max-w-lg">
      <Accordion className="w-full rounded-md border" defaultValue={["item-1"]}>
        {multilevelItems.map((item) => (
          <AccordionItem
            key={item.value}
            value={item.value}
            className="outline-none first:rounded-t-md last:rounded-b-md"
          >
            <AccordionTrigger className="px-5 outline-none focus-visible:ring-0">{item.category}</AccordionTrigger>
            <AccordionContent className="pb-0">
              {item.faqs.map((faq, i) => (
                <Collapsible key={i} className="bg-muted border-t px-8" defaultOpen={i === 0}>
                  <CollapsibleTrigger className="focus-visible:ring-ring/50 flex w-full items-center gap-4 rounded-sm py-4 font-medium outline-none focus-visible:z-10 focus-visible:ring-[3px] aria-expanded:[&>svg.plus-icon]:rotate-45">
                    <PlusIcon className="plus-icon text-muted-foreground pointer-events-none size-4 shrink-0 transition-transform duration-200" />
                    {faq.title}
                  </CollapsibleTrigger>
                  <CollapsibleContent className="text-muted-foreground overflow-hidden pb-4">
                    {faq.content}
                  </CollapsibleContent>
                </Collapsible>
              ))}
            </AccordionContent>
          </AccordionItem>
        ))}
      </Accordion>
    </div>
  )
}

Props — Accordion

属性类型默认值说明
openMultiplebooleanfalse是否允许多个面板同时展开
defaultValuestring | string[]非受控模式下默认展开的面板 value
valuestring | string[]受控模式下当前展开的面板 value
onValueChange(value: string | string[]) => void展开状态变化时的回调
classNamestring自定义样式类名

Props — AccordionItem

属性类型默认值说明
valuestring该面板的唯一标识,必填
disabledbooleanfalse是否禁用该面板
classNamestring自定义样式类名