Skip to main content

Accordion v2.9.1

Organize content into expandable and collapsible sections, allowing users to reveal or hide information as needed.

  • Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam euismod, ante erat imperdiet velit, nec laoreet enim lacus a velit. Nam luctus, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan.

    Fusce vel quam sollicitudin, semper lacus ac, malesuada mi. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent dapibus mi dui, imperdiet tempor tortor malesuada a. Pellentesque imperdiet dui et neque pulvinar, nec egestas urna mattis.

  • Fusce vel quam sollicitudin, semper lacus ac, malesuada mi. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent dapibus mi dui, imperdiet tempor tortor malesuada a. Mauris elementum nisi at leo molestie finibus.

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam euismod, ante erat imperdiet velit, nec laoreet enim lacus a velit. Nam luctus, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan.

  • Consectetur adipiscing elit. Nam euismod, ante erat imperdiet velit, nec laoreet enim lacus a velit. Nuctus, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan.

    Pellentesque imperdiet dui et neque pulvinar, nec egestas urna mattis.

  • Nam euismod, ante erat imperdiet velit, nec laoreet enim lacus a velit. Nam luctus, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan.

  • Praesent non odio bibendum magna bibendum accumsan.

    Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent dapibus mi dui, imperdiet tempor tortor malesuada a. Mauris elementum nisi at leo molestie finibus. Nulla dui metus, placerat vel leo scelerisque, pellentesque malesuada ante. Curabitur vitae arcu non ex lobortis vestibulum et ac enim. Sed feugiat, sapien vel placerat fringilla, dolor orci blandit neque, pretium varius nisi odio vitae quam. Suspendisse id justo ultricies, consequat dui id, vulputate diam. Pellentesque imperdiet dui et neque pulvinar, nec egestas urna mattis.

  • Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam euismod, ante erat imperdiet velit, nec laoreet enim lacus a velit. Nam luctus, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan.

  • Dolor sit amet, nte erat imperdiet velit, nec laoreet enim lacus, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan.

    Fusce vel quam sollicitudin, semper lacus ac, malesuada mi. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent dapibus mi dui, imperdiet tempor tortor malesuada a. Mauris elementum nisi at leo molestie finibus. Nulla dui metus, placerat vel leo scelerisque, pellentesque malesuada ante.

Note: Please note that this is a basic example of accordion functionality. Adding additional markup, such as headings, can enhance the component's accessibility and make it more understandable to assistive technology users. See the Interactive Headings (FAQ) example for more details.

<div class="accordion" data-arrow-inline-end>

  <ul role="list">  
    <li>

      <button class="accordion__toggle" id="default-1">Ante Erat</button>

      <div class="accordion__panel">

        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam euismod, ante erat imperdiet velit, nec laoreet enim lacus a velit. <a href="#!">Nam luctus</a>, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan.</p>
  
        <p>Fusce vel quam sollicitudin, semper lacus ac, malesuada mi. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent dapibus mi dui, imperdiet tempor tortor malesuada a. Pellentesque imperdiet dui et neque pulvinar, nec egestas urna mattis.</p>

      </div>

    </li>
    <li>

      <button class="accordion__toggle" id="default-2">Consectetur Adipiscing</button>

      <div class="accordion__panel">

        <p>Fusce vel quam sollicitudin, semper lacus ac, malesuada mi. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent dapibus mi dui, imperdiet tempor tortor malesuada a. Mauris elementum nisi at leo molestie finibus.</p>

        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam euismod, ante erat imperdiet velit, nec laoreet enim lacus a velit. <a href="#!">Nam luctus</a>, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan.</p>
  
      </div>

    </li>
    <li>

      <button class="accordion__toggle" id="default-3">Jacus Velit</button>

      <div class="accordion__panel">

        <p>Consectetur adipiscing elit. Nam euismod, ante erat imperdiet velit, nec laoreet enim lacus a velit. <a href="#!">Nuctus</a>, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan.</p>

        <p>Pellentesque imperdiet dui et neque pulvinar, nec egestas urna mattis.</p>
  
      </div>

    </li>
    <li>

      <button class="accordion__toggle" id="default-4">Ultricies</button>

      <div class="accordion__panel">

        <p>Nam euismod, ante erat imperdiet velit, nec laoreet enim lacus a velit. <a href="#!">Nam luctus</a>, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan.</p>
  
      </div>

    </li>
    <li>

      <button class="accordion__toggle" id="default-5">Magna</button>

      <div class="accordion__panel">

        <p>Praesent non odio bibendum magna bibendum accumsan.</p>

        <p>Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent dapibus mi dui, imperdiet tempor tortor malesuada a. Mauris elementum nisi at leo molestie finibus. Nulla dui metus, placerat vel leo scelerisque, pellentesque malesuada ante. Curabitur vitae arcu non ex lobortis vestibulum et ac enim. Sed feugiat, sapien vel placerat fringilla, dolor orci blandit neque, pretium varius nisi odio vitae quam. Suspendisse id justo ultricies, consequat dui id, vulputate diam. Pellentesque imperdiet dui et neque pulvinar, nec egestas urna mattis.</p>
  
      </div>

    </li>
    <li>

      <button class="accordion__toggle" id="default-6">Volutpat Mi</button>

      <div class="accordion__panel">

        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam euismod, ante erat imperdiet velit, nec laoreet enim lacus a velit. <a href="#!">Nam luctus</a>, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan.</p>
  
      </div>

    </li>
    <li>

      <button class="accordion__toggle" id="default-7">Tortor Pharetra</button>

      <div class="accordion__panel">

        <p>Dolor sit amet, nte erat imperdiet velit, nec laoreet enim lacus, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan.</p>

        <p>Fusce vel quam sollicitudin, semper lacus ac, malesuada mi. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent dapibus mi dui, imperdiet tempor tortor malesuada a. Mauris elementum nisi at leo molestie finibus. Nulla dui metus, placerat vel leo scelerisque, pellentesque malesuada ante.</p>
  
      </div>

    </li>
  </ul>

</div>
/*!

  Radancy Component Library: Accordion

  Contributor(s):
  Michael "Spell" Spellacy

  Dependencies: Sass

*/

.accordion {

// Component Variables

--accordion-arrow-size: 0.8em; // Toggle Arrow Size
--accordion-arrow-border: calc(2em / 16); // Toggle Arrow Border
--accordion-color-base-1: #000; // Base Color 1
--accordion-color-base-2: #fff; // Base Color 2
--accordion-close-size: calc(30em / 16); // Close Button Size
--accordion-color-active: #a9a9a9; // Active State
--accordion-color-default: #d3d3d3; // Default States
--accordion-color-interactive: #666; // Interactive State 
--accordion-color-selected: #6F00EF; // Selected States
--accordion-column-large: 4; // Horizontal Layout (Large Vieport)
--accordion-column-medium: 2; // Horizontal Layout (Medium Vieport)
--accordion-gap: calc(5em / 16); // Accordion Gap
--accordion-panel-border: calc(2em /16) transparent solid; // Sets border on pointer and panel. Not for use with default animation
--accordion-panel-default: #d3d3d3; // Panel State (Default)
--accordion-pointer-size: calc(25em / 16); // Content Pointer Size
--accordion-toggle-border-radius: 0; // Button Border Radius

// Breakpoints (CSS variables are not allowed in media queries - boo!)

$accordion-large: 55em; // Large Viewport
$accordion-medium: 37.5em; // Medium Viewport

display: grid;
gap: var(--accordion-gap);

  // Accordion Defaults & Resets: Do not remove!

  > ol, > ul, > dl, > ul > li, &__heading {
  all: unset;
  } 

  > ol, > ul, > dl, > ul > li{
  display: contents;
  } 

  // Horizontal Layout

  &[data-horizontal] {

    @media (min-width: $accordion-medium) {

      grid-auto-flow: dense;
      grid-template-columns: repeat(var(--accordion-column-medium), 1fr);

    }

    @media (min-width: $accordion-large) {

      grid-template-columns: repeat(var(--accordion-column-large), 1fr); 

    }
      
  }

  // Accordion Toggle Arrow

  &__arrow {
  align-self: start;
  block-size: var(--accordion-arrow-size);
  border-width: var(--accordion-arrow-border);
  border-block-color: transparent var(--accordion-color-selected);
  border-block-start-width: 0;
  border-inline-color: var(--accordion-color-selected) transparent;
  border-inline-end-width: 0;
  border-style: solid;
  content: "";
  direction: ltr;
  display: block;
  inline-size: var(--accordion-arrow-size);
  margin-block: 0 calc(4em / 16);
  transform: rotate(-45deg);
  
    [data-context] & {
  
      @media (min-width: $accordion-medium) {
  
        display: none;
  
      }
  
    }

    [data-arrow-inline-end] & {

      justify-self: end;

    }
  
  }

  // Accordion Close Button

  &__close {
  background-color: transparent;
  block-size: var(--accordion-close-size);
  border: 0;
  cursor: pointer;
  display: grid;
  float: inline-end;
  inline-size: var(--accordion-close-size);
  justify-items: center;
  margin: 0;
  margin-block-end: 1em;
  margin-inline-start: 1em;
  padding: 0;

    &:after, &::before {
    background-color: var(--accordion-color-base-1);
    block-size: 100%;
    content: "";
    display: block;
    grid-area: 1 / 1;
    inline-size: calc(2em / 16);
    }

    &::after {
    transform: rotate(-45deg);
    }

    &::before {
    transform: rotate(45deg);
    }

  }

  // Accordion Toggle & Toggle All Button

  &__toggle, &__toggle-all {
  align-items: center;
  background-color: var(--accordion-color-default);
  border-radius: var(--accordion-toggle-border-radius);
  border: 0;
  box-sizing: border-box;
  color: inherit;
  cursor: pointer;
  display: grid;
  font-size: inherit;
  gap: 1em;
  grid-template-columns: auto 1fr;
  inline-size: 100%;
  line-height: normal;
  padding: 1em;
  position: relative;
  text-align: start;
  text-wrap: balance;

    // Multi-purpose Span

    span:not(.accordion__arrow) {
    line-height: 1.5;
    }

    // Accordion Animation

    [data-animation] & {

      @media (prefers-reduced-motion: no-preference) {
  
        transition: 300ms;

      }

    }

    // Viewport Context

    [data-context] & {

      @media (min-width: $accordion-medium) {

        pointer-events: none;
        visibility: hidden;

          span {
          color: var(--accordion-color-selected);
          visibility: visible;
          }

      }

    }

    // Horizontal Layout

    [data-horizontal] & {

      @media (min-width: $accordion-medium) {
      
        padding: 1.5em;

      }

    }

    // Horizontal Layout (Overlay): Hides buttons when overlay is "active"

    [data-overlay][data-active] & {
      
      display: none;
    
    }

    // Content Pointer

    [data-pointer] & {

      &::after {
      background-color: var(--accordion-panel-default);
      block-size: var(--accordion-pointer-size);
      border-inline-start: var(--accordion-panel-border);
      border-block-start: var(--accordion-panel-border);
      content: "";
      display: none;
      inline-size: var(--accordion-pointer-size);
      inset-block-end: calc(var(--accordion-pointer-size) * -1);
      inset-inline-end: 0;
      inset-inline-start: 50%;
      pointer-events: none;
      position: absolute;
      transform: rotate(45deg) translateX(-50%);
      z-index: 1;

        // Accordion Animation

        [data-animation] & {

          translate: 0 var(--accordion-pointer-size);

        }

      }   

    }

    // Accessibility: Focus and hover states must meet a 3:1 color ratio between background color and foreground text.
    // The project designer should be providing this state. Also, never remove focus outlines! Either redesign them or 
    // design an alternative focus state. Learn: https://webaim.org/articles/contrast/

    &:focus-visible, &:hover {
    background-color: var(--accordion-color-active);
    }

    &:focus-visible {
    outline-offset: -5px;
    }

    // Accordion Toggle Button: Behavior

    &[aria-expanded="true"], &[aria-pressed="true"] {
    background-color: var(--accordion-color-selected);
    color: var(--accordion-color-base-2);

      [data-animation][data-pointer] & { 

        // Content Pointer: Animated
          
        &::after {
        translate: 0 .1rem;

          @media (prefers-reduced-motion: no-preference) {

            transition: translate 800ms, display 0s allow-discrete;

          }

          @starting-style {

            translate: 0 var(--accordion-pointer-size);

          }

        }

      }

      // Conent Pointer

      [data-pointer] & { 

        &::after {
        display: block;
        }

      }

      // Accordion Toggle Arrow

      .accordion__arrow {
      border-block-end-color: var(--accordion-color-base-2);
      border-inline-start-color: var(--accordion-color-base-2);
      transform: rotate(135deg);
      translate: 0 calc(8rem/16);

        [data-pointer] & {

          visibility: hidden;

        }

      }

    }

  }

  // Accordion Toggle All Button 

  &__toggle-all {
  inline-size: fit-content;
  justify-self: end;
  margin-block: 0 1em;
  }

  // Accordion Panel

  &__panel {
  background-color: var(--accordion-panel-default);
  display: none;
  margin-block-start: calc(var(--accordion-gap) * -1);
  overflow: hidden;
  padding: 1em;
  position: relative;

    // Accordion Animation

    [data-animation] & {

      max-block-size: 0;

      @media (prefers-reduced-motion: no-preference) {

        transition: display 0s, max-block-size 2s ease-in-out allow-discrete;

      }

    }

    // Horizontal Layout

    [data-horizontal] & {

      border: var(--accordion-panel-border);
      margin-block-start: auto;

      @media (min-width: $accordion-medium) {

        grid-column: 1 / span var(--accordion-column-medium);

      }

      @media (min-width: $accordion-large) {

        grid-column: 1 / span var(--accordion-column-large);

      }

    }

    // Fixed Height

    [data-fixed-height] & {

      max-block-size: 18.75em;
      overflow-y: auto;
      overscroll-behavior-y: contain;
      scrollbar-gutter: stable;

      // Fallback Scrollbar

      scrollbar-color: var(--accordion-color-active) var(--accordion-color-default);
      scrollbar-width: thin;

      @supports #{"\selector(::-webkit-scrollbar)"} {

        scrollbar-color: initial;
        scrollbar-width: auto;
        padding-inline-end: 0.5em;
  
        &::-webkit-scrollbar {
        width: calc(18rem / 16);
        }
    
        &::-webkit-scrollbar-track {
        background-color: var(--accordion-color-default);
        margin-block: 0.5em;
        }
    
        &::-webkit-scrollbar-thumb {
        background-color: var(--accordion-color-active);
        border: calc(6rem / 16) var(--accordion-color-default) solid;
        border-radius: 100vw;
        min-block-size: calc(100rem / 16);
        }
    
        &::-webkit-scrollbar-thumb:hover {
        background-color: var(--accordion-color-interactive);
        }
      
      }

    }

    // Viewport Context

    [data-context] & {

      @media (min-width: $accordion-medium) {

        background-color: transparent;
        display: block;

      }

    }

  }

  // Accordion Panel Reveal

  &__toggle[aria-expanded="true"] + &__panel, &__heading:has(&__toggle[aria-expanded="true"]) + &__panel {
  display: block;
  
    [data-animation] & {

      max-block-size: 5000px;

      @starting-style {
      
        max-block-size: 1px;
      
      }
  
    }

  }

}
/*!

  Radancy Component Library: Accordion

  Contributor(s):
  Michael "Spell" Spellacy

*/

(() => {

  "use strict";

  const loadLanguagePack = (url, callback) => {

    // Install Language Pack.

    const getComponentLanguagePack = document.getElementById("component-library-language-pack");

    if (!getComponentLanguagePack) {

      const componentLanguagePack = document.createElement("script");

      componentLanguagePack.setAttribute("src", url);
      componentLanguagePack.setAttribute("id", "component-library-language-pack");
      componentLanguagePack.addEventListener("load", callback);

      document.head.appendChild(componentLanguagePack);

    } else {

      getComponentLanguagePack.addEventListener("load", callback);

    }

  };

  const initAccordion = () => {

    loadLanguagePack("https://services.tmpwebeng.com/component-library/language-pack.js", () => {

      // Display which version is in use via console:

      console.log("%cAccordion%cv2.9.1", "background: #2d2d2d; color: #fff; padding: 6px 10px; border-radius: 16px 0 0 16px; font-weight: 600;" , "background: #6e00ee; color: #fff; padding: 6px 10px; border-radius: 0 16px 16px 0; font-weight: 600;");

      // Classes, data attributes, states, and strings.

      const accordionClass = ".accordion";
      const accordionCloseClassName = "accordion__close";
      const accordionCloseClass = `.${accordionCloseClassName}`;
      const accordionToggleClassName = "accordion__toggle";
      const accordionArrowClassName = "accordion__arrow";
      const accordionToggleClass = `.${accordionToggleClassName}`;
      const accordionToggleAllClass = ".accordion__toggle-all";
      const accordionHeadingClassName = "accordion__heading";
      const accordionPanelClass = ".accordion__panel";
      const accordionDataActiveState = "data-active";
      const accordionDataDefaultOpen = "data-open";
      const accordionDataOverlay = "data-overlay";
      const accordionDataCloseButton = "data-close";
      const accordionDataDisableAnchor = "data-disable-anchor";
      const accordionDataFixedHeight = "data-fixed-height";
      const accordionDataMultiOpen = "data-multiple";
      const accordionDataRemoveArrow = "data-remove-arrow";
      const accordions = document.querySelectorAll(accordionClass);
      const URLFragment = location.hash.slice(1);

      // Language

      const accordionCloseButtonLabel = window.accordionCloseButtonLabel;

      // Loop through and set up all accordions on the page.

      accordions.forEach((accordion, index) => {

        // Set unique ID on all accordions.

        accordion.setAttribute("id", `accordion-${index + 1}`);

        // Get all buttons within accordion.

        const accordionToggles = accordion.querySelectorAll(accordionToggleClass);

        // Get all panels within accordion.

        const accordionPanels = accordion.querySelectorAll(accordionPanelClass);

        // Set variable for selected button target.

        let expandedButton = null;

        // Get "Toggle All " button.

        const btnToggleAll = accordion.querySelector(accordionToggleAllClass);

        // Loop through each toggle button.

        accordionToggles.forEach(btn => {

          // Get button ID. Remember: ID's should always be unique.

          const buttonID = btn.getAttribute("id");

          // Set up each button.

          btn.setAttribute("aria-controls", `accordion-${buttonID}`);
          btn.setAttribute("aria-expanded", "false");

          // Add Toggle Arrow

          if (!accordion.hasAttribute(accordionDataRemoveArrow)) {

            const toggleState = document.createElement("span");

            toggleState.setAttribute("aria-hidden", "true");
            toggleState.classList.add(accordionArrowClassName);
            btn.prepend(toggleState);

          }

          // Handle button click.

          btn.addEventListener("click", () => {

            const isExpanded = btn.getAttribute("aria-expanded") === "true";

            if (!accordion.hasAttribute(accordionDataMultiOpen)) {

              accordionToggles.forEach(button => {

                button.setAttribute("aria-expanded", "false");

              });

            }

            btn.setAttribute("aria-expanded", isExpanded ? "false" : "true");

            // Add "data-active" attribute on the parent accordion. Might be useful to achieve interesting UX.

            accordion.setAttribute(accordionDataActiveState, "");

            // If "Toggle All" button is present, then always set it to false.

            if (btnToggleAll) {

              btnToggleAll.setAttribute("aria-pressed", "false");

            }

            // Place focus on close button if present.

            if (accordion.hasAttribute(accordionDataCloseButton)) {

              btn.nextElementSibling.querySelector(accordionCloseClass).focus();

            }

            // Add URL Fragment to URL if not disabled.

            if (!accordion.hasAttribute(accordionDataDisableAnchor)) {

              history.pushState(null, null, `#${buttonID}`);

            }

          });

          // Default open based on URL fragment or data-open attribute.

          if (buttonID === URLFragment || (!expandedButton && btn.hasAttribute(accordionDataDefaultOpen))) {

            // Add "data-active" to parent.

            accordion.setAttribute(accordionDataActiveState, "");

            // Open targeted element.

            expandedButton = btn;

          }

        });

        if (expandedButton) {

          expandedButton.setAttribute("aria-expanded", "true");

        }

        // If "Toggle All" button is present...

        if (btnToggleAll) {

          btnToggleAll.setAttribute("aria-pressed", "false");

          if (!accordion.hasAttribute(accordionDataRemoveArrow)) {

            const toggleAllState = document.createElement("span");

            toggleAllState.setAttribute("aria-hidden", "true");
            toggleAllState.classList.add(accordionArrowClassName);
            btnToggleAll.append(toggleAllState);

          }

          // Toggle All Event

          btnToggleAll.addEventListener("click", function () {

            const isPressed = this.getAttribute("aria-pressed");

            this.setAttribute("aria-pressed", isPressed === "true" ? "false" : "true");

            // Get all accordion buttons and handle their state based on toggle button state.

            accordionToggles.forEach(btn => {

              btn.setAttribute("aria-expanded", isPressed === "true" ? "false" : "true");

            });

          });

        }

        // Loop through each panel.

        accordionPanels.forEach(panel => {

          // Set up each disclosure.

          const currentPanel = panel.previousElementSibling;

          let panelID;

          if (currentPanel.classList.contains(accordionHeadingClassName)) {

            panelID = currentPanel.querySelector(accordionToggleClass).getAttribute("id");

          } else {

            panelID = currentPanel.getAttribute("id");

          }

          panel.setAttribute("id", `accordion-${panelID}`);

          // To better support scrolling and repositioned focus, the panels should have a proper role and accName.

          if (accordion.hasAttribute(accordionDataFixedHeight) || accordion.hasAttribute(accordionDataOverlay)) {

            panel.setAttribute("role", "region");
            panel.setAttribute("aria-labelledby", panelID);

          }

          // To better support inner-scrolling, the panels must be focusable.

          if (accordion.hasAttribute(accordionDataFixedHeight)) {

            panel.setAttribute("tabindex", "0");

          }

          // Add close button

          if (accordion.hasAttribute(accordionDataCloseButton)) {

            const closeButton = document.createElement("button");

            closeButton.setAttribute("aria-label", accordionCloseButtonLabel);
            closeButton.classList.add(accordionCloseClassName);

            closeButton.addEventListener("click", () => {

              const thisButton = panel.previousElementSibling;

              // Remove "data-active" attribute.

              accordion.removeAttribute(accordionDataActiveState);

              // Reset button state, move focus to button that initiated click.

              thisButton.setAttribute("aria-expanded", "false");
              thisButton.focus();

            });

            panel.prepend(closeButton);

          }

        });

      });

      // Check for duplicate IDs and log a warning

      const buttonIDs = Array.from(document.querySelectorAll(accordionToggleClass)).map(btn => btn.id);

      buttonIDs.forEach((id, index) => {

        if (buttonIDs.indexOf(id, index + 1) !== -1) {

          console.warn(`%c Warning: Duplicate Accordion ID found: #${id}. Accordions must have unique ID values.`, "background: #ff0000; color: #fff");

        }

      });

    });

  };

  initAccordion();

})();

Properties Anchor (Properties)

Use the following properties to configure this component.

Element Note Description
button[.accordion__toggle-btn] Optional
Required as a direct child of .accordion when a "Show All" button is desired. Must also be used with data-multiple and may not be used with data-horizontal.

Attribute Applies To Note Description
data-active .accordion Component
Applied by the script. Can be used as a hook for various styling possibilities.
data-animation .accordion Boolean, Optional
Used to enable content to be animated when opened. Please note that the default animation leverages @starting-style at-rule.
data-close .accordion Boolean, Optional
Specifies that each accordion should have its own close button. By default, no close buttons are provided. Should only be used with data-overlay.
data-context .accordion Boolean, Optional
Used to allow the accordion to become non-interactive on larger viewport sizes, while maintaining interactivity on mobile. Often used in mobile footers.
data-disable-anchor .accordion Boolean, Optional
Used to disable the anchor that appears in the address bar when a button is pressed. Do note that including an anchor can be useful for tracking and sharing specific content, so we should only remove when we have a good reason.
data-fixed-height .accordion Boolean, Optional
Used to fix the panel content to a specific height. The height may be adusted in the CSS.
data-horizontal .accordion Boolean, Optional
Used to apply a horizontal layout to the accordion, which may be adjusted in the CSS.
data-multiple .accordion Boolean, Optional
Used to allow multiple accordion disclosures to be opened instead of one at a time.
data-open .accordion__toggle Boolean, Optional
Specifies which accordion panel should be open on page load. If not specified, no panels will be open by default. Only one accordion may be opened at a time.
data-overlay .accordion Boolean, Optional
Used to allow content to overlay the accordion buttons. This is currently an experimental feature and could, at any time, be removed. Should only be used with data-horizontal.
data-pointer .accordion Boolean, Optional
Used to apply a visual pointer that connects the button to the content. In this state, the down arrow, which is usually present on the button, will not be visible as it is unnecessary. The content pointer can be applied to both horizontal and vertical accordions.
data-remove-arrow .accordion Boolean, Optional
By default, all buttons include a span element for icons. If this is not desirable, you can remove it by adding this attribute and using your own inline or CSS icon. For custom inline icons, ensure they are hidden from assistive technologies by using aria-hidden="true".
id .accordion__toggle String, Mandatory
Each accordion button must have a unique ID with no spaces. The console will display an error if this requirement is not met. Allowing a developer to handle the ID, allows for custom anchoring.

Class Applies To Note Description
.accordion div, section Mandatory
Required on the parent element that wraps your content.
.accordion__toggle button Mandatory
Required on your accordion button.
.accordion__panel div Mandatory
Required on your accordion panel.

Release Notes Anchor (Release Notes)

Keep up to date on major changes to this component.

Bug
June 17, 2026: The property, grid-auto-columns: max-content was breaking the layout. This is likely a longstanding issue that went unnoticed because the button text is typically shorter. When longer text is present, it causes the content to overflow the viewport. Changing the layout to grid-template-columns: auto 1fr appears to resolve the issue and provides more consistent behavior. Version increased to 2.9.1.
Maintenance
December 12, 2025: Removed conditional syntax and restoted min-width due to the admin tool not being able to process it. Boo! Thanks, Paul!
Maintenance
December 08, 2025: Introduced new base color variables, --accordion-color-base-1 and --accordion-color-base-2, representing black and white values. Refactored existing min-width media queries to use conditional syntax (width > $accordion-large) for cleaner, more expressive breakpoint logic.
Bug
February 21, 2025: Removed display: contents from .accordion__heading.
Bug
January 28, 2025: Incorrect Sass nesting was permitting CSS to target all span elements on a page without a specifc class. Changed from span:not(&__arrow) to span:not(.accordion__arrow).
Note
April 24, 2025: Upgraded component to ES6.
Note
May 13, 2025: Changed rem to em units in variables to better support context within web components.

Feature
Add dynamic IDs, possibly as default. Developers often forget to change the default ID's provided in these demos and Josh W brought up a good point about accordions used in CMS scenarios (High).
Behavior
Add Escape Key functionality to Overlay example (High).
Behavior
Add back button support. Currently, the URL fragment changes, but not the content (High).
Behavior
Add video support for YouTube, Vimeo, and HTML5. Video needs to reset when accordion is closed. Delievery team to workaround for now (High).
Feature
Reinstate callback functionality. While it is no longer needed in this component, it may still be useful to have (Medium).
Feature
Consider the possibility of introducing arrow key support. While optional, it might be a nice inclusion (Low).
Feature
Consider the possibility of allowing for nested accordions (Medium).

Report Issues

Find a bug? Want a new feature? Report it on JIRA. You're so awesome!