Skip to content

Gallery / interactive / Accordion chevron morph

Accordion chevron morph

details/summary accordion — chevron flips, content unclips

interactive intermediate interactiveaccordiondetailsdisclosure
Live preview

Techniques: details-element · [open] · clip-path · @keyframes · :focus-visible

Customizeupdates preview, code & clipboard
#7F77DD
300px
0.35s
Code snippets/interactive/accordion-chevron-morph.html
<!--
  ============================================================
  SVGarden — https://www.svgarden.dev
  Author : Simon-Pierre Boucher
  Contact: contact@spboucher.ai
  File   : snippets/interactive/accordion-chevron-morph.html
  Desc   : details/summary accordion — chevron flips, content unclips
  ============================================================
-->
<div class="sg-accordion-chevron-morph" style="--sg-color:#7F77DD; --sg-size:300px; --sg-duration:0.35s;">
  <details class="sg-accordion-chevron-morph-item">
    <summary class="sg-accordion-chevron-morph-head">
      Why one file per snippet?
      <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <polyline points="7 10, 12 15, 17 10" />
      </svg>
    </summary>
    <div class="sg-accordion-chevron-morph-body">
      <p>Adding an animation means adding one file — the gallery, search index and category pages regenerate themselves at build time.</p>
    </div>
  </details>
  <details class="sg-accordion-chevron-morph-item">
    <summary class="sg-accordion-chevron-morph-head">
      Why &lt;details&gt; instead of JS?
      <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <polyline points="7 10, 12 15, 17 10" />
      </svg>
    </summary>
    <div class="sg-accordion-chevron-morph-body">
      <p>The element ships with state, keyboard handling and accessibility built in. CSS only decorates what the browser already does.</p>
    </div>
  </details>
</div>
<style>
  .sg-accordion-chevron-morph {
    width: var(--sg-size);
    border: 1px solid rgba(128, 128, 128, 0.35);
    border-radius: 10px;
    overflow: hidden;
    font-size: 0.95rem;
  }
  .sg-accordion-chevron-morph-item + .sg-accordion-chevron-morph-item {
    border-top: 1px solid rgba(128, 128, 128, 0.35);
  }
  .sg-accordion-chevron-morph-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.7rem 0.9rem;
    font-weight: 600;
    cursor: pointer;
    list-style: none;
    user-select: none;
  }
  .sg-accordion-chevron-morph-head::-webkit-details-marker { display: none; }
  .sg-accordion-chevron-morph-head:hover { color: var(--sg-color); }
  .sg-accordion-chevron-morph-head:focus-visible {
    outline: 2px solid var(--sg-color);
    outline-offset: -2px;
    border-radius: 8px;
  }
  .sg-accordion-chevron-morph-head svg {
    width: 1.15em;
    height: 1.15em;
    flex: none;
  }
  .sg-accordion-chevron-morph-head polyline {
    fill: none;
    stroke: var(--sg-color);
    stroke-width: 2.4;
    stroke-linecap: round;
    stroke-linejoin: round;
    transform-box: fill-box;
    transform-origin: center;
    transition: transform var(--sg-duration) ease;
  }
  .sg-accordion-chevron-morph-item[open] polyline {
    transform: scaleY(-1);
  }
  .sg-accordion-chevron-morph-body {
    padding: 0 0.9rem 0.8rem;
  }
  .sg-accordion-chevron-morph-body p {
    margin: 0;
    opacity: 0.8;
    line-height: 1.5;
  }
  .sg-accordion-chevron-morph-item[open] .sg-accordion-chevron-morph-body {
    animation: sg-accordion-chevron-morph-reveal var(--sg-duration) ease;
  }
  @keyframes sg-accordion-chevron-morph-reveal {
    from {
      clip-path: inset(0 0 100% 0);
      translate: 0 -6px;
      opacity: 0;
    }
    to {
      clip-path: inset(0);
      translate: 0 0;
      opacity: 1;
    }
  }
</style>

More interactive