Skip to content
animorio
IT
All components

The opening search

Search, Transitions, TextGSAPAdvancedCompressed weight: Demo 6.7 KBGSAP 28 KBFreeDownload the zip

A circle with a lens that opens into a search field: as you type the places below filter, the rows that stay slide to their new places, the others leave, the letters found light up and a band follows the arrow keys.

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 opening search</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/onest@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the search: a round button with a lens that opens into a field. Typing, the places below filter as you
         go: the rows that stay slide to their new places, the others leave, the letters found are lit, and a band
         follows the arrow keys or the pointer. The rows are made by the script -->
    <div class="find" data-find data-autoclic>
      <div class="find__bar" data-bar>
        <button class="find__open" type="button" aria-label="Search destinations" aria-expanded="false" data-open>
          <svg viewBox="0 0 24 24" aria-hidden="true" data-lens><path d="M10.5 4.5a6 6 0 1 0 0 12a6 6 0 1 0 0-12zM15 15l4.5 4.5"/></svg>
        </button>
        <input class="find__input" type="search" role="combobox" aria-label="Search destinations" aria-expanded="false" aria-controls="find-list" aria-autocomplete="list" autocomplete="off" autocapitalize="off" spellcheck="false" enterkeyhint="go" tabindex="-1" data-input>
        <span class="find__hint" aria-hidden="true" data-hint><span data-hint-in>Search destinations</span></span>
        <button class="find__close" type="button" aria-label="Close the search" tabindex="-1" data-close>
          <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 7l10 10M17 7 7 17"/></svg>
        </button>
      </div>
      <div class="find__panel" data-panel>
        <span class="find__pick" aria-hidden="true" data-pick></span>
        <ul class="find__list" id="find-list" role="listbox" aria-label="Destinations" data-list></ul>
        <p class="find__none" data-none></p>
      </div>
      <p class="sr" role="status" aria-live="polite" data-said></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.

51 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 opening search” from Animorio and fit it into my site

What it does

On a black background a dark circle with a lens. Pressed, the circle stretches into a pill-shaped field, the lens gives a little turn, a small cross appears at the end and the empty field says Search destinations, then by itself suggests Try “Kyoto”, Try “Porto” and Try “Tallinn”, rolling each time. Typing, a panel opens below with the places containing those letters, first those whose name starts that way, and the letters found are lime green, in the name or in the country. At every letter the panel grows or shrinks, the rows that stay slide to their new place, the ones that no longer match slide away fading and the new ones come in from above. A grey band sits on the first row and follows the arrow keys or the pointer; Enter or a click picks the place, which goes into the field while the panel closes and the field gives a little jolt. If nothing matches the panel says No places match and the letters typed. Escape empties the field and, once empty, closes it back into the circle.

How it works

From circle to field

The field is 56 pixels wide when closed and widens to WIDTH in OPEN seconds with expo.out; the text and the cross appear a little later, the lens turns by 90 degrees and back (yoyo), and the focus goes into the field. Closing, everything narrows again and the focus goes back to the lens.

The search

search() looks for the letters in the name and in the country regardless of capitals and accents, puts first the names that start that way and keeps SHOWN at most; lit() wraps the letters found in a mark. It is the only function to change to ask a server for the results.

Rows that move

The rows stay the same for each place (a Map). Before changing them the script measures where they are: those leaving step out of the flow where they are (all measured before any is moved) and slide away; those staying start from where they were and slide to their new place; the new ones come in a moment later. The panel grows or shrinks to the list’s height.

The band

The band is a single element that slides to the row picked (52 pixels a row). The field has role combobox and aria-activedescendant on the row picked, which has aria-selected; the arrows move it, Enter picks, Escape empties and then closes.

Suggestions and preview

With the field empty the suggestion changes every 2.4 seconds, among those in TRIES. A click from a script (detail 0, the library’s preview) moves the search on by one step: it opens it and types lis one letter at a time, then picks the place found, then closes it again.

Values to change

NameFileDefaultWhat it changes
WIDTHscript.js520How wide the field opens at most, in pixels.
OPENscript.js0.6The seconds the circle takes to open into the field.
SHOWNscript.js6How many places the panel shows at most.
TRIESscript.js['Search destinations', 'Try “Kyoto”', 'Try “Porto”', 'Try “Tallinn”']What the empty field suggests, in turn.
PLACESscript.js[['Lisbon', 'Portugal'], ['Lyon', 'France'], …]The places to search in: name and country.

Try them in the Playground

When to use it

For a site’s, a shop’s or a catalogue’s search, for picking a city, a product or a person from a list: wherever you search as you type.

On phones

The field opens to the width of the screen minus a margin; the text is 17 pixels, so iPhone does not zoom the page when it is tapped, and the keyboard shows Go. The rows are tapped to pick.

With reduced motion

The field opens and closes at once, the lens does not turn, the suggestions do not roll, the rows appear and disappear without sliding and the band jumps from one row to the next.

Pitfalls

  • The rows leaving must all be measured before taking any out of the flow: measured one at a time, each measure would count the one already gone and they would all end up in the same place.
  • A row still sliding from the previous change must be measured without the slide in progress (gsap.getProperty of y), or it would start again with a little jump.
  • Once a place is picked, the field holds «Lisbon, Portugal»: searched as it is, it would find nothing and say No places match. The script remembers it was a choice and does not search until something is typed again.
  • When the search closes the focus goes back to the lens, but after a tap Safari would show its outline: the script remembers whether the last gesture came from a finger, the mouse or the keys (data-using on the html) and the outline shows only with the keys.

Libraries

GSAP 3.15.0, Onest (font variabile) 5.3.0.

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

Testing

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