Skip to content
animorio
IT
All components

The split button

Buttons, Transitions, SVGGSAPMediumCompressed weight: Demo 6.4 KBGSAP 28 KBFreeDownload the zip

An action and the ways to do it in the same button: the arrow on the right opens a menu that unrolls from the button, the tick slides onto the way chosen and the way becomes the action, with the words rolling and the button taking their width. Pressed, it merges with a bar running along the bottom, then turns purple, the arrow folds away and it says Merged.

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 split button</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/hubot-sans@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the split button: the action on the left, the arrow on the right opens the ways to do it; the chosen way
         becomes the action, and pressed, the button merges and turns purple -->
    <div class="split" data-split data-autoclic>
      <span class="split__frame" aria-hidden="true" data-frame></span>
      <div class="split__btn" role="group" aria-label="Merge" data-group>
        <button class="split__main" type="button" data-main>
          <span class="split__icon" aria-hidden="true" data-icon></span>
          <span class="split__label"><span class="split__word" data-word>Merge pull request</span></span>
          <span class="split__bar" aria-hidden="true" data-bar></span>
        </button>
        <button class="split__caret" type="button" aria-haspopup="menu" aria-expanded="false" aria-controls="merge-menu" aria-label="Choose how to merge" data-caret>
          <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 10l5 5 5-5"/></svg>
        </button>
      </div>
      <div class="split__menu" id="merge-menu" role="menu" aria-label="How to merge" data-menu hidden>
        <span class="split__tick" aria-hidden="true" data-tick><svg viewBox="0 0 24 24"><path d="M5 12.6l4.3 4.3L19 7.2"/></svg></span>
        <button class="split__option" type="button" role="menuitemradio" aria-checked="true" tabindex="-1" data-option="merge" data-action="Merge pull request">
          <span class="split__title">Create a merge commit</span>
          <span class="split__text">All 3 commits from this branch go into main with a merge commit.</span>
        </button>
        <button class="split__option" type="button" role="menuitemradio" aria-checked="false" tabindex="-1" data-option="squash" data-action="Squash and merge">
          <span class="split__title">Squash and merge</span>
          <span class="split__text">The 3 commits are combined into one commit on main.</span>
        </button>
        <button class="split__option" type="button" role="menuitemradio" aria-checked="false" tabindex="-1" data-option="rebase" data-action="Rebase and merge">
          <span class="split__title">Rebase and merge</span>
          <span class="split__text">The 3 commits are replayed on top of main, one by one.</span>
        </button>
      </div>
      <p class="split__hint"><span data-hint>3 commits ready to go into main</span></p>
      <p class="sr" aria-live="polite" data-say></p>
    </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.

18 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 split button” from Animorio and fit it into my site

What it does

On a black background a green button in two parts: on the left a drawing and Merge pull request, on the right, past a thin line, an arrow pointing down; below, the line 3 commits ready to go into main. The arrow opens a dark menu that unrolls downwards, with the three ways coming in one after the other (Create a merge commit, Squash and merge and Rebase and merge, each with its explanation) and the tick appearing on the way chosen, while the arrow turns upwards. Choosing another way the tick slides onto it, the menu closes and the button's words roll to the name of the way, while the button widens or narrows to the new words and the drawing turns to make room for its own. Pressing the left part the words become Merging, the drawing becomes an open circle that spins and a light bar runs along the bottom of the button; done, the button turns purple, the arrow folds away until it is gone, the words say Merged and the button gives a little hop, while the line below says Merged into main. After a moment it is green again, with the arrow and the way chosen.

How it works

Two buttons in a group

The action and the arrow are two buttons in a group with a single background colour, so the green and the purple change together, and with overflow hidden on the rounded corners; the hover light is a white layer over each of the two, at 9% opacity.

The menu that unrolls

The menu opens with an inset clip-path coming down from the top (from inset(0% 0% 100% 0%) to inset(0%), with rounded corners), a scale from 0.95 to 1 and expo.out in UNFOLD seconds; the three ways come in from below 0.05 seconds apart, and the arrow turns 180 degrees.

A single tick

There is one tick for the whole menu: it sits at the height of the way chosen (offsetTop) and, choosing another, slides there with power3.inOut; after 0.28 seconds the menu closes, the words roll to the new name and the action takes its width, measured with an invisible copy of the words.

The merge

Pressed, the drawing becomes an open circle that spins and a 3 pixel bar grows from the left in MERGE seconds (scaleX with the origin on the left). Then the group's colour goes purple, the arrow narrows to zero width and the group passes through a scale of 1.05; after DONE seconds it is green again, with the arrow and the way chosen.

Keyboard and preview

The menu follows the menu button pattern: arrow down or up on the arrow opens it and moves to the way chosen or to the last one, the arrows go round the ways, Home and End go to the ends, Escape closes it and goes back to the arrow, Tab closes it. A press outside closes it. A click from a script (the library's preview) opens the menu, then picks the next way, then merges.

Values to change

NameFileDefaultWhat it changes
MERGEscript.js1.4The seconds the merge takes.
DONEscript.js2.2The seconds the button stays purple before it is ready again.
UNFOLDscript.js0.45The seconds the menu takes to unroll.

Try them in the Playground

When to use it

For a main action that has variants: merging or publishing in different ways, save or save as, send now or later, downloading in different formats, booking with or without breakfast.

On phones

The menu is 340 pixels wide, or the screen minus 32, centred under the button; a touch outside closes it, and each way, with its explanation, is more than 60 pixels tall, easy for a finger.

With reduced motion

The menu appears and disappears at once, the tick jumps to the way chosen, words and drawings change without rolling or turning, the waiting circle stays still and at the end the purple arrives without a hop; the merge bar still runs, because it tells how long is left.

Pitfalls

  • The menu is out of the page's flow: for the library's preview an invisible frame (data-frame) keeps the open menu's room, or the picture would cut it.
  • Disabling a button that has the focus makes the page lose the focus: during the merge aria-disabled is used and clicks are ignored.
  • A clip-path with rounded corners must have the same shape at the start and at the end (inset(… round 14px)), or GSAP cannot go from one to the other.
  • The colour set by GSAP stays written on the element: to go back to green the CSS variable is read again, so it follows a changed green too.
  • The arrow and the menu are two different elements: a press outside must leave out both, or a press on the arrow would close the menu that its click opens again at once.

Libraries

GSAP 3.15.0, Hubot Sans (font variabile) 5.3.0.

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

Testing

Tested on 4 October 2026

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

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