Skip to content
animorio
IT
All components

The pull-apart accordion

Accordions, SVG, Physics, TransitionsGSAPAdvancedCompressed weight: Demo 9.2 KBGSAP 28 KBFreeDownload the zip

The questions of an open-air pool sit on a single card the colour of water. Opening one, the card tears along both edges of its row like a liquid: the slits run in from the sides, the last thread snaps into two drops that sink back into the edges, and the piece comes away and stretches down over the answer. Closed again, the pieces reach for each other, touch, and the seam zips shut out to the sides.

The code

A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>The pull-apart accordion</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/geologica@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the questions of an open-air pool. At rest they are one card; opening a question, the card tears along both
         edges of its row like a liquid, the row comes away as a piece of its own and stretches down to show the
         answer. The card is a single path the script draws behind the rows -->
    <div class="faq" data-faq>
      <svg class="faq__shape" aria-hidden="true" focusable="false">
        <defs>
          <linearGradient id="faq-water" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="0" y2="600" data-gradient>
            <stop offset="0" class="faq__stop-top"/>
            <stop offset="1" class="faq__stop-deep"/>
          </linearGradient>
          <filter id="faq-lift" x="-20%" y="-20%" width="140%" height="150%" color-interpolation-filters="sRGB">
            <feDropShadow class="faq__shadow-far" dx="0" dy="14" stdDeviation="14"/>
            <feDropShadow class="faq__shadow-near" dx="0" dy="2" stdDeviation="1.5"/>
          </filter>
          <filter id="faq-float" x="-30%" y="-40%" width="160%" height="220%" color-interpolation-filters="sRGB">
            <feGaussianBlur in="SourceAlpha" stdDeviation="14"/>
            <feOffset dy="16" result="soft"/>
            <feFlood class="faq__float"/>
            <feComposite in2="soft" operator="in"/>
          </filter>
        </defs>
        <g filter="url(#faq-float)" data-lifts></g>
        <path class="faq__body" fill="url(#faq-water)" filter="url(#faq-lift)" data-body/>
        <g data-seams></g>
      </svg>

      <div class="faq__item" data-item>
        <h3 class="faq__head"><button class="faq__q" type="button" id="faq-q1" aria-expanded="false" aria-controls="faq-a1"><span class="faq__text">Do I need to book?</span><svg class="faq__chev" viewBox="0 0 16 10" aria-hidden="true"><path/></svg></button></h3>
        <div class="faq__a" id="faq-a1" role="region" aria-labelledby="faq-q1" hidden><div class="faq__inner"><p>Lanes go in 45-minute sessions you can book up to a week ahead. If a lane is free you can just walk in: the board by the gate shows which ones.</p></div></div>
      </div>
      <div class="faq__item" data-item>
        <h3 class="faq__head"><button class="faq__q" type="button" id="faq-q2" aria-expanded="false" aria-controls="faq-a2"><span class="faq__text">Is the water heated?</span><svg class="faq__chev" viewBox="0 0 16 10" aria-hidden="true"><path/></svg></button></h3>
        <div class="faq__a" id="faq-a2" role="region" aria-labelledby="faq-q2" hidden><div class="faq__inner"><p>All year round: 25 degrees in the long pool and 30 in the shallow one. On cold mornings the steam rising off the water is half the fun.</p></div></div>
      </div>
      <div class="faq__item" data-item>
        <h3 class="faq__head"><button class="faq__q" type="button" id="faq-q3" aria-expanded="false" aria-controls="faq-a3"><span class="faq__text">What should I bring?</span><svg class="faq__chev" viewBox="0 0 16 10" aria-hidden="true"><path/></svg></button></h3>
        <div class="faq__a" id="faq-a3" role="region" aria-labelledby="faq-q3" hidden><div class="faq__inner"><p>A swim cap, a coin for the lockers and a towel, or borrow one at the desk. Flip-flops help on the deck when the tiles are cold.</p></div></div>
      </div>
      <div class="faq__item" data-item>
        <h3 class="faq__head"><button class="faq__q" type="button" id="faq-q4" aria-expanded="false" aria-controls="faq-a4"><span class="faq__text">Can kids swim alone?</span><svg class="faq__chev" viewBox="0 0 16 10" aria-hidden="true"><path/></svg></button></h3>
        <div class="faq__a" id="faq-a4" role="region" aria-labelledby="faq-q4" hidden><div class="faq__inner"><p>From eight years old, after a short swim test at the desk. Younger children swim with an adult, one grown-up for every two children.</p></div></div>
      </div>
      <div class="faq__item" data-item>
        <h3 class="faq__head"><button class="faq__q" type="button" id="faq-q5" aria-expanded="false" aria-controls="faq-a5"><span class="faq__text">What if it storms?</span><svg class="faq__chev" viewBox="0 0 16 10" aria-hidden="true"><path/></svg></button></h3>
        <div class="faq__a" id="faq-a5" role="region" aria-labelledby="faq-q5" hidden><div class="faq__inner"><p>We clear the water at the first rumble of thunder and wait half an hour after the last one. Your session goes straight back into your account.</p></div></div>
      </div>
    </div>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="script.js"></script>
</body>
</html>

Playground

Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.

26 values to change

Or ask Claude Code for it

With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:

Take “The pull-apart accordion” from Animorio and fit it into my site

What it does

On a white background, alone in the middle, a card with round corners the colour of pool water, lighter at the top and deeper at the bottom, with five questions in white split by thin lines: Do I need to book?, Is the water heated?, What should I bring?, Can kids swim alone?, What if it storms?, each with a chevron. On arrival the five rows appear as separate drops and, from the top down, each one flows into the one above until the card is one; then the second question opens. Opening a question, two slits with round tips run in from the sides at both edges of its row, while the row drops slightly: they run towards the point where the question was pressed, the corners they leave behind round off and the thin line in between fades. When the two tips touch, a neck of water is left that thins and snaps: its two ends hang for a moment like drops, one under the piece above and one over the piece below, and sink back into the edges with a wobble, while the pieces hop apart. The torn-off piece stretches down and uncovers the answer, which arrives blurred and comes into focus; the pieces below slide down to make room and a shadow of its own grows under it, as if it floated. The chevron turns over through a flat line. Opening another one, the open one shortens and goes back up: when the pieces are close, the two torn ends stretch towards each other, touch, and the seam closes from where they touched out to the sides. Under the mouse the card pinches slightly at both edges of the question, where it would tear.

How it works

One card, one path

The card is a single SVG path rebuilt on every frame from the positions of the rows. Each group of rows still joined is a subpath: it goes down the right side and, at every seam that is tearing, into the slit (corner arc, straight edge, half an ellipse for the tip, straight edge, corner arc); then it goes back up the left side doing the same. The questions are absolutely placed buttons that only move with translate3d; each answer is cut by a clip-path inset at the bottom of its piece. While it moves, the page is never laid out again.

Springs that add up

How much of each answer shows and each gap between two rows are springs, with stiffness and damping worked out from a duration and a bounce as Apple describes them (OPEN and BOUNCE; the gaps get a quicker spring). The top of each row is the bottom of the one above plus the gap between them: the pieces below make room by themselves and never overlap, even when a spring goes past. The sums are done in steps of 1/240 of a second, with the frame step capped at 1/30.

The tear

A GSAP tween takes the tear's progress from 0 to 1 with power2.in, slow at first and then all at once, in TEAR seconds. Each slit is that fraction of the distance from its side to the meeting point (where the question was pressed; the middle for the keyboard), and its tip is half an ellipse as tall as the gap: when the two tips touch, the neck is zero wide. At 1 there are two pieces, each with its torn end: a bump made of two Bézier curves, 62 per cent of the gap tall, that sinks back on a spring set by WOBBLE while the gap gets a kick.

Sewing back

Closing, when the gap drops under 60 per cent of GAP the two ends stretch towards each other; when they touch the seam is whole again with the slits reaching the meeting point, and a power3.out tween takes them back to the sides in 0.36 seconds. Everything can be interrupted: opening again halfway, the tween turns round and the slit starts from where it is.

Shadows, lines and rest

The thin lines are only drawn on the bridge of water that is left and fade as the tear runs. The card has two feDropShadows; the open piece also has a shadow of its own, a narrower rectangle blurred and moved down, that grows with the answer. The loop runs on GSAP’s ticker only while something moves. The questions are buttons inside an h3 with aria-expanded and aria-controls, the answers regions that are hidden when closed; arrows, Home and End go from one question to another. The question after the open one has data-autoclic for the preview.

Values to change

NameFileDefaultWhat it changes
FIRSTscript.js1The question open when the page opens, from 0; −1 for none.
ONLY_ONEscript.jstrueOpening a question closes the open one; with false many can be open.
GAPscript.js16The room between two pieces of card that have come apart, in pixels.
OPENscript.js0.62The seconds an answer takes to open or close.
BOUNCEscript.js0.14How far the answers and the pieces go past their place and come back (0 not at all).
TEARscript.js0.42The seconds the tear takes to run across the card.
WOBBLEscript.js0.6How much a torn end wobbles as it sinks back into its edge (0 not at all).
HINTscript.js8How deep the card pinches at the edges of the question under the mouse, in pixels; 0 not at all.
ROUNDscript.js22The radius of the card’s corners, in pixels.

Try them in the Playground

When to use it

For the frequently asked questions of a service, a venue or an app, for the terms of a membership or the sections of a product sheet: wherever there are many answers and one is read at a time, and the site has a soft, fresh or playful character. The piece that has come away shows at a glance which answer is open, even from afar.

On phones

The card is at most 640 pixels wide and on a phone narrows to the screen, with a 16 pixel margin each side (358 on a 390 phone). Closed rows are 80 pixels tall on a computer and 72 on a phone, as in the library’s other accordion, and the questions go down to 17 pixels. A finger pressing a question makes the card pinch at its edges as the mouse over it does, and the tear runs towards the point touched.

With reduced motion

No tear and no movement: the card splits and closes again at once, already in place, and the answer’s text fades in. When the page opens the card fades in with the second question already open; no drops flowing together and no pinch under the mouse.

Pitfalls

  • A thread of light along the card’s top edges (the same path traced again a pixel lower and clipped inside the card) drew light lines inside the slits during the tear: they looked like scratches. It was taken out.
  • A clip-path on an element with a transform attribute is measured in the space already moved: the clip went down together with the stroke and clipped nothing. The clip goes on a g around it, the shift on the element inside.
  • With the gaps on the same spring as the answers, the slits ran across the card while still as thin as a hair and the tear looked like a cut with scissors. The gaps now have a quicker spring of their own: the slit is already open while it runs.
  • On arrival the drops flowed together while the card was still transparent, and the flow could not be seen. Now the gaps stay open until the rows have appeared, then they are let go from the top, one every 90 milliseconds.
  • The list is centred on the page: if its height followed the answers, opening one would lift its top by half. So it has a fixed height, with room for every question, the longest answer and two gaps.
  • The corner radius lived in a CSS variable the script only reads when it measures: changed live in the Playground, the corners did not follow. It became a value of the script (ROUND).
  • Two torn ends may never touch, if the gap closes faster than they stretch out or while they wobble below zero: the pieces would stay drawn apart at zero distance, with a notch at the sides. Under 0.6 pixels of gap the seam closes anyway.
  • On a phone, a question tapped just after arrival was closed a moment later by the second one opening by itself. Now a choice made earlier cancels the automatic opening.

Libraries

GSAP 3.15.0, Geologica (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 9.2 KB; the libraries: GSAP 28 KB.

Testing

Tested on 5 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: To do

License

Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.

All components