Skip to content
animorio
IT
All components

The roller blind switch

Toggles, SVG, Physics, HoverGSAP, CSS onlyMediumCompressed weight: Demo 6.0 KBGSAP 28 KBFreeDownload the zip

The night reading switch, in the minimal style: the track is a small window with a sky and a low sun, and the knob is the pull cord of a roller blind. Switched on, the blind unrolls with its fine folds and covers the window while night falls behind it; switched off, a tug and the spring rolls it up in a snap, bouncing twice against the roller.

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 roller blind switch</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">
    <div class="setting" data-setting>
      <div class="setting__text">
        <label class="setting__name" id="night-name" for="night-switch">Night reading</label>
        <span class="setting__sub" aria-hidden="true">
          <span class="setting__subwords">
            <span class="setting__s" data-sub-off>Blind up, full light</span>
            <span class="setting__s" data-sub-on>Blind down, easy on the eyes</span>
          </span>
        </span>
      </div>
      <div class="setting__ctrl">
        <span class="setting__word" id="night-word" aria-hidden="true">
          <span class="setting__w" data-word-off>Off</span>
          <span class="setting__w" data-word-on>On</span>
        </span>
        <button class="switch" id="night-switch" type="button" role="switch" aria-checked="false" aria-labelledby="night-name night-word" data-switch data-autoclic>
          <svg class="switch__art" viewBox="0 0 104 56" aria-hidden="true" focusable="false">
            <defs>
              <clipPath id="window">
                <rect x="2.5" y="2.5" width="99" height="51" rx="25.5"></rect>
              </clipPath>
              <linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
                <stop offset="0" data-sky-top stop-color="#dfe9ef"></stop>
                <stop offset="1" data-sky-bottom stop-color="#fbf6ec"></stop>
              </linearGradient>
              <linearGradient id="fall" x1="0" y1="0" x2="0" y2="1">
                <stop offset="0" stop-color="#000" stop-opacity="0.16"></stop>
                <stop offset="1" stop-color="#000" stop-opacity="0"></stop>
              </linearGradient>
            </defs>
            <g clip-path="url(#window)">
              <rect class="switch__sky" x="0" y="0" width="104" height="56" fill="url(#sky)"></rect>
              <circle class="switch__sun" data-sun cx="32" cy="48" r="10"></circle>
              <path class="switch__moon" data-moon d="M0 -5.2 A5.2 5.2 0 1 0 4.6 2.4 A4.1 4.1 0 1 1 0 -5.2 Z"></path>
              <rect class="switch__fall" data-fall x="0" y="0" width="104" height="5" fill="url(#fall)"></rect>
              <rect class="switch__cloth" data-cloth x="0" y="0" width="104" height="10"></rect>
              <path class="switch__slats" data-slats d=""></path>
              <rect class="switch__rail" data-rail x="0" y="0" width="104" height="1.8"></rect>
              <rect class="switch__roll" data-roll x="0" y="0" width="104" height="8"></rect>
              <line class="switch__rollline" data-rollline x1="0" y1="0" x2="104" y2="0"></line>
            </g>
            <rect class="switch__track" x="1" y="1" width="102" height="54" rx="27"></rect>
            <g data-cord>
              <line class="switch__cord" data-cordline x1="0" y1="0" x2="0" y2="8"></line>
              <path class="switch__pull" data-pull d="M0 0 C1.9 0 3.1 1.6 3.1 3.6 C3.1 5.9 1.7 7.6 0 7.6 C-1.7 7.6 -3.1 5.9 -3.1 3.6 C-3.1 1.6 -1.9 0 0 0 Z"></path>
              <path class="switch__gleam" data-gleam d="M-1.4 2.2 C-1.9 3 -2 4 -1.7 5"></path>
            </g>
          </svg>
        </button>
      </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.

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 roller blind switch” from Animorio and fit it into my site

What it does

On a white page, between two thin rules, the ‘Night reading’ setting with the line ‘Blind up, full light’ under it; on the right the word ‘Off’ and the switch. The track is a hairline pill that works as a small window: inside there is a pale sky with a low sun, and at the top the roller with the blind wound on it; from the blind's rail hangs the pull cord, a fine line with a small dark acorn at its end, which is the knob. On a click (or a tap, Space, Enter or the right arrow) the cord is pulled down, the blind unrolls from the roller with its fine folds coming off one after the other, reaches the bottom a pixel past and settles; meanwhile the sky behind goes to night, the sun sinks below the sill and a moon comes up on the other side, and the roller gets thinner. The line becomes ‘Blind down, easy on the eyes’, ‘Off’ thins away and ‘On’ comes in narrow and light and takes weight. Another click (or the left arrow): a tug down, then the spring rolls it up in a snap and the blind bounces twice against the roller. The cord swings by itself when the rail moves, on a small damped spring. With the pointer over it the cord drops a little, inviting the pull; pressed, it gives a little more.

How it works

One state

Everything is in one object of numbers: how far down the blind is p, the tug, the lean under the mouse, the press and the word. A render function, called by the GSAP ticker on every frame, writes the cloth, the folds, the rail, the roller, the cord and the sky from it.

Folds coming off the roller

The fine folds are a single path redrawn on every frame, one line every FOLD pixels counted from the rail upwards: so as the blind comes down, new folds come off the roller one at a time, like real cloth, and the roller gets thinner by as much as has unrolled.

Down by hand, up by spring

Switched on: the tug lengthens the cord by TUG pixels, then p goes OVERSHOOT pixels past the bottom with power2.inOut in DROP seconds and comes back with sine.out. Switched off: another tug frees the catch, p goes back to zero with power3.in in SNAP seconds, which speeds up like a spring, and two ever smaller bounces against the roller.

The swinging cord

The cord's angle is a damped spring pushed by the rail's acceleration: when the blind starts or stops, the acorn swings and settles by itself. The sky goes to night blue with gsap.utils.interpolate on the gradient's two colours, the sun sinks and the moon comes up.

Accessible

It is a button with role="switch" and aria-checked, labelled by the setting's name and the word; Space and Enter turn it like a click, the right and left arrows switch it on and off, and focus shows as an orange ring. A click halfway stops the running turn and starts the new one from where the blind is.

Values to change

NameFileDefaultWhat it changes
DROPscript.js0.95How long the blind takes to unroll, in seconds.
SNAPscript.js0.36How long the spring takes to roll it up, in seconds.
OVERSHOOTscript.js1.1How far the rail goes past the bottom before it settles, in pixels.
BOUNCEscript.js1.6How high the rail bounces off the roller when it rolls up, in pixels.
SWINGscript.js1How much the cord swings when the rail moves (0 never).
FOLDscript.js5.2The gap between the fine folds of the cloth, in pixels.
TUGscript.js4How far the cord is pulled down on the tug, in pixels.

Try them in the Playground

When to use it

For an on or off setting that darkens or dims: the night reading of a books or news app, dark mode, a rest mode, a blue light filter. It also suits the settings of a smart home, for real blinds, or a hotel's site.

On phones

The same with a tap: the switch stays 104 by 56, with the cord hanging below it when the blind is down; the setting's row narrows to the screen's width and the line under the name stays on one line.

With reduced motion

No unrolling and no spring: the blind is already up or already down, the sky already day or night, the cord still; the words swap in a short fade and the line changes at once.

Pitfalls

  • The cord sits outside the window's clip, because with the blind down it hangs below the track: everything else (sky, cloth, folds, rail, roller) is inside a clipPath with the pill's round corners.
  • Folds counted from the top stayed put while the cloth came down, like lines drawn on the glass: counted from the rail upwards, they come off the roller like real cloth.
  • The theme switches already in the library use a landscape with sun and moon, a lamp, a plane: here the idea is another, the track as a window and the knob as a pull cord.

Libraries

GSAP 3.15.0, Archivo (font variabile) 5.3.0.

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

Testing

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