Skip to content
animorio
IT
All components

The snowy footer

Footers, Physics, HoverCanvas, GSAPAdvancedCompressed weight: Demo 21 KBGSAP 28 KBFreeDownload the zip

The night-blue footer of a mountain hut, with its name in giant red letters along the bottom: snow falls over the footer and settles on the top edge of every letter, of the email field and of the round buttons, in the shape real snow takes. A hand passed over the letters sweeps the snow off in a puff of powder and the caps fill up again; pointing at a link settles snow on its letters too, and signing up to the snow report brings a fresh fall.

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 snowy footer</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the footer of a mountain hut: its name stands in giant red letters along the bottom, and snow falls over the
         footer and settles on them, in caps that keep their angle of repose. A hand sweeps a letter clean and the
         powder flies, the caps fill up again; signing up to the snow report brings a fresh fall -->
    <footer class="ft" data-footer data-autoclic>
      <canvas class="ft__snow" aria-hidden="true" data-snow></canvas>

      <div class="ft__top">
        <form class="ft__form" action="#" novalidate aria-labelledby="ft-news" data-form>
          <h2 class="ft__title" id="ft-news">The Monday snow report</h2>
          <p class="ft__lede">Snow depth, open routes and free bunks, every Monday.</p>
          <label class="sr" for="ft-email">Email address</label>
          <div class="ft__field" data-field>
            <input class="ft__input" id="ft-email" name="email" type="email" inputmode="email" autocomplete="email" autocapitalize="off" spellcheck="false" enterkeyhint="send" placeholder="you@example.com" aria-describedby="ft-note" aria-invalid="false" data-input>
            <button class="ft__send" type="submit" data-send>
              <span class="ft__send-bg"></span>
              <span class="ft__send-label" data-send-label>Sign up</span>
              <svg class="ft__spin" viewBox="0 0 24 24" aria-hidden="true" data-spin><circle cx="12" cy="12" r="8"/></svg>
              <svg class="ft__done" viewBox="0 0 24 24" aria-hidden="true" data-done><path d="M6.4 12.6l3.7 3.7 7.6-8.6"/></svg>
            </button>
          </div>
          <p class="ft__note" id="ft-note" aria-live="polite" data-note><span class="ft__line">One email a week. Leave whenever you like.</span></p>
        </form>

        <nav class="ft__cols" aria-label="Footer">
          <div class="ft__col">
            <h2 class="ft__head"><span class="ft__head-text">Stay</span><button class="ft__toggle" type="button" aria-expanded="false" aria-controls="ft-stay" data-toggle>Stay<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M3.5 6l4.5 4.5L12.5 6"/></svg></button></h2>
            <ul class="ft__list" id="ft-stay" data-list>
              <li><a class="ft__link" href="#" data-link>Book a bunk</a></li>
              <li><a class="ft__link" href="#" data-link>Prices</a></li>
              <li><a class="ft__link" href="#" data-link>Half board</a></li>
              <li><a class="ft__link" href="#" data-link>Groups</a></li>
            </ul>
          </div>
          <div class="ft__col">
            <h2 class="ft__head"><span class="ft__head-text">Routes</span><button class="ft__toggle" type="button" aria-expanded="false" aria-controls="ft-routes" data-toggle>Routes<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M3.5 6l4.5 4.5L12.5 6"/></svg></button></h2>
            <ul class="ft__list" id="ft-routes" data-list>
              <li><a class="ft__link" href="#" data-link>Hut to hut</a></li>
              <li><a class="ft__link" href="#" data-link>Ski tours</a></li>
              <li><a class="ft__link" href="#" data-link>Conditions</a></li>
              <li><a class="ft__link" href="#" data-link>Guides</a></li>
            </ul>
          </div>
          <div class="ft__col">
            <h2 class="ft__head"><span class="ft__head-text">The hut</span><button class="ft__toggle" type="button" aria-expanded="false" aria-controls="ft-hut" data-toggle>The hut<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M3.5 6l4.5 4.5L12.5 6"/></svg></button></h2>
            <ul class="ft__list" id="ft-hut" data-list>
              <li><a class="ft__link" href="#" data-link>Our story</a></li>
              <li><a class="ft__link" href="#" data-link>Getting here</a></li>
              <li><a class="ft__link" href="#" data-link>Kit list</a></li>
              <li><a class="ft__link" href="#" data-link>Contact</a></li>
            </ul>
          </div>
        </nav>
      </div>

      <div class="ft__base">
        <p class="ft__brand">
          <svg class="ft__mark" viewBox="0 0 32 32" aria-hidden="true"><path class="m-hill" d="M1.5 28L11.2 11.6l4.4 7.1 4.6-7.8L30.5 28"/><path class="m-hut" d="M9.2 28v-8.6l6.8-5.8 6.8 5.8V28z"/><path class="m-door" d="M14.3 28v-4.4h3.4V28z"/><path class="m-snow" d="M7.4 19.8l8.6-7.6 8.6 7.6-1.4 1.1L16 15l-7.2 5.9z"/></svg>
          <span class="ft__name">Rimefell Hut</span>
          <span class="ft__copy"><svg viewBox="0 0 12 12" aria-hidden="true"><circle cx="6" cy="6" r="5.1"/><path d="M7.9 4.5a2.3 2.3 0 1 0 0 3"/></svg><span class="sr">Copyright</span> 2026 Rimefell Hut Trust</span>
        </p>
        <div class="ft__social">
          <a class="ft__icon" href="#" data-icon><span class="sr">Hut webcam</span><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="7" width="13" height="10.5" rx="2.4"/><circle cx="9.5" cy="12.25" r="2.6"/><path d="M16 10.6l4.6-2.4v8.6L16 14.4"/></svg></a>
          <a class="ft__icon" href="#" data-icon><span class="sr">Trip journal</span><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 7.2c-2.4-1.6-5.2-2-8-1.4v12.4c2.8-.6 5.6-.2 8 1.4 2.4-1.6 5.2-2 8-1.4V5.8c-2.8-.6-5.6-.2-8 1.4z"/><path d="M12 7.2v12.4"/></svg></a>
          <a class="ft__icon" href="#" data-icon><span class="sr">Route maps</span><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3.5 6.4l5.4-2 6.2 2 5.4-2v13.2l-5.4 2-6.2-2-5.4 2z"/><path d="M8.9 4.4v13.2M15.1 6.4v13.2"/></svg></a>
        </div>
        <nav class="ft__legal" aria-label="Legal">
          <a class="ft__link ft__link--small" href="#" data-link>Privacy</a>
          <a class="ft__link ft__link--small" href="#" data-link>Terms</a>
          <a class="ft__link ft__link--small" href="#" data-link>Cookies</a>
        </nav>
      </div>

      <!-- the hut's name, drawn: each letter is a path, and the snow settles on its top edge -->
      <svg class="ft__giant" viewBox="0 -2 1077.5 102" aria-hidden="true" data-giant>
        <defs>
          <clipPath id="ft-line1" clipPathUnits="userSpaceOnUse"><rect x="-20" y="-60" width="582" height="160"/></clipPath>
          <clipPath id="ft-line2" clipPathUnits="userSpaceOnUse"><rect x="562" y="-60" width="536" height="160"/></clipPath>
        </defs>
        <g clip-path="url(#ft-line1)">
          <path data-letter d="M0 100H37V66.4H87.7Q109 66.4 121.4 58.5Q133.7 50.5 133.7 33.2Q133.7 15.8 122 7.9Q110.3 0 90.3 0H0ZM95.2 100H137.1L91.6 53H53.8ZM37 44.1V23.8H79.8Q88.6 23.8 92.6 26.2Q96.7 28.7 96.7 34Q96.7 39.2 92.7 41.6Q88.8 44.1 79.8 44.1Z"/>
          <path data-letter d="M162.6 100H199.2V0H162.6Z"/>
          <path data-letter d="M229 100H265.6V24.5H265.9L295.9 98.3H340.6L370.6 24.5H370.8V100H407.5V0H347.4L318.3 73.2H318.1L289.1 0H229Z"/>
          <path data-letter d="M431.8 100H552.8V76.2H468.8V60.7H545.6V38.6H468.8V23.8H552.8V0H431.8Z"/>
        </g>
        <g data-line2>
          <g clip-path="url(#ft-line2)">
            <path data-letter d="M572.4 100H609.5V67.2H677.6V45.2H609.5V23.8H684.8V0H572.4Z"/>
            <path data-letter d="M703.2 100H824.2V76.2H740.2V60.7H817V38.6H740.2V23.8H824.2V0H703.2Z"/>
            <path data-letter d="M846.2 100H952.1V76.2H883.2V0H846.2Z"/>
            <path data-letter d="M971.6 100H1077.5V76.2H1008.6V0H971.6Z"/>
          </g>
        </g>
      </svg>
    </footer>
  </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.

56 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 snowy footer” from Animorio and fit it into my site

What it does

On a white background a night-blue footer with rounded corners, 1040 pixels wide and about 400 tall, for Rimefell Hut, a made-up mountain hut. Top left the sign-up to the Monday snow report (The Monday snow report, a line saying what arrives, the email field with the Sign up button inside it and a line of help under it), on the right three columns of links (Stay, Routes, The hut); then, after a rule, the mark (a red hut with a snowy roof in front of two peaks), the copyright, three round buttons with drawn icons (the hut webcam, the trip journal, the route maps) and the legal links; and all along the bottom the hut's name, RIMEFELL, in broad, solid red letters. When the footer comes into view the letters come up out of their line one after the other with a little bounce and a heavy fall begins, then eases: the flakes fall at three depths (small and faint at the back, big and faster in front), sway and drift with the wind, and the near ones settle. The snow piles up on the top edge of every letter, of the email field and of the round buttons, in the shape it would really take: flat and soft on the E and the F, with rounded shoulders and a small lip bulging past the corners, a pillow on the foot of the L leaning against the stem, nothing on sides too steep to hold it (the leg of the R, the notch of the M), a dome thinning out on the round buttons. The pointer passed over the letters is a sweeping hand: where it goes the snow is gone, down to the fingers if it only skims it, and flies off in a puff of powder the way the hand went, with a few clumps that drop and land lower down; the sides of the channel soften and, while it snows, the caps fill up again in a few seconds. A click on a letter knocks all its snow off at once and the letter gives a little, with a bounce. Pointing at a link, or reaching it with the Tab key, turns the word the red of the name and the snow blows in from the left and settles on its letters, which sink a pixel under the weight; moving away puffs the snow off. Over a round button the button jumps, the icon shakes and the snow falls off it. The sign-up has its states: a wrong address shakes the field, which loses its snow, and the line under it says in coral what is wrong; a right one sends the word out of the button, which narrows to a circle with a spinning arc and then a tick, the line under it says thank you, and a fresh fall arrives: the flakes come thicker and bigger, the caps pile higher than usual and then settle.

How it works

The top edge, read from the letters

Each letter of the name is an SVG path. To know where the snow lands, the letter is drawn once on a hidden canvas at twice the size, and for every column of pixels the first solid pixel from the top is found: that is its edge. The email field (a pill) and the round buttons have their edge worked out from the circle; the word of a link pointed at is drawn in its own font, on the baseline given by fontBoundingBoxAscent and fontBoundingBoxDescent.

How much snow each column holds

For every column the most snow it can hold is worked out the way real snow behaves: where the edge is steeper than the angle of repose (STEEP pixels up for each pixel across) nothing stays, on a gentle slope less stays the steeper it is, on a corner only a thin lip clings and from there the cap rises in a quarter-circle shoulder to its full depth (DEPTH, never so deep it reaches the line above), and against a wall rising beside it the snow piles up. Two passes, forwards and back, keep the surface never steeper than STEEP; stretches narrower than seven pixels hold nothing.

The snowfall

The flakes are on three depths with their own size, speed and opacity; they fall, sway and drift with the wind (WIND, which slowly rises and falls). Those of the two front depths that cross the surface of the snow settle there and add to it in a bell over nine columns; meanwhile every cap grows towards its most in REFILL seconds, faster in a heavy fall. Every step the snow evens out along the edge, measured against its most, so no spikes grow and the walls of a channel soften. When the fall eases off, the extra flakes fade where they are, all over.

The sweeping hand

Between two positions of the pointer the hand moves in small steps, so a quick pass misses nothing. Within its width (BRUSH) the snow is taken off down to the height of the fingers, and more stays towards the sides of the hand, with a soft edge; what comes off becomes powder thrown the way the hand went, which hangs in the air and fades, and a few clumps that drop with gravity and land on the edge further down. A click on a letter knocks all its snow off, while GSAP makes it give and come back with an elastic.out.

Drawn behind, never on top

Each stretch of snow is one shape: the surface in quadratic curves, a lip bulging past the corner in a Bézier curve, and the bottom a pixel inside the thing it lies on, filled with a gradient from white to the blue of its shade. The canvas sits behind the whole footer: the letters, the field and the buttons hide the snow's foot, so nothing is ever covered. Every frame the position of every edge is read again from its box, so the snow follows the letter coming up or giving, the field shaking, the button jumping and the column opening on a phone.

Values to change

NameFileDefaultWhat it changes
SNOWFALLscript.js1How hard it snows: at 0 the flakes stop, the caps are already there and a swept letter stays clean.
WINDscript.js16How hard the wind pushes the flakes sideways, in pixels a second (below zero it blows to the left).
DEPTHscript.js16The deepest the snow lies on the big letters, in pixels (less on a phone, and never up to the line above).
STEEPscript.js1.6How steep the snow can stand, in pixels up for each pixel across: more makes squarer caps, less pointed domes.
REFILLscript.js6Roughly how many seconds a swept letter takes to fill up again.
BRUSHscript.js26How wide the hand that sweeps the snow off is, in pixels.
POWDERscript.js1How much powder flies when the snow is knocked off: at 0 the snow just goes.
WAITscript.js1.1How long the sign-up takes in the demo, in seconds.

Try them in the Playground

When to use it

For the footer of a mountain hut, a ski school, a winter sports shop, a mountain resort, a winter market or festival: anywhere the bottom of the page can have its season, with a big name for the snow to settle on.

On phones

The footer takes the width of the screen: the newsletter goes on top and the three columns fold away behind their headings, rows 52 pixels tall with an arrow that turns: a tap opens a column, which grows with its links (44-pixel rows) coming down one after the other. The mark, the round buttons and the legal links wrap. The name splits into two lines, RIME and FELL, so the letters stay big, and each line has its own snow, a little shallower. A finger dragged sideways over the letters sweeps the snow (up and down scrolls the page), a tap on a letter knocks it all off, and tapping a link settles snow on its letters for a moment.

With reduced motion

Nothing falls: the caps are already in place, still, and the canvas is only drawn when something changes. The name does not come up, it fades in with its snow; the hand does not sweep and a click does not knock the letters. Pointing at a link turns the word red and the snow fades in on its letters, without the word sinking; the round buttons do not jump, a wrong field does not shake, the button does not narrow and the arc keeps still, and a sign-up brings no fresh fall. On a phone the columns open without sliding, the links fading in.

Pitfalls

  • The first caps, with a constant slope from the corners, were flat slabs with a straight bevel and looked like icing: now the shoulder is a quarter circle rising from a thin lip to the full depth, and over a corner the snow bulges out a little, rounded.
  • On the round buttons and on sloping sides the snow ran down the sides like a helmet with ear flaps: now a column steeper than STEEP holds nothing, and on a gentle slope less stays the steeper it is.
  • On the leg of the R and at the bottom of the M's notch two white specks were left that looked like mistakes: stretches narrower than seven pixels let the snow slide off (three for the small words of the links).
  • Sweeping left a row of spikes: the hand took the snow off with a round bottom, and the clumps falling back made sharp little heaps. Now the hand cuts flat with soft sides, what lands spreads in a bell over nine columns, and every step the snow evens out against its most.
  • The first idea for the links was a white copy of the word a hair higher, behind it: with a light word it looked like doubled text. Now real snow settles on the links, read from the word's letters as for the name, and the word turns red so the snow stands out.
  • Reading the edge of a word with the canvas needs the right baseline: it comes from the font's fontBoundingBoxAscent and fontBoundingBoxDescent, with the leading split half above and half below as the browser does.
  • Opening a column on a phone the footer grows, the canvas changes size and is cleared, and for a few frames the snow was gone: as soon as its size changes the canvas is drawn again at once, without waiting for the next frame.
  • After the heavy fall the extra flakes were only taken away as they reached the bottom, and an empty band came down from the top: now the extra flakes fade where they are, all over.
  • With a high DEPTH, or the fresh fall after a sign-up, the caps reached the legal links and the snow ended up behind the text: the depth and the fresh fall's growth are held to the room over the letters (and between the two lines on a phone).
  • The coral ring of a wrong field did not show while the field was still active: the :has(.ft__input:focus) rule was more specific; now the error wins with the focus too.
  • The swept powder rose as high as the copyright line and for a moment filled the space between its words: now it leaves almost sideways, along the caps, and rises little.

Libraries

GSAP 3.15.0, Archivo (font variabile) 5.3.0.

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

Testing

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