implement
Accordion

Accordion

Expand and collapse sections of content, one at a time or several at once.

import {
	Accordion,
	AccordionContent,
	AccordionItem,
	AccordionTrigger,
} from "@/lib/components/ui/accordion";

export default function AccordionDemo() {
	return Accordion(
		{ type: "multiple", class: "w-full max-w-md" },
		AccordionItem(
			{ value: "what" },
			AccordionTrigger("What is implement?"),
			AccordionContent("A signal-based UI framework with no compiler."),
		),
		AccordionItem(
			{ value: "why" },
			AccordionTrigger("Why no compiler?"),
			AccordionContent("Your app is plain TypeScript that builds real DOM nodes."),
		),
		AccordionItem(
			{ value: "styling" },
			AccordionTrigger("How do I style it?"),
			AccordionContent(
				"Every part exposes data attributes like data-state, so plain CSS or Tailwind works.",
			),
		),
	);
}

Installation

npx jsrepo add @implementjs/ui/accordion

It installs @implementjs/lucide at the same time.

Usage

AccordionTrigger is where most of the styling lives. It wraps itself in an AccordionHeader, lays the label out against a chevron, and turns the chevron over while the item is open — so a trigger is just its label.

import {
	Accordion,
	AccordionContent,
	AccordionItem,
	AccordionTrigger,
} from "@/lib/components/ui/accordion";

Accordion(
	{ type: "multiple" },
	AccordionItem(
		{ value: "what" },
		AccordionTrigger("What is implement?"),
		AccordionContent("A signal-based UI framework with no compiler."),
	),
);

Animation

The open and close slide comes from the accordion-down / accordion-up keyframes, which the component references but does not define — they belong in your stylesheet, and the introduction has the block to paste. Without them the content still shows and hides correctly; it just snaps.

AccordionContent puts your class on an inner padded Div rather than on the animated element, so padding you add never fights the height animation.

API Reference

Every prop the styling does not consume is forwarded to the Accordion primitive, so the tables below are the whole surface — the behavior props and the styling ones together.

Accordion

The root. Owns which items are open and the arrow-key focus movement. Renders a Div; extra props are forwarded onto it.

PropTypeDefaultDescription
type"single" | "multiple""single"Whether opening an item closes the others, or several can stay open.
valueSignal<string | null> (single) | Signal<string[]> (multiple)The open item(s). Pass a signal to control the accordion from outside; omit it for uncontrolled state.
disabledSignal<boolean> | booleanfalseDisables every item in the accordion.
loopbooleantrueWhether arrow keys wrap from the last trigger back to the first.
orientation"horizontal" | "vertical""vertical"Which arrow keys move focus between triggers: vertical is Up/Down, horizontal is Left/Right.
Data attributeValue
[data-accordion-root]Present
[data-orientation]"horizontal" | "vertical"
[data-disabled]Present when disabled

AccordionItem

One section of the accordion. Styled with a bottom border, dropped on the last item. Renders a Div; extra props are forwarded onto it.

PropTypeDefaultDescription
value*stringIdentifies the item. Must be unique within the accordion.
disabledSignal<boolean> | booleanfalseDisables this item.
Data attributeValue
[data-accordion-item]Present
[data-state]"open" | "closed"
[data-disabled]Present when disabled
[data-orientation]"horizontal" | "vertical"

AccordionTrigger

Toggles its item open and closed. The styled trigger wraps itself in an AccordionHeader and appends a chevron that turns over while the item is open. Renders a Button; extra props are forwarded onto it.

PropTypeDefaultDescription
disabledSignal<boolean> | booleanfalseDisables this trigger.
Data attributeValue
[data-accordion-trigger]Present
[data-state]"open" | "closed"
[data-value]The item's value
[data-disabled]Present when disabled
[data-orientation]"horizontal" | "vertical"

AccordionContent

The body of an item. Hidden with the `hidden` attribute while closed; a close animation on it finishes before the attribute is set. class lands on an inner padded Div, so the animated element keeps the overflow and height classes it needs. The slide comes from the accordion keyframes in your stylesheet. Renders a Div; extra props are forwarded onto it.

PropTypeDefaultDescription
hiddenUntilFoundbooleanfalseClosed content uses hidden="until-found" so find-in-page can reveal it.
Data attributeValue
[data-accordion-content]Present
[data-state]"open" | "closed"
[data-disabled]Present when disabled
[data-orientation]"horizontal" | "vertical"
CSS variableDescription
--ip-accordion-content-heightThe natural height of the content, for open/close animations.
--ip-accordion-content-widthThe natural width of the content, for open/close animations.

AccordionHeader

Wraps a trigger in role="heading" when the item title should be a heading. Renders a Div; extra props are forwarded onto it.

PropTypeDefaultDescription
level1 | 2 | 3 | 4 | 5 | 63The aria-level of the heading.
Data attributeValue
[data-accordion-header]Present
[data-state]"open" | "closed"
[data-disabled]Present when disabled
[data-orientation]"horizontal" | "vertical"
[data-heading-level]"1" – "6"