Skip to content
animorio
IT
All components

The door button

Buttons, Physics, 3DCSS only, Canvas, GSAPAdvancedCompressed weight: Demo 13 KBGSAP 28 KBFreeDownload the zip

The Log out button of an account, with a real door in the round end of the pill: a cream frame with its sill, a painted door with two sunken panels and a brass lever handle, and behind it a room with its bulb on, whose light shows as a thin line under the door. The door is CSS 3D and turns on its hinges: under the pointer the handle goes down and the door opens a crack, and a blade of warm light spreads over the pill, as wide as the gap. At a click the door swings wide, a small figure walks out, the door slams and bounces on its stop, and the light inside goes out: Signed out. The next click turns the light back on, the figure walks home and the door shuts behind it.

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 door 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 door button: the Log out button of an account, with a real door in the round right end of the pill, built
         in CSS 3D: the frame, the door with its lever handle, and behind it a room with its light on. Under the
         pointer the handle goes down and the door opens a few degrees: a blade of warm light spreads over the pill.
         A click opens it wide, a small figure walks out, the door shuts and bounces on its stop and the light goes
         out; the next click signs back in, the light comes on and the figure walks home -->
    <div class="exit" data-exit>
      <button class="exit__btn" type="button" data-btn data-autoclic>
        <span class="exit__fill" aria-hidden="true"></span>
        <canvas class="exit__light" aria-hidden="true" data-light></canvas>
        <span class="exit__faces">
          <span class="exit__face" data-face="in">
            <span class="exit__lead" data-lead-in>Signed in as Ada</span>
            <span class="exit__word" data-wide="Log out" data-word-in>Log out</span>
          </span>
          <span class="exit__face" data-face="out" aria-hidden="true">
            <span class="exit__lead" data-lead-out>Signed out</span>
            <span class="exit__word" data-wide="Sign in" data-word-out>Sign in</span>
          </span>
          <span class="exit__face exit__face--busy" data-face="busy" aria-hidden="true">
            <span class="exit__shine" data-busy-text>Signing out…</span>
          </span>
          <span class="exit__face exit__face--big" data-face="big" aria-hidden="true">
            <span class="exit__big" data-big-text>Signed out</span>
          </span>
        </span>
        <span class="exit__doorway" aria-hidden="true" data-doorway>
          <span class="exit__room" data-room>
            <span class="exit__wall"></span>
            <span class="exit__cord"></span>
            <span class="exit__bulb" data-bulb></span>
          </span>
          <span class="exit__persp" data-persp>
            <span class="exit__door" data-door>
              <span class="exit__panel exit__panel--top"></span>
              <span class="exit__panel exit__panel--low"></span>
              <span class="exit__shade" data-shade></span>
              <span class="exit__handle">
                <span class="exit__rose"></span>
                <span class="exit__lever" data-lever></span>
              </span>
            </span>
          </span>
          <span class="exit__frame"></span>
          <span class="exit__sill"></span>
          <span class="exit__under" data-under></span>
        </span>
        <svg class="exit__walker" aria-hidden="true" data-walker>
          <defs>
            <clipPath id="exit-clip"><rect data-clip x="-60" y="-20" width="400" height="100"/></clipPath>
          </defs>
          <g clip-path="url(#exit-clip)">
            <g data-figure opacity="0">
              <path class="exit__limb exit__limb--far" data-limb="farLeg"/>
              <path class="exit__limb exit__limb--far exit__limb--arm" data-limb="farArm"/>
              <path class="exit__limb exit__limb--body" data-limb="body"/>
              <circle class="exit__head" r="3.1" data-head/>
              <path class="exit__limb" data-limb="nearLeg"/>
              <path class="exit__limb exit__limb--arm" data-limb="nearArm"/>
            </g>
          </g>
        </svg>
      </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.

28 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 door button” from Animorio and fit it into my site

What it does

On a black background a petrol blue pill, 60 pixels tall, with a thread of light along its top. On the left, in Mona Sans, a small grey green line, Signed in as Ada, and under it Log out in ivory; in the round right end a door seen from the front: the cream jambs and lintel, darker on the side in shadow, the pale sill standing out a little, and the rust door with two sunken panels and a brass lever handle. Under the door runs a thread of warm light, spilling a little onto the pill in front of the sill: inside the light is on. On arrival the button rises gently and the bulb behind the door comes on with a couple of flickers. With the mouse over it the words widen, the fill swells with a bounce, the handle goes down with a click and the door opens a few degrees: through the gap the lit room shows, and a thin blade of light comes out from the sill and runs over the pill as far as under the words. Taking the mouse away the door closes and knocks lightly on its stop. At a click the words become Signing out…, with a sheen running through them; the handle goes down, the door swings wide into the room and the blade becomes a fan of light: the room shows, its warm wall and the bulb hanging on its cord. A small dark figure comes out from behind the door and walks to the left in the light, step after step, its legs and arms swinging; as it walks the door swings shut, slams on its stop and bounces twice, the wall gives a small jolt, and the figure walks out of the light and fades. The thread of light under the door stays a moment, then the bulb goes out with a last orange glow. Signed out arrives, large, then shrinks and rises into the small line, while Sign in comes up under it. Another click: Signing in…, the door opens on the dark room, the bulb comes on with its flickers, the figure comes out of the dark and walks into the room, the door shuts behind it and bounces; Signed in arrives and the small line is Signed in as Ada again, over Log out. With the keyboard the focus shows an ivory ring and the door opens a crack as under the mouse; Enter or Space sign out and in. On a phone a resting finger opens the door a crack and the tap signs out.

How it works

The door in CSS 3D

The door is a span inside a box with perspective: 90px, as big as the opening (24 by 37 pixels), with transform-origin on its right edge: rotateY with a negative angle turns it on its hinges into the room. The panels are inset box-shadows, and a black veil with an opacity of 0.62 · sin(angle) darkens it as it turns away. Behind it is the room: a dark ground, a warm wall with the light's opacity, the cord and the bulb, whose light and halo are color-mix and calc on the --lit variable the script writes. The frame, the sill and the line of light under the door stand in front, so the open door passes behind them.

The gap, worked out

The script knows where the door's free edge falls on the screen: turning by an angle a, the edge goes back by 24 · sin a and towards the hinges by 24 · (1 − cos a), and the perspective draws it towards the middle by 90 / (90 + 24 · sin a), exactly as the browser does. The width of the gap sets everything else: the line of light under the door goes out as soon as the gap opens, the blade takes its spread, and the figure is cut (an SVG clipPath rewritten every frame) right of the free edge, so it comes and goes from behind the door.

The blade of light

A canvas as big as the pill, in mix-blend-mode: screen, draws the light only when the door or the bulb changes. The blade is a fan that starts at the sill and runs to the left: its upper edge rises by 0.02 + 0.13 · √s radians and its lower edge falls by 0.05 + 0.5 · √s, where s is the gap divided by 16 pixels. The root means a door just ajar already throws a thin, bright blade. The soft edges are eight fans one inside the other, each at 0.16 opacity, with a gradient that starts almost white and fades to the left; round the opening a radial glow lights the wall. The colour sinks towards orange when the light is weak, like a filament warming up or cooling down.

The door that slams

The door's angle is a damped spring towards a target: 84 degrees to open (slow, with hardly a bounce), 18 degrees ajar under the mouse, and −6 degrees when shut, a little past the stop, so the spring presses it against it. At zero degrees there is the stop, a hard limit: the door reaches it with its speed, stops and comes back with BOUNCE times that speed, two or three times, less each time, until it rests. When the knock is hard the wall and the fill give a small elastic jolt. Steps of 1/240 of a second, the frame capped at 1/30; once the door is still, the loop stops working and drawing.

The walking figure

The figure is an SVG pictogram of six round-capped strokes (body, head, two legs and two arms) redrawn every frame. The phase of the walk comes from the distance walked (2π every 24 pixels), so the feet do not slide. The thigh swings by 0.44 · sin(phase), the knee bends while the leg goes forward (cos(phase) positive), the arms swing against the legs with the elbow bending forward. The hip rides just high enough for the lower foot to stand on the sill: the bob of the walk comes by itself. The far leg and arm are a little lighter. GSAP carries the figure from behind the door to the end of the landing at WALK speed, and fades it as it leaves the light.

The states and the words

Signing out: Signing out…, the door opens, the figure walks out, the door slams when it is halfway along the blade, the light goes out, Signed out. Signing in: Signing in…, the door opens on the dark, the bulb comes on (a timeline of flickers, then power2.out to 1), the figure walks in, the door shuts, Signed in. The finished word, large, is measured and carried with its own scale exactly onto the small line, then the real line takes its place and the next action rises under it. The button is a real button, aria-disabled while it works; an aria-live region says Signing out…, Signed out, Signing in…, Signed in as Ada. A round takes about 4.2 seconds, more than the 2.8 between two clicks of the preview: one click falls in the wait and every state stays in view.

Values to change

NameFileDefaultWhat it changes
PERSONscript.jsAdaWho is signed in: the small line says Signed in as and this name.
LOG_OUTscript.jsLog outThe words while signed in, the action to leave.
SIGN_INscript.jsSign inThe words while signed out, the action to come back.
SIGNING_OUTscript.jsSigning out…The words while it signs out; also what the aria-live region says.
SIGNING_INscript.jsSigning in…The words while it signs in.
SIGNED_OUTscript.jsSigned outThe word once done, which then becomes the small line over Sign in.
SIGNED_INscript.jsSigned inThe word once done, signing in; the small line adds as and the name.
AJARscript.js18How many degrees the door opens under the pointer: the higher, the wider the blade of light.
OPENscript.js84How many degrees it opens to let the figure through.
WALKscript.js44How fast the figure walks, in pixels a second: its legs always keep step with the ground.
BOUNCEscript.js0.45How much speed the door keeps when it hits its stop: 0 it stops dead, 0.8 it rattles a long time.
FLICKERscript.jstrueThe bulb flickers before it comes on; off, it fades on.

Try them in the Playground

When to use it

For the button that signs out and back in: an app's account menu, a dashboard's bar, a profile page, a members' area, a holiday home or hotel site, an architecture or interiors studio. The door says what happens better than an arrow in a box. It has to be wired to your site's real sign out and sign in (the call to your server or to the sign in service): here it is all simulated.

On phones

The pill is about 245 pixels wide: at 390 pixels it fits with room to spare, and with longer words the type gets smaller. The door and the figure keep their size. A finger resting on the button opens the door a crack as the mouse does, and the tap signs out or in.

With reduced motion

The door stays shut and the figure does not walk; the pill does not swell and the words do not widen (under the mouse the fill only lightens a little). Signing out, the light under the door fades out and the words fade from one to the next; signing in, the light fades back on the same way, with no flicker.

Pitfalls

  • With the perspective set to the left of the door (the eye at the middle of the pill) the door open at 84 degrees, turning into the room, projected over almost the whole opening: the gap stayed two pixels wide and the figure could not pass. The perspective now has its origin at the middle of the opening, and the script uses the browser's own formula to know where the free edge falls.
  • On opening the page there was a dark dot at the upper left of the pill: the figure's head, a circle with r set in the HTML, sat at x 0 and y 0 before the figure set off. The figure's group now starts with opacity 0.
  • With the target when shut far past the stop the door reached it so fast that it bounced back some thirty degrees, as if slammed by a draught. Simulated on its own, the choice is −6 degrees with a damping of 0.35: the first bounce is about nine degrees, the second one.
  • The blade with a sharp edge, as wide as the full fan, cut the pill diagonally like a searchlight. Now its spread grows with the root of the gap, it is narrower at the top, and its edges are eight overlapping fans at a low opacity.
  • The far leg and arm, lightened towards the colour of the light, looked outside the blade like a wooden leg apart from the body. Now they lighten towards the colour of the wall.
  • A round took almost 5.5 seconds and in the preview, which clicks every 2.8, the finished state was in view only a moment before the next click. The figure walks faster and the finished word waits less: a round takes about 4.2 seconds.

Libraries

GSAP 3.15.0, Mona Sans (font variabile) 5.3.0.

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