Skip to content
animorio
IT
All components

The file drop zone

Forms, Transitions, SVGGSAPAdvancedCompressed weight: Demo 8.3 KBGSAP 28 KBFreeDownload the zip

A dashed frame to drop files in: with files over the page the sheets fan out and the dashes join into one line; dropped, each file comes in as a row a sheet of its colour lands in, the bar fills up and a tick draws itself at the end, or the row turns red if the file is too big.

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 file drop zone</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/figtree@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the drop zone: three sheets inside a dashed frame. Dragging files over the page the sheets fan out and the
         dashes join into a line; dropped (or chosen with a click), the files come down as rows that fill up while
         they upload, then close with a tick. The upload is pretended: see send() in the script -->
    <div class="drop" data-drop>
      <div class="drop__zone" role="button" tabindex="0" aria-describedby="drop-hint" data-zone data-autoclic>
        <svg class="drop__frame" aria-hidden="true" data-frame>
          <rect class="drop__dash" data-dash/>
        </svg>
        <div class="drop__art" aria-hidden="true" data-art>
          <span class="sheet sheet--back" data-sheet></span>
          <span class="sheet sheet--mid" data-sheet></span>
          <span class="sheet sheet--front" data-sheet>
            <svg viewBox="0 0 24 24"><path d="M12 17V7.5M7.5 11.5 12 7l4.5 4.5"/></svg>
          </span>
        </div>
        <p class="drop__title" data-title><span data-title-in>Drop files here or <span class="drop__browse">browse</span></span></p>
        <p class="drop__hint" id="drop-hint">PDF, images or zip, up to 20 MB each</p>
        <input class="drop__input" type="file" multiple tabindex="-1" aria-hidden="true" data-input>
      </div>
      <ul class="drop__list" data-list></ul>
      <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.

25 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 file drop zone” from Animorio and fit it into my site

What it does

On a black background a dark frame with a dashed border, holding three sheets (two grey and a violet one in front, with an arrow pointing up), the words Drop files here or browse and below them PDF, images or zip, up to 20 MB each; the dashes walk slowly round the frame. With the pointer over it, the sheets open a little. Dragging files over the page the dashes turn violet and run, the sheets fan out and the words say Drop them in here; over the frame the dashes join into one line, the sheets fan right out and move with the pointer, and the words say Release to upload. Once the files are dropped the sheets dip and bounce, a wave goes out from the frame and for each file a sheet is tossed up and lands in its row taking the colour of the kind of file: red for PDFs, yellow for zips, light blue for images, violet for videos. The rows open one after the other with name and size, the violet bar fills while the percentage counts, and when the upload is done the bar folds away and a green tick draws itself next to Uploaded. A file bigger than 20 MB turns red, shakes and says Too big, 20 MB max. The little cross takes a row away: it slides off to the right while the others move up. On a phone the words become Tap to choose files.

How it works

A frame that walks

The dashes are an SVG rectangle the script fits to the frame. A stroke-dashoffset loops over 170 pixels, ten times the dash pattern (9 drawn and 8 empty), so the loop has no seam; each state changes its speed with timeScale, and over the frame stroke-dasharray goes from 9 8 to 17 0 and the dashes become a line.

Files over the page

The window listens to dragover only for drags carrying files: it tells whether they are over the frame or outside it and restarts a 0.2-second wait. dragover keeps coming as long as the files are over the page, so when the wait runs out the files have gone and the frame goes back to rest.

The fan

Each sheet has a place at rest and a direction to open in: fan() takes them to a share of FAN, a little with the pointer over the frame, halfway with files over the page, all of it over the frame, where the sheets also move towards the pointer. When the files are dropped the sheets dip and come back up with elastic.out, and a copy of the rectangle grows and fades.

Sheets into the rows

New rows are added to the list and the script measures straight away where their little sheet will end up, before they open from zero height. A copy of the front sheet flies there: x on one curve, y first up by 34 pixels and then down, so it is a toss; meanwhile it shrinks to the little sheet’s size and takes the colour of the kind of file.

The upload

send() is a pretend one: it gets the file, a function to call with the part sent so far (from 0 to 1) and a signal saying whether the row has been removed, and answers with a promise; the real upload goes in its place, for example an XMLHttpRequest with upload.onprogress. When it is done the bar folds away and the tick draws itself with stroke-dashoffset; over MAX_MB the row turns red and shakes.

Values to change

NameFileDefaultWhat it changes
MAX_MBscript.js20The biggest file accepted, in megabytes.
FANscript.js[16, 24, 12]How far the sheets fan out with files over the frame: degrees, pixels sideways and pixels up.
SPEEDscript.js7The pretend upload’s speed, in megabytes a second.
MARCHscript.js16How fast the dashes run round the frame with files over it, in pixels a second.
SAMPLESscript.js[['autumn-brochure.pdf', 2.4], ['farm-photos.zip', 14.8], ['drone-flight.mov', 48.2]]The pretend files the library’s preview drops, with their size in megabytes.

Try them in the Playground

When to use it

For uploading documents, photos or attachments: in a contact or application form, in a customer area, in a site’s back office.

On phones

On a phone files are not dragged: the words become Tap to choose files and a tap opens the file picker. The rows take the width of the screen and long names end with an ellipsis.

With reduced motion

The dashes stand still and the sheets go from one position to the next without moving; no toss, bounce or shake: the rows appear already open, the words beside them change at once and the bar fills in steps.

Pitfalls

  • dragenter and dragleave also fire when moving from one child element to another, and counting them easily goes wrong: better dragover, which keeps coming as long as files are over the page, and going back to rest when it stops.
  • Without preventDefault on dragover and drop, a file dropped outside the frame would be opened by the browser in place of the page: the window stops both, but only for drags of files.
  • A row closing down to nothing must not leave gaps: the room between the rows is a margin under each row, which closes with it, not the list’s gap.
  • The spot where the sheet lands must be measured right after adding the rows, before they start opening from zero height: measured later, the sheet would land in the wrong place.
  • A real click on the frame opens the file picker; one coming from a script (detail 0, the library’s preview) drops some pretend files, so the preview never opens windows.

Libraries

GSAP 3.15.0, Figtree (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 8.3 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