Accordion
betaA stack of headings that each open and close a section below them, one at a time or several at once.
Preview
Website marketing and FAQs
Your website, from first sketch to launch.
Start with the pages people need. Shape the message, design the details, then bring it all together.
Decide what visitors need to know and what they should do next. A simple page plan keeps the project focused.
- A clear purpose for every page
- A route from the homepage to an enquiry
- A list of content to keep, update, or write
Before we begin
A few practical questions about working together.
Editable settings
Your profile
Design lead
Project reviewer
Full project workspace
Keep the brief, reference images, and feedback together so everyone can find the latest version.
- Name each folder for the work it holds.
- Pin the current brief at the top.
- Give reviewers access to that folder.
Keep the working team small. Editors can change the project; viewers can read and comment.
Avery Chen
Jordan Lee
Sam Rivera
Ready for review
Website refresh
| Work | Status | Amount |
|---|---|---|
| Research & content | $640 | |
| Page design | $960 | |
| Final review | $320 | |
| Total estimate | $1,920 | |
Avery Chen
The brief is ready. Please add your references before we review the first direction.
- 12 November 2026
- Homepage, selected work, and contact page
“Make it easy to see what we do, then give people one clear way to get in touch.”
Sizes
Unavailable section
Install
1. Add the registry to
components.json, once per app. Without it, the shadcn CLI looks up@txbrendanin a public list that txbrendan/ui does not control.npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.
npx shadcn@latest add @txbrendan/theme3. Add the component.
npx shadcn@latest add @txbrendan/accordion
Use it for
- Questions and answers, like an FAQ, where people read only the ones they need.
- Long settings or details split into sections, so the page starts short.
- Build each section from AccordionItem, AccordionTrigger, and AccordionPanel. Give each item a value when you control which ones are open.
- Add multiple when people may want to compare two sections side by side.
- Add hiddenUntilFound so the browser's find-in-page can search closed sections and open the match.
- Panels can contain images, links, lists, and controls. Use keepMounted on a panel with local form state so closing it preserves edits.
Avoid it for
- Content everyone needs to read. Show it on the page.
- Switching between views of the same thing. Use tabs.
- A single section that shows more. Use a details element instead.
- Steps that must happen in order. Use a page per step, or step dots.
Props
- Accordion
Root. Holds which sections are open. Takes value, defaultValue, onValueChange, multiple, disabled, and hiddenUntilFound.
{ value?: string[]; defaultValue?: string[]; onValueChange?: (value: string[], details) => void; multiple?: boolean; disabled?: boolean; hiddenUntilFound?: boolean }
- size
Accordion. Row height and text size: 36, 44, or 56px rows at the default density.
"sm" | "md" | "lg" · default "md"
- multiple
Accordion. Lets more than one section be open at once.
boolean · default false
- value
Accordion. The open items' values, controlled. Use it with onValueChange.
string[]
- defaultValue
Accordion. The items open at the start when you do not control it.
string[]
- onValueChange
Accordion. Called with the open items' values when one opens or closes.
(value: string[], details) => void
- hiddenUntilFound
Accordion or AccordionPanel. Keeps closed panels in the page with hidden='until-found', so find-in-page can open them.
boolean · default false
- disabled
Accordion or AccordionItem. Blocks opening and closing. It dims.
boolean · default false
- AccordionItem
Part. One section. A line separates it from the next. value names it for value and defaultValue.
ComponentProps<"div"> & { value?: string }
- AccordionTrigger
Part. The heading's button. It renders inside an <h3> and adds a chevron that turns when the section opens.
ComponentProps<"button">
- AccordionPanel
Part. The section's content. It grows open and shrinks closed.
ComponentProps<"div">
- keepMounted
AccordionPanel. Keeps the contents mounted while closed, preserving local field values. Hidden controls stay out of the tab order.
boolean · default false
- className
On any part. Later classes win. On AccordionPanel it goes on the inner box, where the padding is, so className='pb-6' works.
string
| Prop | Type | Default | What it does |
|---|---|---|---|
| Accordion | { value?: string[]; defaultValue?: string[]; onValueChange?: (value: string[], details) => void; multiple?: boolean; disabled?: boolean; hiddenUntilFound?: boolean } | - | Root. Holds which sections are open. Takes value, defaultValue, onValueChange, multiple, disabled, and hiddenUntilFound. |
| size | "sm" | "md" | "lg" | "md" | Accordion. Row height and text size: 36, 44, or 56px rows at the default density. |
| multiple | boolean | false | Accordion. Lets more than one section be open at once. |
| value | string[] | - | Accordion. The open items' values, controlled. Use it with onValueChange. |
| defaultValue | string[] | - | Accordion. The items open at the start when you do not control it. |
| onValueChange | (value: string[], details) => void | - | Accordion. Called with the open items' values when one opens or closes. |
| hiddenUntilFound | boolean | false | Accordion or AccordionPanel. Keeps closed panels in the page with hidden='until-found', so find-in-page can open them. |
| disabled | boolean | false | Accordion or AccordionItem. Blocks opening and closing. It dims. |
| AccordionItem | ComponentProps<"div"> & { value?: string } | - | Part. One section. A line separates it from the next. value names it for value and defaultValue. |
| AccordionTrigger | ComponentProps<"button"> | - | Part. The heading's button. It renders inside an <h3> and adds a chevron that turns when the section opens. |
| AccordionPanel | ComponentProps<"div"> | - | Part. The section's content. It grows open and shrinks closed. |
| keepMounted | boolean | false | AccordionPanel. Keeps the contents mounted while closed, preserving local field values. Hidden controls stay out of the tab order. |
| className | string | - | On any part. Later classes win. On AccordionPanel it goes on the inner box, where the padding is, so className='pb-6' works. |
Accessibility
- Each trigger is a button inside an <h3>, so screen reader users can jump between sections by heading.
- The button has aria-expanded and aria-controls. An open panel has role="region", named by its trigger.
- Each trigger is its own tab stop. Enter and Space open and close it. Arrow keys do not move between triggers, as in the current ARIA pattern.
- The chevron is hidden from screen readers. aria-expanded says whether a section is open.
- The heading level is fixed at h3. If the page needs another level, pass render to the header in your copy of accordion.tsx.
- The focus ring uses --ring, which the theme tests hold to 3:1 against the page and cards.
- Opening grows the panel over --motion-duration-base while its contents slide into place and fade in over 1.5 times --motion-duration-slow. Closing fades the contents over --motion-duration-close. Reduced motion removes height, content, and chevron movement; Motion Off removes all transitions.
Examples
FAQ
<Accordion hiddenUntilFound>
<AccordionItem value="refunds">
<AccordionTrigger>Can I get a refund?</AccordionTrigger>
<AccordionPanel>Yes, within 30 days of your order. We send the money back to the card you paid with.</AccordionPanel>
</AccordionItem>
<AccordionItem value="shipping">
<AccordionTrigger>How long does shipping take?</AccordionTrigger>
<AccordionPanel>Three to five working days in the US, and up to ten days anywhere else.</AccordionPanel>
</AccordionItem>
</Accordion>Editable values that survive closing a section
<Accordion multiple defaultValue={["account"]} size="lg">
<AccordionItem value="account">
<AccordionTrigger>Account</AccordionTrigger>
<AccordionPanel keepMounted>
<label>Display name <input defaultValue="Workspace owner" /></label>
</AccordionPanel>
</AccordionItem>
<AccordionItem value="billing">
<AccordionTrigger>Billing</AccordionTrigger>
<AccordionPanel>Your plan, card, and past invoices.</AccordionPanel>
</AccordionItem>
</Accordion>Controlled
"use client";
import { useState } from "react";
function Sections() {
const [open, setOpen] = useState<string[]>([]);
return (
<Accordion value={open} onValueChange={(value) => setOpen(value as string[])}>
<AccordionItem value="one">
<AccordionTrigger>First section</AccordionTrigger>
<AccordionPanel>Shown when open includes "one".</AccordionPanel>
</AccordionItem>
</Accordion>
);
}