Accordion
Vertically stacked sections that expand one (or several) at a time. For a single independent section use Collapsible; for switching between peer views use Tabs.
Added 14 October 2026
React
Codecomponents/react/accordion-demo.tsx
import {Accordion, AccordionContent, AccordionItem, AccordionTrigger} from '@stroeer-labs/design-library/react';
interface FaqItem {
value: string;
question: string;
answer: string;
disabled?: boolean;
}
interface FaqSection {
label: string;
defaultValue?: Array<string>;
items: Array<FaqItem>;
}
const sections: Array<FaqSection> = [
{
label: 'Single open (default)',
items: [
{
value: 'formats',
question: 'Which ad formats are supported?',
answer: 'Billboard, sitebar and mobile banner formats are supported out of the box. Video and special formats are available on request.',
},
{
value: 'budget',
question: 'How is the daily budget spent?',
answer: 'Delivery is paced evenly across the day. Unspent budget from quiet hours is redistributed to peak traffic windows, so the daily total is never exceeded.',
},
{
value: 'pause',
question: 'Can I pause a running campaign?',
answer: 'Yes — pausing takes effect within a few minutes. The remaining budget stays reserved and delivery resumes where it left off.',
},
],
},
{
label: 'Default open',
defaultValue: ['schedule'],
items: [
{
value: 'schedule',
question: 'When does my campaign start?',
answer: 'Delivery starts on the booked start date at 00:00, once the creative has passed review.',
},
{
value: 'targeting',
question: 'Which regions can I target?',
answer: 'Targeting is available per federal state, city or postcode area, and can be combined freely.',
},
{
value: 'reporting',
question: 'Where do I find reports?',
answer: 'Impressions, clicks and spend are updated hourly in the campaign dashboard.',
},
],
},
{
label: 'Disabled item',
items: [
{
value: 'invoice',
question: 'How am I invoiced?',
answer: 'Invoices are issued monthly, after the delivery period ends.',
},
{
value: 'cancellation',
question: 'What are the cancellation terms?',
answer: 'This section is unavailable.',
disabled: true,
},
{
value: 'support',
question: 'How do I reach support?',
answer: 'Use the in-app chat or write to your account team — replies within one business day.',
},
],
},
];
export function AccordionDemo() {
return (
<div className="space-y-6">
{sections.map((section) => (
<div key={section.label} className="space-y-2">
<p className="text-xs font-medium text-muted-foreground">{section.label}</p>
<Accordion defaultValue={section.defaultValue} className="w-full max-w-md">
{section.items.map((item) => (
<AccordionItem key={item.value} value={item.value} disabled={item.disabled}>
<AccordionTrigger>{item.question}</AccordionTrigger>
<AccordionContent>
<p className="text-muted-foreground">{item.answer}</p>
</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
))}
</div>
);
}Angular
Codecomponents/angular/accordion-demo.ts
import '@angular/compiler';
import {Component} from '@angular/core';
import {NgIcon, provideIcons} from '@ng-icons/core';
import {lucideChevronDown} from '@ng-icons/lucide';
import {HlmAccordionImports} from '@stroeer-labs/design-library/angular';
interface FaqItem {
question: string;
answer: string;
opened?: boolean;
disabled?: boolean;
}
interface FaqSection {
label: string;
items: Array<FaqItem>;
}
@Component({
selector: 'dl-accordion-demo',
standalone: true,
imports: [HlmAccordionImports, NgIcon],
viewProviders: [provideIcons({lucideChevronDown})],
template: `
<div class="space-y-6">
@for (section of sections; track section.label) {
<div class="space-y-2">
<p class="text-xs font-medium text-muted-foreground">{{ section.label }}</p>
<div hlmAccordion class="w-full max-w-md">
@for (item of section.items; track item.question) {
<div hlmAccordionItem [isOpened]="item.opened ?? false" [disabled]="item.disabled ?? false">
<h3 hlmAccordionHeader>
<button hlmAccordionTrigger>
{{ item.question }}
<ng-icon hlmAccordionIcon name="lucideChevronDown" aria-hidden="true" />
</button>
</h3>
<hlm-accordion-content>
<p class="text-muted-foreground">{{ item.answer }}</p>
</hlm-accordion-content>
</div>
}
</div>
</div>
}
</div>
`,
})
export class AccordionDemoComponent {
protected readonly sections: Array<FaqSection> = [
{
label: 'Single open (default)',
items: [
{
question: 'Which ad formats are supported?',
answer: 'Billboard, sitebar and mobile banner formats are supported out of the box. Video and special formats are available on request.',
},
{
question: 'How is the daily budget spent?',
answer: 'Delivery is paced evenly across the day. Unspent budget from quiet hours is redistributed to peak traffic windows, so the daily total is never exceeded.',
},
{
question: 'Can I pause a running campaign?',
answer: 'Yes — pausing takes effect within a few minutes. The remaining budget stays reserved and delivery resumes where it left off.',
},
],
},
{
label: 'Default open',
items: [
{
question: 'When does my campaign start?',
answer: 'Delivery starts on the booked start date at 00:00, once the creative has passed review.',
opened: true,
},
{
question: 'Which regions can I target?',
answer: 'Targeting is available per federal state, city or postcode area, and can be combined freely.',
},
{
question: 'Where do I find reports?',
answer: 'Impressions, clicks and spend are updated hourly in the campaign dashboard.',
},
],
},
{
label: 'Disabled item',
items: [
{
question: 'How am I invoiced?',
answer: 'Invoices are issued monthly, after the delivery period ends.',
},
{
question: 'What are the cancellation terms?',
answer: 'This section is unavailable.',
disabled: true,
},
{
question: 'How do I reach support?',
answer: 'Use the in-app chat or write to your account team — replies within one business day.',
},
],
},
];
}