Accordion

A vertically stacked set of interactive headings that each reveal an associated section of content.

Loading...
llms.txt
 import { Component } from '@angular/core';
import {
    RdxAccordionContentDirective,
    RdxAccordionHeaderDirective,
    RdxAccordionItemDirective,
    RdxAccordionRootDirective,
    RdxAccordionTriggerDirective
} from '@radix-ng/primitives/accordion';
import { ChevronDown, LucideAngularModule, X } from 'lucide-angular';
 
@Component({
    selector: 'radix-accordion-demo',
    standalone: true,
    imports: [
        RdxAccordionRootDirective,
        RdxAccordionItemDirective,
        RdxAccordionHeaderDirective,
        RdxAccordionTriggerDirective,
        RdxAccordionContentDirective,
        LucideAngularModule
    ],
    template: `
        <div class="AccordionRoot" [defaultValue]="'item-1'" rdxAccordionRoot>
            <div class="AccordionItem" [value]="'item-1'" rdxAccordionItem>
                <div class="AccordionHeader" rdxAccordionHeader>
                    <button class="AccordionTrigger" type="button" rdxAccordionTrigger>
                        Is it accessible?
                        <lucide-angular class="AccordionChevron" [img]="ChevronDownIcon" size="16" />
                    </button>
                </div>
                <div class="AccordionContent" rdxAccordionContent>
                    <div class="AccordionContentText">Yes. It adheres to the WAI-ARIA design pattern.</div>
                </div>
            </div>
 
            <div class="AccordionItem" [value]="'item-2'" rdxAccordionItem>
                <div class="AccordionHeader" rdxAccordionHeader>
                    <button class="AccordionTrigger" type="button" rdxAccordionTrigger>
                        Is it unstyled?
                        <lucide-angular class="AccordionChevron" [img]="ChevronDownIcon" size="16" />
                    </button>
                </div>
                <div class="AccordionContent" rdxAccordionContent>
                    <div class="AccordionContentText">
                        Yes. It's unstyled by default, giving you freedom over the look and feel.
                    </div>
                </div>
            </div>
 
            <div class="AccordionItem" [value]="'item-3'" rdxAccordionItem>
                <div class="AccordionHeader" rdxAccordionHeader>
                    <button class="AccordionTrigger" type="button" rdxAccordionTrigger>
                        Can it be animated?
                        <lucide-angular class="AccordionChevron" [img]="ChevronDownIcon" size="16" />
                    </button>
                </div>
                <div class="AccordionContent" rdxAccordionContent>
                    <div class="AccordionContentText">Yes! You can animate the Accordion with CSS or JavaScript.</div>
                </div>
            </div>
        </div>
    `,
    styleUrl: 'accordion-demo.css'
})
export class AccordionDemoComponent {
    readonly ChevronDownIcon = ChevronDown;
    protected readonly XIcon = X;
}
  
 /* reset */
:host {
    button,
    h3 {
        all: unset;
    }
 
    display: contents;
}
 
.AccordionRoot {
    border-radius: 6px;
    background-color: var(--mauve-6);
    box-shadow: 0 2px 10px var(--black-a4);
}
 
.AccordionRoot[data-orientation='vertical'] {
    width: 300px;
    max-width: 295px;
}
 
.AccordionRoot[data-orientation='horizontal'] {
    height: 300px;
 
    display: flex;
    flex-direction: row;
}
 
.AccordionItem {
    overflow: hidden;
    margin-top: 1px;
}
 
.AccordionItem[data-orientation='horizontal'] {
    display: flex;
}
 
.AccordionItem[data-orientation='vertical']:first-child {
    margin-top: 0;
    border-top-left-radius: 4px;
    border-top-right-radius: 4px;
}
 
.AccordionItem[data-orientation='vertical']:last-child {
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
}
 
.AccordionItem[data-orientation='horizontal']:first-child {
    margin-top: 0;
    border-top-left-radius: 4px;
    border-bottom-left-radius: 4px;
}
 
.AccordionItem[data-orientation='horizontal']:last-child {
    border-top-right-radius: 4px;
    border-bottom-right-radius: 4px;
}
 
.AccordionItem:focus-within {
    position: relative;
    z-index: 1;
    box-shadow: 0 0 0 2px var(--mauve-12);
}
 
.AccordionHeader {
    display: flex;
}
 
.AccordionTrigger {
    font-family: inherit;
    padding: 0 20px;
    height: 45px;
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 15px;
    line-height: 1;
    color: var(--violet-11);
    box-shadow: 0 1px 0 var(--mauve-6);
    background-color: white;
    cursor: default;
}
 
.AccordionTrigger[data-orientation='horizontal'] {
    height: 100%;
    padding: 20px;
    writing-mode: vertical-rl;
}
 
.AccordionTrigger[data-disabled='true'] {
    color: var(--gray-7);
}
 
.AccordionTrigger:hover {
    background-color: var(--mauve-2);
}
 
.AccordionContent {
    display: flex;
    overflow: hidden;
    font-size: 15px;
    color: var(--mauve-11);
    background-color: var(--mauve-2);
}
 
.AccordionContent[hidden] {
    display: none !important;
}
 
.AccordionContent[data-orientation='vertical'][data-state='open'] {
    animation: slideDown 300ms cubic-bezier(0.87, 0, 0.13, 1);
}
 
.AccordionContent[data-orientation='vertical'][data-state='closed'] {
    animation: slideUp 300ms cubic-bezier(0.87, 0, 0.13, 1);
}
 
.AccordionContent[data-orientation='horizontal'][data-state='open'] {
    animation: slideRight 300ms cubic-bezier(0.87, 0, 0.13, 1);
}
 
.AccordionContent[data-orientation='horizontal'][data-state='closed'] {
    animation: slideLeft 300ms cubic-bezier(0.87, 0, 0.13, 1);
}
 
.AccordionContentText {
    padding: 15px 20px;
}
 
.AccordionChevron {
    display: flex;
    color: var(--violet-10);
    transition: transform 300ms cubic-bezier(0.87, 0, 0.13, 1);
}
 
.AccordionTrigger[data-state='open'] > .AccordionChevron {
    transform: rotate(180deg);
}
 
.horizontal-flex-container {
    display: flex;
}
 
@keyframes slideDown {
    from {
        height: 0;
    }
 
    to {
        height: var(--radix-accordion-content-height);
    }
}
 
@keyframes slideUp {
    from {
        height: var(--radix-accordion-content-height);
    }
 
    to {
        height: 0;
    }
}
 
@keyframes slideRight {
    from {
        width: 0;
    }
 
    to {
        width: var(--radix-accordion-content-width);
    }
}
 
@keyframes slideLeft {
    from {
        width: var(--radix-accordion-content-width);
    }
 
    to {
        width: 0;
    }
} 

Features

  • Full keyboard navigation.
  • Supports horizontal/vertical orientation.
  • Supports Right to Left direction.
  • Can expand one or multiple items.
  • Can be controlled or uncontrolled.

API Reference

Root

Prop Default Type
dir
InputSignal<Direction>

The reading direction of the accordion when applicable. If omitted, assumes LTR (left-to-right) reading mode.

disabled
false
InputSignalWithTransform<boolean, BooleanInput>

Whether the Accordion is disabled.

orientation
'vertical'
InputSignal<DataOrientation>

The orientation of the accordion.

defaultValue
InputSignal<undefined | string | string[]>

The default active value of the item(s). Use when you do not need to control the state of the item(s).

value
ModelSignal<undefined | AcceptableValue | AcceptableValue[]>

The controlled value of the item to expand.

collapsible
false
InputSignalWithTransform<boolean, BooleanInput>

When type is "single", allows closing content when clicking trigger for an open item. When type is "multiple", this prop has no effect.

type
'single'
InputSignal<"multiple" | "single">

Determines whether a "single" or "multiple" items can be selected at a time.

Emit Payload
onValueChange
[value: void]

Event handler called when the expanded state of an item changes and type is "multiple".

Data Attribute Value
[data-orientation]
"vertical" | "horizontal"

Item

Contains all the parts of a collapsible section.

Prop Default Type
value
InputSignal<undefined | string>

A string value for the accordion item. All items within an accordion should use a unique value.

disabled
InputSignalWithTransform<boolean, BooleanInput>

Whether or not an accordion item is disabled from user interaction. When `true` , prevents the user from interacting with the item.

Data Attribute Value
[data-state]
"open" | "closed"
[data-disabled]
Present when disabled
[data-orientation]
"vertical" | "horizontal"

Wraps an rdxAccordionTrigger .

Data Attribute Value
[data-state]
"open" | "closed"
[data-disabled]
Present when disabled
[data-orientation]
"vertical" | "horizontal"

Trigger

Toggles the collapsed state of its associated item. It should be nested inside an rdxAccordionHeader .

Data Attribute Value
[data-state]
"open" | "closed"
[data-disabled]
Present when disabled
[data-orientation]
"vertical" | "horizontal"

Content

Contains the collapsible content for an item.

Data Attribute Value
[data-state]
"open" | "closed"
[data-disabled]
Present when disabled
[data-orientation]
"vertical" | "horizontal"
CSS Variable Description
--radix-accordion-content-width The width of the content when it opens/closes
--radix-accordion-content-height The height of the content when it opens/closes