Skip to content
animorio
IT
All components

The venetian blind button

Buttons, Physics, HoverCSS only, Canvas, GSAPAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

The Sign out of an app with a small window in the round end of the pill and a venetian blind in it: slats in real 3D, a warm day behind them, and the light coming through the gaps and falling across the pill in slanting stripes, words included. Under the pointer the slats lean one after the other to aim their stripes at it. At a click the cord with its wooden acorn is pulled, the slats shut from the top in a wave with a bounce and the light goes out: Signed out. Another click opens them from the bottom and the light comes back: Good morning, Rui.

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 venetian blind button</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the venetian blind button: the Sign out of an app, with a small window in the round left end of the pill and a
         venetian blind in it. Behind the slats there is a warm day; the light comes through the gaps and falls on the
         pill in slanting stripes. Under the pointer the slats lean, one after the other, to aim their light at it. A click
         pulls the cord: the slats close one by one with a bounce and the light goes. Another click opens them again -->
    <div class="blind" data-blind>
      <button class="blind__btn" type="button" data-btn data-autoclic>
        <span class="blind__fill" aria-hidden="true" data-fill></span>
        <span class="blind__under" aria-hidden="true">
          <span class="blind__glow" data-glow></span>
          <span class="blind__window" data-window>
            <span class="blind__sky">
              <span class="blind__sun"></span>
              <svg class="blind__branch" viewBox="0 0 66 44" data-branch>
                <path d="M70 -4C58 4 49 9 40 13c-6 3-12 4-17 8" />
                <g>
                  <path d="M58 3c-5-5-11-4-13 0 4 3 9 3 13 0z" />
                  <path d="M50 8c-1-7-7-9-11-6 2 4 6 6 11 6z" />
                  <path d="M46 10c-6 0-9 5-7 9 4-1 6-5 7-9z" />
                  <path d="M37 14c-4-5-10-5-12-1 3 3 8 4 12 1z" />
                  <path d="M33 16c-5 2-6 8-3 10 3-3 4-6 3-10z" />
                  <path d="M26 20c-3-4-8-3-10 0 3 2 7 3 10 0z" />
                </g>
              </svg>
            </span>
            <span class="blind__slats" data-slats></span>
            <span class="blind__tapes"></span>
            <span class="blind__rail"></span>
          </span>
        </span>
        <span class="blind__label">
          <span class="blind__word" data-word data-wide="Sign out">Sign out</span>
        </span>
        <span class="blind__over" aria-hidden="true">
          <canvas class="blind__light" data-light></canvas>
          <canvas class="blind__cord" data-cord></canvas>
        </span>
      </button>
      <p class="sr" aria-live="polite" data-live></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.

29 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 venetian blind button” from Animorio and fit it into my site

What it does

On a white background a dark petrol green pill 60 pixels tall, with a thread of light along the top and a soft shadow under it. In the round left end there is a small window with a cream frame, round on the pill's side and square on the other: the head rail at the top, six slats of painted aluminium hanging in two ladder tapes, and behind them a warm day, bright at the upper left where the sun is and orange towards the sill, with a branch of leaves that barely moves. Next to the window hangs the cord with a wooden acorn. The daylight comes through the slats and out onto the pill in slanting tungsten stripes, which go down a little and fade towards the end, crossing the words Sign out too. On arrival the button rises gently and the slats, shut, open in a wave. With the mouse over it the slats lean one after the other, starting from the one nearest the pointer, until the stripes point at it: with the pointer level with the window they turn almost edge on and the light comes in flat and stronger; coming onto the pill a ripple runs along the blind; passing over the cord the pointer pushes it and the acorn swings. The words widen and the fill swells with a bounce. At a click the cord is pulled down and springs back swinging, the words become Signing out… and the slats shut from the top, one every tenth of a second, each one hitting the one below and bouncing; the stripes go out one at a time, the warm glow round the window fades and a shut cream window is left: Signed out, and soon after Sign in. Another click pulls the cord, the slats open from the bottom, the stripes come back on the pill: Good morning, Rui, then Sign out again. Each time the pill takes the width of its words with a small bounce. With the keyboard the focus shows a petrol green ring, the words widen and a ripple runs through the slats; Enter or Space work like the click. On a phone a finger resting near the button makes the slats lean, and the tap signs out or in.

How it works

The slats in 3D

The window has perspective: 200px and overflow: hidden; the slats are spans made by the script (SLATS, from 3 to 10), 1.17 times as tall as the pitch so shut they overlap, turned with rotateX. Each slat has a dark veil (::after) whose opacity follows the square of the sine of its angle: the more the slat turns edge on, the more its face looks away from the room and darkens. The tapes and the head rail sit in front, still. The angle is only written when it changes by a tenth of a degree.

The coupled springs

Each slat's angle is a damped spring (2.2 swings a second, damping 0.2) towards its place, plus a coupling to its neighbours, θ'' = K (target − θ) + 160 (θ neighbours − 2θ) − C θ': as with real ladder tapes, a push on one slat runs along the blind as a wave. Below zero degrees a slat hits its neighbour: it stops and takes back BOUNCE times its speed the other way, and that is the bounce of the closing. Steps of 1/240 of a second, the frame capped at 1/30; when nothing moves the loop sleeps.

The light coming through

The stripes are a canvas over the words in mix-blend-mode: screen. For each slat the clear gap is the pitch less the slat's height seen from the front (|cos θ|), and the light leaves at a slope of (90° − θ) · 0.55: an open slat lets it through almost level, a half-shut one sends it down. Each stripe is a trapezoid that widens a little and fades with a gradient, in two passes (a soft edge and a sharp core), and it starts inside the window's opening, with no hard edge at the frame. The warm glow round the window follows the mean of the gaps.

Aiming at the pointer

From the pointer's place in the pill the script works out the angle that would send the stripes from the middle of the window at it, and from that the slats' lean, at most LEAN degrees, weighted by the pointer's distance from the pill. They all want the same lean, so the stripes stay parallel and move together; but each reaches it with a delay that grows with its distance from the slat nearest the pointer (rate 16 / (1 + 1.1 · distance)), and that is the wave.

The cord

The cord is a verlet chain of six links under a bracket, with gravity and six passes of constraints; the pointer passing through pushes it aside. At a click a hand holds the acorn and pulls it down 9 pixels, lengthening the links, then lets go: the links shorten again and the acorn springs back up swinging. The acorn and its shadow turn along the last link.

The states

The button is a real button, with aria-disabled while it signs out or in and an aria-live region that says Signing out…, Signed out, Signing in… and Good morning, Rui; it is all simulated with GSAP's timing. The words leave first, blurring, and come in after; the pill takes the measured width of the new words (at the width of the wdth axis, so the hover never spills them) with back.out, and when it narrows it waits for the old ones to be gone.

Values to change

NameFileDefaultWhat it changes
SIGN_OUTscript.jsSign outThe words at rest, signed in.
SIGNING_OUTscript.jsSigning out…The words while it signs out; the aria-live region says them too.
SIGNED_OUTscript.jsSigned outThe words once signed out, before Sign in.
SIGN_INscript.jsSign inThe words at rest, signed out.
SIGNING_INscript.jsSigning in…The words while it signs in.
WELCOMEscript.jsGood morning, RuiThe greeting once signed in. The sign in is simulated: in a real site this is the name your server returns.
WAITscript.js1The seconds of the simulated request (at least as long as the slats take to shut).
SLATSscript.js6How many slats the blind has.
OPENscript.js66How far the slats turn when open, in degrees: towards 90 they are edge on and more light comes through.
LEANscript.js22How far they lean to aim the light at the pointer, in degrees. 0 they stay still.
STAGGERscript.js0.1The seconds between one slat and the next as they shut or open.
BOUNCEscript.js0.45How much a slat bounces when it hits the one below: 0 none, 0.8 a lot.
LIGHTscript.js1How bright the stripes on the pill are.

Try them in the Playground

When to use it

For the sign out or sign in of a home, interiors, hotel and holiday rental, weather or wellbeing app, where daylight belongs. It has to be wired to your real sign out: here the request is simulated.

On phones

The pill is between about 200 and 265 pixels wide and always fits at 390; if the words are too long the type gets smaller. A finger resting near the button makes the slats lean towards it, a tap pulls the cord and signs out or in. The window and the cord keep their size.

With reduced motion

The slats do not turn or follow the pointer, the cord stays still, the pill does not swell and the words do not widen (under the mouse the fill only lightens a little). At a click the slats fade out and back in already shut, the stripes fade away and the words change with a fade; another click brings them back open, with the light.

Pitfalls

  • With a different lean for each slat (each aimed its own stripe at the pointer) the stripes crossed and the light turned to a haze on the words. Now they all want the same lean, taken from the middle of the window, and reach it late one after the other: the stripes stay parallel and the wave stays.
  • With the slats almost edge on the light became a single band with thin lines: a stripe is now at most 62% of its gap, and between two stripes it is always dark.
  • The stripes in lighter mode added up where they met and burnt the words to a flat white; in screen mode and with a thinner core the light adds up without ever covering the text.
  • The lowest stripe started below the sill, with a vertical edge at the frame: now each stripe is born inside the window's opening and rises from nothing over the first eighteen pixels.
  • With the perspective seen from above the middle the top slats showed their grey bellies and the window looked dirty: now the eye is in the middle and the dark veil is lighter.
  • Narrowing the pill while the old words faded, their last letters stuck out of it for a moment: now when it narrows it waits for the old words to be gone.

Libraries

GSAP 3.15.0, Mona Sans (font variabile) 5.3.0.

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

Testing

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