Accordion list

The Accordion list module (M120): a section title beside or above a list of expandable rows — FAQs, steps, office lists. Each row is a native <details> with a title, an optional step counter or badge, and a body of text with an optional CTA group. Three layouts: stacked, side by side, and side by side with a narrow title column.

Built withButtonTag

Examples

Stacked

Figma "Type=Stacked": the section title above a full-width list. The second row is open, showing the body text and the optional CTA group — the FAQ from the client's outdoor-gym-cost page.

<section class="kompan-accordion" aria-labelledby="accordion-frequently-asked-questions">
  <div class="kompan-accordion__container">
    <div class="kompan-accordion__header">
      <h2 class="kompan-accordion__heading-title" id="accordion-frequently-asked-questions">Frequently Asked Questions</h2>
    </div>
    <div class="kompan-accordion__list">
      <details class="kompan-accordion__item">
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__title">How much does it cost to build an outdoor fitness centre?</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>A basic outdoor gym with three to five stations starts at a modest budget, while a full fitness park with cardio, strength and functional zones costs many times that. The site, the surfacing and the equipment mix decide most of it.</p>
    </div>
  </details>
      <details class="kompan-accordion__item" open>
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__title">What affects the price of outdoor gym equipment?</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>The delivery of the outdoor gym equipment drives the cost up or down depending on the freight and shipping costs and your timeline. Also, the choice of surfacing, the number of stations and the installation method make a difference.</p>
      <div class="kompan-accordion__cta"><a class="kompan-button kompan-button_md" href="#">Request a quote</a><a class="kompan-button kompan-button_md kompan-button_secondary-dark" href="#">Download the guide</a></div>
    </div>
  </details>
      <details class="kompan-accordion__item">
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__title">What to consider when budgeting for an outdoor gym area?</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>Think beyond the equipment: ground preparation, surfacing, lighting, signage and maintenance over the lifetime of the site are part of the total cost.</p>
    </div>
  </details>
    </div>
  </div>
</section>

Side by side

Figma "Type=Side by side - Even": the title and its secondary text form a column as wide as the list. On mobile it stacks with a larger gap. Add kompan-accordion_side.

<section class="kompan-accordion kompan-accordion_side" aria-labelledby="accordion-frequently-asked-questions">
  <div class="kompan-accordion__container">
    <div class="kompan-accordion__header">
      <h2 class="kompan-accordion__heading-title" id="accordion-frequently-asked-questions">Frequently Asked Questions</h2>
      <p class="kompan-accordion__subtitle">What people ask before they build an outdoor gym.</p>
    </div>
    <div class="kompan-accordion__list">
      <details class="kompan-accordion__item">
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__title">How much does it cost to build an outdoor fitness centre?</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>A basic outdoor gym with three to five stations starts at a modest budget, while a full fitness park with cardio, strength and functional zones costs many times that. The site, the surfacing and the equipment mix decide most of it.</p>
    </div>
  </details>
      <details class="kompan-accordion__item" open>
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__title">What affects the price of outdoor gym equipment?</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>The delivery of the outdoor gym equipment drives the cost up or down depending on the freight and shipping costs and your timeline. Also, the choice of surfacing, the number of stations and the installation method make a difference.</p>
      <div class="kompan-accordion__cta"><a class="kompan-button kompan-button_md" href="#">Request a quote</a><a class="kompan-button kompan-button_md kompan-button_secondary-dark" href="#">Download the guide</a></div>
    </div>
  </details>
      <details class="kompan-accordion__item">
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__title">What to consider when budgeting for an outdoor gym area?</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>Think beyond the equipment: ground preparation, surfacing, lighting, signage and maintenance over the lifetime of the site are part of the total cost.</p>
    </div>
  </details>
    </div>
  </div>
</section>

Side by side, narrow title

Figma "Type=Side by side - uneven": the title column is capped at width-xs and the list takes twice the space — the layout of the worldwide contact list. Add kompan-accordion_side-narrow. Rows are given a shared name so only one is open at a time.

<section class="kompan-accordion kompan-accordion_side-narrow" aria-labelledby="accordion-europe">
  <div class="kompan-accordion__container">
    <div class="kompan-accordion__header">
      <h2 class="kompan-accordion__heading-title" id="accordion-europe">Europe</h2>
      <p class="kompan-accordion__subtitle">Find your local KOMPAN team.</p>
    </div>
    <div class="kompan-accordion__list">
      <details class="kompan-accordion__item" name="europe">
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__title">KOMPAN Austria</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>Contact details for KOMPAN Austria.</p>
    </div>
  </details>
      <details class="kompan-accordion__item" open name="europe">
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__title">KOMPAN Belgium</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>KOMPAN NV/SA, Bosstraat 15, 8780 Oostrozebeke, Belgium. +32 09/216 66 60 · KOMPAN.be@KOMPAN.com</p>
    </div>
  </details>
      <details class="kompan-accordion__item" name="europe">
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__title">KOMPAN Commercial Systems</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>Contact details for KOMPAN Commercial Systems.</p>
    </div>
  </details>
      <details class="kompan-accordion__item" name="europe">
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__title">KOMPAN Czech</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>Contact details for KOMPAN Czech.</p>
    </div>
  </details>
      <details class="kompan-accordion__item" name="europe">
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__title">KOMPAN Denmark</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>Contact details for KOMPAN Denmark.</p>
    </div>
  </details>
      <details class="kompan-accordion__item" name="europe">
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__title">KOMPAN England</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>Contact details for KOMPAN England.</p>
    </div>
  </details>
      <details class="kompan-accordion__item" name="europe">
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__title">KOMPAN Finland</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>Contact details for KOMPAN Finland.</p>
    </div>
  </details>
    </div>
  </div>
</section>

Step counter and badge

The item's "Show Step Counter" and "Show badge" options: a numbered circle before the title that fills when the row is open, and a small Tag after it.

<section class="kompan-accordion" aria-labelledby="accordion-how-to-plan-an-outdoor-gym">
  <div class="kompan-accordion__container">
    <div class="kompan-accordion__header">
      <h2 class="kompan-accordion__heading-title" id="accordion-how-to-plan-an-outdoor-gym">How to plan an outdoor gym</h2>
    </div>
    <div class="kompan-accordion__list">
      <details class="kompan-accordion__item">
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__step">1</span><span class="kompan-accordion__title">Choose your site</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>Look at footfall, sunlight, drainage and how far users are from parking and toilets.</p>
    </div>
  </details>
      <details class="kompan-accordion__item" open>
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__step">2</span><span class="kompan-accordion__title">Pick the equipment mix</span><span class="kompan-tag kompan-tag_sm">Popular</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>Combine cardio, strength and functional training so beginners and experienced users both find something to do.</p>
      <div class="kompan-accordion__cta"><a class="kompan-button kompan-button_md" href="#">See the product range</a></div>
    </div>
  </details>
      <details class="kompan-accordion__item">
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__step">3</span><span class="kompan-accordion__title">Plan surfacing and installation</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>Rubber, artificial turf or gravel each have different costs, safety properties and maintenance needs.</p>
    </div>
  </details>
      <details class="kompan-accordion__item">
    <summary class="kompan-accordion__summary">
      <span class="kompan-accordion__heading"><span class="kompan-accordion__step">4</span><span class="kompan-accordion__title">Launch and maintain</span></span>
      <svg class="kompan-accordion__chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6l4 4 4-4"/></svg>
    </summary>
    <div class="kompan-accordion__body">
      <p>A yearly inspection keeps the site safe and the warranty valid.</p>
    </div>
  </details>
    </div>
  </div>
</section>

Accessibility

Rows are native disclosure widgets

Each row is a <details> with a <summary>, so expand/collapse, keyboard operation (Tab to the summary, Enter or Space to toggle) and the expanded state announcement come from the browser — no script is needed for the basics.

The summary is the only control

The chevron is decorative (aria-hidden) and the step counter and badge are plain text inside the summary; the whole row is one button-like target with a visible focus ring.

One open at a time is optional

Giving the rows a shared name attribute makes them exclusive (opening one closes the others) in browsers that support it; without a name every row can be open. Do not use exclusivity for content people need to compare.

The section is named by its title

The <section> is labelled by its heading, so it appears in the landmark list by name ("Frequently Asked Questions"). Keep the heading level in step with the page outline.

Body text is real content

Collapsed bodies are still in the DOM and findable in page search in modern browsers; keep them as normal paragraphs and buttons, not images of text.

Stylesheet

Shipped as @wah-kompan/kompan-ui/components/modules/accordion.css. Every value is a token reference, so it themes with the rest of the system rather than being restyled. The shipped file also imports Button, Tag — what this component is built with — so one import is enough.

css
.kompan-accordion {
  --kompan-accordion-gap: var(--spacing-3xl);
  --kompan-accordion-title-width: none;

  display: grid;
  box-sizing: border-box;
  margin: 0;
  padding-block: var(--spacing-4xl) var(--spacing-7xl);
  background: var(--color-bg-primary);
  color: var(--color-text-primary);
  font-family: var(--type-font-family-body);
}

/* The 1280px content column, padded by the responsive container token */
.kompan-accordion__container {
  display: flex;
  flex-direction: column;
  gap: var(--kompan-accordion-gap);
  box-sizing: border-box;
  width: 100%;
  max-width: var(--container-default-max-width);
  margin-inline: auto;
  padding-inline: var(--container-padding-lg);
}

/* SectionTitle (Medium): title + optional secondary text */
.kompan-accordion__header {
  display: grid;
  gap: var(--spacing-xxs);
  max-width: var(--kompan-accordion-title-width);
}

.kompan-accordion__heading-title {
  max-width: var(--width-paragraph-max-width);
  margin: 0;
  font-family: var(--type-font-family-display);
  font-size: var(--type-font-size-display-sm);
  font-weight: var(--type-font-weight-semibold);
  line-height: var(--type-line-height-display-sm);
}

.kompan-accordion__subtitle {
  max-width: var(--width-paragraph-max-width);
  margin: 0;
  color: var(--color-text-secondary);
  font-size: var(--type-font-size-md);
  font-weight: var(--type-font-weight-regular);
  line-height: var(--type-line-height-md);
}

.kompan-accordion__list {
  flex: 1 1 0;
  min-width: 0;
}

/* One row: a hairline under every item, and one above the first */
.kompan-accordion__item {
  border-bottom: var(--stroke-sm) solid var(--color-border-secondary);
}

.kompan-accordion__item:first-child {
  border-top: var(--stroke-sm) solid var(--color-border-secondary);
}

.kompan-accordion__summary {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  padding-block: var(--spacing-2xl);
  cursor: pointer;
  list-style: none;
  -webkit-tap-highlight-color: transparent;
}

.kompan-accordion__summary::-webkit-details-marker {
  display: none;
}

.kompan-accordion__summary:focus-visible {
  outline: var(--stroke-md) solid var(--color-button-focus-ring);
  outline-offset: 2px;
}

.kompan-accordion__heading {
  display: flex;
  flex: 1 1 0;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--spacing-sm);
  min-width: 0;
}

.kompan-accordion__title {
  min-width: 0;
  font-size: var(--type-font-size-md);
  font-weight: var(--type-font-weight-semibold);
  line-height: var(--type-line-height-md);
  overflow-wrap: break-word;
}

/* Optional step counter: a 24px circle that fills when the item is open */
.kompan-accordion__step {
  display: inline-grid;
  flex: none;
  place-items: center;
  inline-size: 1.5rem;
  block-size: 1.5rem;
  border-radius: var(--radius-full);
  background: var(--color-input-bg-tertiary);
  color: var(--color-input-fg-primary);
  font-size: var(--type-font-size-sm);
  font-weight: var(--type-font-weight-regular);
  line-height: var(--type-line-height-sm);
}

.kompan-accordion__item[open] > .kompan-accordion__summary .kompan-accordion__step {
  background: var(--color-input-bg-selected);
  color: var(--color-input-fg-selected);
}

.kompan-accordion__chevron {
  flex: none;
  inline-size: var(--width-icon-sm);
  block-size: var(--width-icon-sm);
  color: var(--color-fg-secondary);
  transition: transform 200ms ease;
}

.kompan-accordion__item[open] > .kompan-accordion__summary .kompan-accordion__chevron {
  transform: rotate(180deg);
}

/* Body ("AccordionInnerContent.Text"): text, then the optional CTA group */
.kompan-accordion__body {
  display: grid;
  gap: var(--spacing-xl);
  padding-bottom: var(--spacing-xl);
  color: var(--color-text-secondary);
  font-size: var(--type-font-size-sm);
  font-weight: var(--type-font-weight-regular);
  line-height: var(--type-line-height-sm);
}

.kompan-accordion__body > p {
  max-width: var(--width-paragraph-max-width);
  margin: 0;
}

.kompan-accordion__cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--spacing-lg);
}

/* Side by side: the title becomes a column next to the list */
.kompan-accordion_side,
.kompan-accordion_side-narrow {
  --kompan-accordion-gap: var(--spacing-5xl);
  padding-block: var(--spacing-5xl);
}

.kompan-accordion_side .kompan-accordion__container,
.kompan-accordion_side-narrow .kompan-accordion__container {
  flex-direction: row;
  align-items: flex-start;
}

.kompan-accordion_side .kompan-accordion__header {
  flex: 1 1 0;
  min-width: 0;
}

.kompan-accordion_side-narrow {
  --kompan-accordion-title-width: var(--width-xs);
}

.kompan-accordion_side-narrow .kompan-accordion__header {
  flex: 1 1 0;
  min-width: 0;
}

.kompan-accordion_side-narrow .kompan-accordion__list {
  flex: 2 1 0;
}

/* Mobile: every layout stacks; the side-by-side ones keep a larger gap */
@media (max-width: 480px) {
  .kompan-accordion_side .kompan-accordion__container,
  .kompan-accordion_side-narrow .kompan-accordion__container {
    flex-direction: column;
    gap: var(--spacing-7xl);
  }

  .kompan-accordion_side .kompan-accordion__header,
  .kompan-accordion_side-narrow .kompan-accordion__header {
    flex: none;
    width: 100%;
    max-width: none;
  }

  .kompan-accordion_side-narrow .kompan-accordion__list {
    flex: none;
    width: 100%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .kompan-accordion__chevron {
    transition: none;
  }
}