
The layered button
Buttons, Hover, Physics
Free
The profile card of a hair stylist, with her portrait drawn by the script and her long copper hair made of real locks: each lock is a chain of points that keeps its waves on springs, has a weight and never stretches. The pointer runs through the hair like fingers, the locks follow it and swing back into place, and she looks at it. Follow makes her toss her head: the hair fans out and falls back, bouncing, while she smiles with her eyes closed.
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 hair stylist's card</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bodoni-moda@5.3.0/opsz.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
<script>document.documentElement.classList.add('js');</script>
</head>
<body>
<main class="stage">
<!-- the profile card of a hair stylist. Her portrait is drawn by the script, and her long copper hair is made of
locks that move like hair: the pointer runs through them as fingers would, a tap ruffles them, and when she
is followed she tosses her head and the hair flies out and falls back into its waves -->
<article class="profile" aria-labelledby="profile-name" data-profile data-autoclic>
<div class="profile__portrait" data-portrait-box>
<canvas class="profile__canvas" role="img" aria-label="Portrait of Lucía Ferrer, smiling, with long wavy copper hair" data-portrait></canvas>
</div>
<div class="profile__body">
<h2 class="profile__name" id="profile-name" data-name>Lucía Ferrer</h2>
<p class="profile__role">Hair stylist and colourist in Valencia</p>
<dl class="facts">
<div class="facts__item">
<dt>Experience</dt>
<dd>14 years</dd>
</div>
<div class="facts__item">
<dt>286 reviews</dt>
<dd>
4.9
<svg class="facts__star" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3.2l2.6 5.5 6 .8-4.4 4.2 1.1 6-5.3-2.9-5.3 2.9 1.1-6-4.4-4.2 6-.8z"/></svg>
</dd>
</div>
<div class="facts__item">
<dt>Cut and finish</dt>
<dd>€48</dd>
</div>
</dl>
<div class="actions">
<button class="act act--follow" type="button" data-follow>
<span class="act__icon" aria-hidden="true">
<svg class="act__glyph" viewBox="0 0 24 24" data-glyph="plus"><path d="M12 5.8v12.4M5.8 12h12.4"/></svg>
<svg class="act__glyph" viewBox="0 0 24 24" data-glyph="check"><path d="M5.6 12.6l4.1 4.1 8.7-9.2"/></svg>
</span>
<span class="act__label" data-label>Follow</span>
</button>
<button class="act act--message" type="button" data-message>
<span class="act__icon" aria-hidden="true">
<svg class="act__glyph" viewBox="0 0 24 24" data-glyph="bubble"><path d="M5 6.8A2.3 2.3 0 0 1 7.3 4.5h9.4A2.3 2.3 0 0 1 19 6.8v7.1a2.3 2.3 0 0 1-2.3 2.3h-5.2L7.4 19.4v-3.2h-.1A2.3 2.3 0 0 1 5 13.9z"/></svg>
<svg class="act__glyph" viewBox="0 0 24 24" data-glyph="check"><path d="M5.6 12.6l4.1 4.1 8.7-9.2"/></svg>
</span>
<span class="act__label" data-label>Message</span>
</button>
</div>
<p class="sr" aria-live="polite" data-live></p>
</div>
</article>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body>
</html>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.
39 values to change
With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:
Take “The hair stylist's card” from Animorio and fit it into my siteEverything is drawn on a board of 324 by 300 points brought to the canvas size, with the figure a little enlarged from its foot. The head has a pose, an angle round the base of the neck and a shift: face, eyes, mouth, ear and the crown of the hair are drawn inside that transform, and the neck bends between the still shoulders and the head. The drawing is redone only when something moves; when the hair is still, the loop draws nothing.
The hair is 35 locks in five groups: behind the head on the left and on the right, leaving along the curve of the skull and then falling; on the right short ones combed back and tucked behind the ear; in front on the left, over the shoulder, and those that leave the parting and fall past the cheek. Each lock is born from a starting angle that turns downwards, with a sine wave along the way, in 12 points. It is drawn as a ribbon: a Catmull-Rom curve through the points, thin at the root, full a third of the way and pointed at the end, with two dark strands inside and the shine on the side of the light.
Each point is a Verlet point, in steps of 1/240 of a second with the frame step capped at 1/30: it keeps its speed (which dies down in SWING seconds) and the weight WEIGHT. Then the lock looks for its shape: the rest shape, turned with the head, is turned again by 75 per cent of the angle that best lays it over the lock as it is now, and every point moves towards it with the strength SET, firmer at the root and softer at the tip. So the lock swings as a whole and keeps its waves without tangling. Last, each point goes back to one segment from the one before (the lock never stretches) and the ones in front stay out of an oval on the face.
The pointer is brought onto the board and, at every step, its move is given to the points within HAND pixels of it, in full close by and less and less further out: the locks are dragged and seven tenths of that push stay in their speed, so after the hand has passed they go on a little and then come back. Lifted, hair is heavy and slips through the fingers (an upward push counts less than half). A click or a tap pushes the hair away from the point; with a finger resting, the hand follows the finger.
Follow starts a GSAP timeline on the head’s pose (TOSS degrees to the side, a little hop, the way back) and, at the top of the gesture, gives every lock a turn round its own root, outwards, plus a little lift: a turn is the one push the length constraint does not eat, and the hair fans out and falls back. The eyes close into half moons, the smile opens and stays open while she is followed. The hoop in her ear is a pendulum that feels the lobe’s change of speed.
The irises move towards the pointer by GAZE pixels on a spring, the features of the face a little less, and the head leans a few degrees the same way; once there, the spring stops altogether. With the keys she looks at the button that has the focus. A blink comes at intervals of 2.6 to 5.4 seconds, with a GSAP delayedCall. When for twelve frames nothing moves by a hundredth of a pixel, the physics goes to sleep.
| Name | File | Default | What it changes |
|---|---|---|---|
HAND | script.js | [50, 0.9] | The hand in her hair: how far it reaches, in pixels, and how much of its move the hair takes (0 to 1). |
SET | script.js | 0.12 | How firmly the locks keep their waves: low is loose and heavy, high is set with spray. |
WEIGHT | script.js | 220 | How heavy the hair is: how fast it falls, in pixels a second squared. |
SWING | script.js | 0.17 | How long the hair keeps swinging after a push, in seconds. |
TOSS | script.js | 9 | How far she tosses her head when she is followed, in degrees. |
GAZE | script.js | 2.4 | How far her eyes turn towards the pointer, in pixels. |
WORDS | script.js | ['Following', 'Sent'] | The two pills’ words once she is followed, and once the message has gone. |
For the profile of people who work with their hands and their looks in a booking app: a hair stylist, a barber, a make-up artist, a dance teacher; for a salon’s page, an artist’s card in a marketplace, a team page where everyone has a drawn portrait.
The card is 340 pixels wide, or the screen minus 32, and the portrait is drawn again at the real size. A finger resting on the portrait combs the hair and she looks at it (the portrait has touch-action: none, so over it the finger does not scroll the page); a tap ruffles it. The pills are 50 pixels tall and the toss is the same.
The hair stays still in its waves, the eyes look ahead and she does not blink; the card appears without rising. Follow does not toss her head: the wider smile is simply there and the pill changes in a fade; Sent arrives in a fade too. The pills only change colour under the pointer, without swelling.
GSAP 3.15.0, Bodoni Moda (font variabile) 5.3.0, Mona Sans (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 16 KB; the libraries: GSAP 28 KB.
Tested on 7 October 2026
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.
With your account’s email and password.
or with your email
You’re signed in as .
With your account, you can download the zip of every animation and every pack, for free.