Skip to content
animorio
IT
All components

The filters that rise into line

Tags, Transitions, TextGSAP, CSS onlyAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

In a minimal style: the filters of a recipe book set as light words on thin ruled lines. Choose one and the word rises along a curve into the top line, above a black rule, takes weight and a small vermilion cross draws itself after it; the others close the gap one after the other, and the ones that change line run out at one end of theirs and come back in at the other end of the next, like type reflowing. Underneath, the number of recipes left changes digit by digit.

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 filters that rise into line</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/standard.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the filters of a recipe book, set as words on ruled lines. Choosing one, it rises into the top line, takes
         weight and gets a small cross to take it away; the words after it close the gap, and the ones that change
         line run out at one end of their line and come in at the other end of the next, like type reflowing. The
         buttons are real and stay in the layout; the words you see are drawn over them by the script, so they can
         travel. The filters are made by the script from its list -->
    <div class="pantry" data-pantry data-autoclic>
      <div class="sheet" data-sheet>
        <div class="rules" aria-hidden="true" data-rules></div>
        <span class="rule-ink" aria-hidden="true" data-rule-ink></span>
        <p class="hint" aria-hidden="true" data-hint>Pick what you fancy</p>
        <div class="picked" role="group" aria-label="Chosen filters" data-picked></div>
        <div class="rest" role="group" aria-label="Filter the recipes" data-rest></div>
        <div class="ink" aria-hidden="true" data-ink></div>
      </div>
      <div class="foot">
        <button class="clear" type="button" aria-disabled="true" data-clear>Clear</button>
        <p class="count" aria-hidden="true"><span class="count__n" data-count></span> <span class="count__noun" data-noun>recipes</span></p>
        <p class="sr" aria-live="polite" data-live></p>
      </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.

32 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 filters that rise into line” from Animorio and fit it into my site

What it does

On a white background, nine words on thin sand-coloured rules, light and a little narrow: Vegan, Gluten-free, One pot, Spicy, Vegetarian, Dairy-free, No oven, Batch cook, High protein. At the top, above a black rule, the line of the chosen ones, where Weeknight already stands when the page opens, black and full, with its vermilion cross; at the bottom Clear on the left and, on the right, 83 recipes. When the page opens the rules draw themselves from the left one after the other and the words come up onto them. Choosing a word, it rises straight up to the black rule, runs along it to its place in the top line and steps up into it, taking weight and width with a small overshoot that settles; as it runs it narrows a little on the width axis. As soon as it lands its cross draws itself, one stroke after the other. The words that came after it close the gap one after the other, the nearest first; the ones that have to change line run out at the end of theirs, fading past the end of the rule, and come in at the other end of the next one. If the line of the chosen ones fills up it opens another: the black rule moves down a line and the words below move down all together. Touching a chosen word again, its cross draws back and the word goes back to its place among the others by the same way, losing weight, while the lines make room for it from the far end. The number of recipes changes digit by digit, from the right: new digits drop from above when there are fewer recipes and rise from below when there are more, arrive in vermilion and turn black. With nothing chosen the top line says Pick what you fancy and Clear dims; Clear sends every chosen word back to its place, the last one first. With the pointer over a word on the lines, it rises two pixels and takes a little weight; over a chosen one it loses a little, as if about to let go.

How it works

Real buttons, words drawn over them

Every filter is a button with aria-pressed, in two role="group" elements: the chosen ones on top, in the order they were chosen, and the others in the order of the list. Choosing, the button really moves from one group to the other (and gets its focus back), with the style of its place: so the layout, with flex-wrap, says where every word belongs. The buttons have transparent text; what you see is a copy of every word in a layer above, moved by the script, with the keyboard's ring travelling along with it.

Springs towards the layout's place

At every change the script measures where the buttons ended up (getBoundingClientRect, once) and every drawn word reaches its place on a damped spring (SPRING), integrated on the GSAP ticker in steps of a quarter of a frame, with the step capped at 1/30 of a second. Weight and width follow a second, softer spring, from WEIGHT[0] to WEIGHT[1] and from WIDTH[0] to WIDTH[1], which goes a little past and settles; speed takes width away (SQUEEZE). A change halfway is just a new place to reach. When nothing moves the ticker stops.

The flight along the rule

A word that changes group flies on a cubic Bézier curve: its two control points stand above the start and above the end, at the height of the black rule. So it leaves straight up (or down), runs along the rule with its text resting on it, without passing over the words of the top line, and arrives straight into its place. How far along the curve it is comes from a spring (FLIGHT); past the end the curve carries on along its tangent, so the overshoot is a pixel, upright.

Lines that reflow

A word that stays in its group but changes line never crosses the others: its copy runs out at the end of the line it was on (a cloned ghost, removed afterwards) and the word comes in at the other end of its new line. A gradient mask, worked out in pixels in the word's own coordinates, fades it over the 16 pixels past the end of the rule. The line is counted from the start of its group, so when the line of the chosen ones opens another, the words below move down all together, with the black rule, instead of reflowing.

A choreography that never collides

The delay of every word (RIPPLE) depends on its direction: words closing a gap go from the gap forwards, after a tenth of a second that lets the chosen word get away; words making room go from the far end backwards. So no word ever catches up with the one in front. With Clear the chosen words go back one after the other, the last one first. The count sits in windows one tabular figure wide: the old digit and the new one slide together on the same spring, the new one in vermilion fading to black; a digit no longer needed closes its window.

Keys, reduced motion and the preview

Tab, Enter and Space work as always, and an aria-live region says what changed and how many recipes are left. With reduced motion the words that change place fade out and back in already in place, the digits with a fade. A click from a script on the component (detail 0, the library's preview) plays a round: it chooses Vegetarian, Spicy and One pot, drops Weeknight, empties everything with Clear and puts Weeknight back, ending as it started.

Values to change

NameFileDefaultWhat it changes
FILTERSscript.js['Vegan', 'Weeknight', 'Gluten-free', …]The filters, in their order on the lines (names the example's book does not know get random recipes).
STARTscript.js['Weeknight']The filters already chosen when the page opens.
RECIPESscript.js186How many recipes the example's book has.
NOUNscript.js['recipe', 'recipes']What is being counted, singular and plural.
SPRINGscript.js[240, 0.8]The springs the words travel on: how stiff, and how damped (1 lands without overshoot).
FLIGHTscript.js0.4How quick the flight between the lines and the top line is, against the other springs.
RIPPLEscript.js0.035The seconds between one word moving and the next.
WEIGHTscript.js[330, 640]The weight of the words on the lines and of the chosen ones (200 to 800).
WIDTHscript.js[90, 100]The width axis of the words on the lines and of the chosen ones (75 to 100).
SQUEEZEscript.js12How much a running word narrows on the width axis.

Try them in the Playground

When to use it

For filters that add up above a list: the recipes of a food blog, the products of a shop, the stays of a travel site, the courses of a school. Wherever the choices need to be in view all together, with the number of results left, and taking a choice away must be as easy as making it.

On phones

At 390 pixels the lines are taller (44 pixels, good for a finger) and the words go on three lines; the line of the chosen ones opens a second one when it fills up and the lines below move down together, so the component keeps its height. A tap chooses and removes, and pressing squeezes the word a hair; the pointer's hover is not there.

With reduced motion

Nothing flies and nothing slides: the words that change place fade out and back in already in place, with the right weight and the cross already drawn; the number changes with a fade and the rules are already drawn when the page opens.

Pitfalls

  • The first flight was a straight spring towards the new place: y arrived before x and the word passed over the cross of the chosen word next to it. Now it flies on a Bézier curve that rises straight up and runs along the black rule, below the words of the top line.
  • Words that changed line went diagonally over the others: now they run out at one end of the line and come in at the other, with a ghost copy that leaves and a mask that fades them past the end of the rule.
  • When the line of the chosen ones opened a second line, every word below found itself on a new line and they all reflowed at once in a tangle: lines are now counted from the start of their group, so they just move down, all together, and the ghost of a word changing line moves down with them.
  • With the ripple always starting from the change, the words making room set off before the one in front of them and caught up with it: now the words moving forwards start from the far end.
  • In the counter the old digit and the new one had different timings and overlapped for a moment: now they slide on the same spring, further than the window is tall, and the window fades at the top and bottom.
  • Bricolage Grotesque's Q, with its long tail under the line, made a word look underlined at this size: the Quick filter became Weeknight.

Libraries

GSAP 3.15.0, Bricolage Grotesque (font variabile, con gli assi del peso, della larghezza e della grandezza ottica) 5.3.0.

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

Testing

Tested on 16 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