accordion.html
<div style="display: grid; gap: 1rem">

    <p-accordion name="example" class="accordion">
        <summary class="accordion__summary">
            <h3 class="accordion__heading">
                Hey wait - is this a pure CSS accordion now?
            </h3>
            <div class="accordion__iconWrapper">
                <svg class="accordion__icon icon icon--chevron-right" aria-hidden="true" role="presentation">
                    <use href="/main-icons-sprite.svg#chevron-right" />
                </svg>
            </div>
        </summary>
        <div class="accordion__content">
            <p>
                You bet it is – it works great, animates on opening and closing, but there's only one real gotcha now...
                <strong>browser support.</strong>
            </p>
            <p>
                Neither Firefox nor Safari support <code>interpolate-size: allow-keywords</code> which is required for this
                implementation.
            </p>
        </div>
    </p-accordion>

    <details name="example" class="accordion accent-primary">
        <summary class="accordion__summary">
            <h3 class="accordion__heading">
                Why do we need a FAQ style module?
            </h3>
            <div class="accordion__iconWrapper">
                <svg class="accordion__icon icon icon--chevron-right" aria-hidden="true" role="presentation">
                    <use href="/main-icons-sprite.svg#chevron-right" />
                </svg>
            </div>
        </summary>
        <div class="accordion__content">
            <p>
                Donec mattis consequat libero at fermentum. Phasellus ultricies ultrices felis eu dapibus. Donec nec pellentesque eros. Proin cursus, felis eu sollicitudin sodales, mi tellus posuere risus, id vehicula enim nisi id risus. Praesent ultrices, eros tempor tincidunt vehicula, sem orci imperdiet ante, iaculis egestas arcu dolor pretium dui. Nullam egestas cursus diam quis ultrices. Praesent mollis ligula vel lorem gravida, at commodo nibh posuere. Maecenas id mollis metus, sit amet ultricies urna. Pellentesque congue elementum massa, a tristique sem feugiat ac.
            </p>
        </div>
    </details>

    <details name="example" class="accordion accent-tertiary">
        <summary class="accordion__summary">
            <h3 class="accordion__heading">
                Is this considered a frequently asked question?
            </h3>
            <div class="accordion__iconWrapper">
                <svg class="accordion__icon icon icon--chevron-right" aria-hidden="true" role="presentation">
                    <use href="/main-icons-sprite.svg#chevron-right" />
                </svg>
            </div>
        </summary>
        <div class="accordion__content">
            <p>
                Donec mattis consequat libero at fermentum. Phasellus ultricies ultrices felis eu dapibus. Donec nec pellentesque eros. Proin cursus, felis eu sollicitudin sodales, mi tellus posuere risus, id vehicula enim nisi id risus. Praesent ultrices, eros tempor tincidunt vehicula, sem orci imperdiet ante, iaculis egestas arcu dolor pretium dui. Nullam egestas cursus diam quis ultrices. Praesent mollis ligula vel lorem gravida, at commodo nibh posuere. Maecenas id mollis metus, sit amet ultricies urna. Pellentesque congue elementum massa, a tristique sem feugiat ac.
            </p>
        </div>
    </details>

    <details name="example" class="accordion">
        <summary class="accordion__summary">
            <h3 class="accordion__heading">
                Can this be used in more general situations?
            </h3>
            <div class="accordion__iconWrapper">
                <svg class="accordion__icon icon icon--chevron-right" aria-hidden="true" role="presentation">
                    <use href="/main-icons-sprite.svg#chevron-right" />
                </svg>
            </div>
        </summary>
        <div class="accordion__content">
            <p>
                Donec mattis consequat libero at fermentum. Phasellus ultricies ultrices felis eu dapibus. Donec nec pellentesque eros. Proin cursus, felis eu sollicitudin sodales, mi tellus posuere risus, id vehicula enim nisi id risus. Praesent ultrices, eros tempor tincidunt vehicula, sem orci imperdiet ante, iaculis egestas arcu dolor pretium dui. Nullam egestas cursus diam quis ultrices. Praesent mollis ligula vel lorem gravida, at commodo nibh posuere. Maecenas id mollis metus, sit amet ultricies urna. Pellentesque congue elementum massa, a tristique sem feugiat ac.
            </p>
        </div>
    </details>

    <details name="example" class="accordion">
        <summary class="accordion__summary">
            <h3 class="accordion__heading">
                Why use a different color for interactions vs. navigation?
            </h3>
            <div class="accordion__iconWrapper">
                <svg class="accordion__icon icon icon--chevron-right" aria-hidden="true" role="presentation">
                    <use href="/main-icons-sprite.svg#chevron-right" />
                </svg>
            </div>
        </summary>
        <div class="accordion__content">
            <p>
                Donec mattis consequat libero at fermentum. Phasellus ultricies ultrices felis eu dapibus. Donec nec pellentesque eros. Proin cursus, felis eu sollicitudin sodales, mi tellus posuere risus, id vehicula enim nisi id risus. Praesent ultrices, eros tempor tincidunt vehicula, sem orci imperdiet ante, iaculis egestas arcu dolor pretium dui. Nullam egestas cursus diam quis ultrices. Praesent mollis ligula vel lorem gravida, at commodo nibh posuere. Maecenas id mollis metus, sit amet ultricies urna. Pellentesque congue elementum massa, a tristique sem feugiat ac.
            </p>
        </div>
    </details>
</div>
index.scss
/**
 * @uses PAccordion
 */
.accordion {
    $b: &;

    background: white;
    border-radius: .5rem;
    box-shadow: var(--root-box-shadow-low);
    overflow: hidden;

    &::details-content {
      height: 0;
      transition: height var(--accordion-transition-duration, var(--root-duration-slow)) var(--root-ease-out);

      @supports (interpolate-size: allow-keywords) {
        transition: content-visibility var(--root-duration-slow) allow-discrete,
          height var(--root-duration-slow) var(--root-ease-out);
      }
    }

    &[open]::details-content {
      height: var(--accordion-height);


      @supports (interpolate-size: allow-keywords) {
        height: auto;
      }
    }

    &__summary {
        --heading-color: var(--root-color-headings);

        align-items: center;
        border-radius: .5rem;
        cursor: pointer;
        display: grid;
        grid-auto-flow: column;
        justify-items: start;
        list-style: none;
        outline-offset: -2px;
        padding: .5rem;
        user-select: none;

        &::-webkit-details-marker {
          display: none;
        }
    }
    
    &__heading {
        margin: 0;
        padding: .25rem 1rem;
    }
    
    &__iconWrapper {
        background: var(--accent-color);
        display: grid;
        justify-self: end;
        padding: .5rem;
    }
    
    &__icon {
        --icon-color: var(--accent-color-contrast);
        aspect-ratio: 1;
        height: var(--root-font-size-h3);
        transition: transform var(--root-duration-moderate) var(--root-ease-out);
    }
    
    &__content {
        padding: 0 1.5rem 1.5rem;
    }
    
    &[open] {
        box-shadow: var(--root-box-shadow-high);
        
        #{$b}__iconWrapper {
            background: var(--color-gray-200);
        }
        
        #{$b}__icon {
            --icon-color: #000;
            transform: rotate(90deg);
        }
    }
}
PAccordion.vue
<script setup>
import { useTemplateRef } from 'vue'

/**
 * PAccordion is a vue component for the <details> element. It uses the native <details> element
 * state to determine whether it's open or closed, and merely handles updating CSS properties
 * to allow the element to use transitions between dynamic closed and [open] heights.
 * 
 * @param {string} summary - selector for the summary element.
 *                           Default: 'summary:first-of-type'
 */
const props = defineProps({
  summary: { type: String, default: 'summary:first-of-type' }
})
const details = useTemplateRef('details')

const handleToggle = event => {
  const el = event.currentTarget
  const summary = el.querySelector(props.summary)

  if (!summary) {
    return
  }

  if (!summary.contains(event.target)) {
    return
  }

  el.style.removeProperty('--accordion-height')

  if (!el.open) {
    const startingHeight = el.clientHeight

    el.style.setProperty('--accordion-transition-duration', 0)

    requestAnimationFrame(() => {
      const endingHeight = el.clientHeight

      el.style.removeProperty('--accordion-transition-duration')
      el.style.setProperty('--accordion-height', 0)

      requestAnimationFrame(() => {
        el.style.setProperty('--accordion-height', `${endingHeight - startingHeight}px`)
      })
    })
  }
}
</script>

<template>
  <details ref="details" class="accordion" @click="handleToggle">
    <slot />
  </details>
</template>