UI components

Accordion

Component for grouping details HTML elements where only one of them can be open at the same time.

An accordion is a vertically stacked group of collapsible sections. HTML has already a native element for this, the <details> element, but it doesn't support the "only one open at a time" behavior, so we need to add some JS to make it work, and that's what this component does.

If you don't need to ensure only one section is open at a time, you don't need this component at all, just use the <details> element directly.

Is it accessible?
Yes, more than any custom accordion you'll find on the web, because it's made with native elements.
Is it unstyled?
Yes. The Accordion doesn't need any styles, you are free to style your <details> elements however you need.
Can it be animated?
The <details> elements can be animated (with some restrictions) using CSS animations, or with JS.
<Accordion>
  <details>
    <summary>Is it accessible?</summary>
    <div class="panel">
      Yes, more than any custom accordion you'll find on the web, because it's made
      with native elements.
    </div>
  </details>

  <details>
    <summary>Is it unstyled?</summary>
    <div class="panel">
      Yes. The Accordion doesn't need any styles, you are free to style your
      <code><details></code> elements however you need.
    </div>
  </details>

  <details>
    <summary>Can it be animated?</summary>
    <div class="panel">
      The <code><details></code> elements can be animated (with some restrictions)
      using CSS animations, or with JS.
    </div>
  </details>
</Accordion>
@scope (#accordion-demo) {
  details {
    margin: 0;
    border-width: 1px;
    border-color: rgb(212 212 212);
    background-color: rgb(245 245 245);
    padding: 0;
    overflow: hidden;
    height: 3.5rem;
    width: 36rem;
    transition: all 0.15s ease-in-out;
  }
  details[open] {
    height: 10rem;
  }
  details:focus-within {
    box-shadow: 0 1px 2px 0 #2563eb;
  }
  details:first-of-type {
    margin-top: 0px;
    border-top-left-radius: 0.375rem;
    border-top-right-radius: 0.375rem;
  }
  details:last-of-type {
    margin-bottom: 0px;
    border-bottom-left-radius: 0.375rem;
    border-bottom-right-radius: 0.375rem;
  }
  summary {
    position: relative;
    height: 3.5rem;
    border-bottom-width: 1px;
    border-color: transparent;
    background-color: rgb(255, 255, 255);
    padding: 1rem 1.25rem;
    color: rgb(0, 0, 0);
    cursor: default;
  }
  details[open] > summary {
    border-color: rgb(229 229 229);
  }
  .panel {
    color: rgb(82 82 82);
    padding: 0 1.25rem;
    padding: 0.75rem 1.25rem;
  }
}

The Accordion is a simple wrapper plus some JS logic, so it doesn't uses any arguments and it's as accesible as the details element you put inside.

Events

The Accordion doesn't emit or listen to any events, but the <details> elements inside do.

In addition to the usual events supported by HTML elements, the <details> element supports the toggle event, which is dispatched to the <details> element whenever its state changes between open and closed. The Accordion component listen to it to be able to close the other <details> elements when one is opened.

The toggle event is sent after the state is changed, although if the state changes multiple times before the browser can dispatch the event, the events are coalesced so that only one is sent.

details.addEventListener("toggle", (event) => {
  if (details.open) {
    /* the element was toggled open */
  } else {
    /* the element was toggled closed */
  }
});