
The layered button
Buttons, Hover, Physics
Free
The header of a bathhouse printed on a sheet of thermochromic liquid crystal, the film of mood rings: cold it is black, warmed it turns red, gold, green, blue and violet. The pointer is a warm fingertip that leaves trails which cool away; under the current link a heater keeps the film hot, and choosing another link it slides there like an iron, with a rainbow tail.
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 thermochromic bar</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/familjen-grotesk@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the top bar of a bathhouse's site, printed on a sheet of black thermochromic film: the pointer warms it like a
fingertip, and a heater under the current link keeps the film hot round the word -->
<header class="bar" data-bar>
<!-- the film: a heat simulation painted through the colours of the liquid crystal -->
<canvas class="bar__film" aria-hidden="true" data-film></canvas>
<canvas class="bar__film bar__film--veil" aria-hidden="true" data-veil></canvas>
<span class="bar__gloss" aria-hidden="true"></span>
<a class="brand" href="#" aria-label="Solvarm, home" data-brand>
<svg class="brand__mark" viewBox="0 0 28 28" aria-hidden="true">
<path class="brand__steam" d="M9.5 13.5c-1.6-1.6 1.6-3.2 0-4.8s1.6-3.2 0-4.8"/>
<path class="brand__steam" d="M14 13.5c-1.6-1.6 1.6-3.2 0-4.8s1.6-3.2 0-4.8"/>
<path class="brand__steam" d="M18.5 13.5c-1.6-1.6 1.6-3.2 0-4.8s1.6-3.2 0-4.8"/>
<path d="M4.5 23.5c1.2-3.6 4.6-6 9.5-6s8.3 2.4 9.5 6z"/>
</svg>
<span class="brand__name">Solvarm</span>
</a>
<nav class="bar__strip" aria-label="Main" data-strip>
<ul class="bar__list" data-list>
<li><a class="bar__link" href="#" data-link data-autoclic><span>Saunas</span></a></li>
<li><a class="bar__link" href="#" data-link data-autoclic><span>Cold baths</span></a></li>
<li><a class="bar__link" href="#" aria-current="page" data-link><span>Treatments</span></a></li>
<li><a class="bar__link" href="#" data-link data-autoclic><span>Memberships</span></a></li>
<li><a class="bar__link" href="#" data-link data-autoclic><span>Journal</span></a></li>
</ul>
</nav>
<a class="bar__cta" href="#" aria-label="Book a visit" data-cta><span>Book<span class="bar__more"> a visit</span></span></a>
</header>
</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.
30 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 thermochromic bar” from Animorio and fit it into my siteUnder the bar lies a grid of cells, one every 4 pixels, with the heat of each in a Float32Array. A hundred and eighty times a second, whatever the screen's frame rate (the time step is capped at 1/30 of a second), every cell moves towards the mean of its four neighbours (T += SPREAD · (sum of the neighbours − 4T)), loses COOL/180 of its heat and takes the fingertip's. It is the heat equation solved with finite differences; at the edges the missing neighbour is the cell itself, so no heat leaks out of the edge.
A table of 512 colours, made from the colours in :root, runs from cold to hottest: black up to 0.17, then a deep red rising out of the black, red at 0.3, gold at 0.45, green at 0.55, teal, blue, violet at 0.86 and black again past 1.1, as the real crystal does past its clearing point. The canvas is half the size of the bar: each of its pixels reads the heat between the four nearest cells, where it is warm it shifts a little with a still noise (so the edges of the colours wander) and takes its colour from the table; putImageData paints it and the page stretches it.
Under the letters of the current link sits a rounded box with a soft edge (the distance from a rounded rectangle, faded over 5 pixels) that pulls the cells towards HOT at every step. The rings round it are not drawn: spreading and cooling make them. The box is measured in the strip's coordinates (offsetLeft, which ignores the scroll), so on a phone the heat travels with the word, and it fades out near the strip's edges.
Choosing a link, GSAP takes the heater's place and width to the new one in SLIDE seconds with power3.inOut, and on the way lowers it to 55 per cent and weakens it to 80: the cells it crosses warm halfway, and a rainbow tail that cools is left behind. A press pulls the film towards the finger's heat with a gaussian bell 10 pixels wide.
Every frame the pointer's heat is laid along the way it went since the frame before, a bell every 4 pixels. The total grows with the square root of the length: a quick swipe leaves a fainter trail than a slow one, but it leaves one. Each cell takes heat in proportion to the difference between the finger and itself, as skin does: the finger never warms anything past itself, and its heat (0.72) is below the heater's, so where it rests the film reaches green, and on the current link it does not cool it. On a phone the finger comes from touchmove, which keeps coming while the strip scrolls.
Every frame the film is compared with the one before: when no cell changes by more than 0.0012 for twelve frames in a row, and nothing moves, the loop leaves GSAP's ticker and the canvas stays as it is; a gesture wakes it. A step costs 0.03 milliseconds and a paint 0.2. With less motion the settled film is worked out in one go (900 steps) and changing link is a fade over a second canvas.
| Name | File | Default | What it changes |
|---|---|---|---|
HOT | script.js | 0.9 | How hot the film is under the current link: 0.3 red, 0.45 gold, 0.55 green, 0.75 blue, 0.9 violet, past 1 dark again. |
WARM | script.js | 0.4 | How hot a link gets when pointed at or reached with Tab, and the button. |
FINGER | script.js | 20 | How much heat goes from the pointer into the film: higher, brighter trails and bigger rings. |
SPREAD | script.js | 0.07 | How fast heat spreads through the film: higher, wider and softer rings. |
COOL | script.js | 0.95 | How fast the film goes back to black: higher, shorter trails. |
SLIDE | script.js | 0.7 | The seconds the heater takes to slide to the link chosen. |
PRESS | script.js | 1 | How much heat a click or a tap leaves where it lands (0, none). |
For the header of anyone selling warmth or well-being: saunas, bathhouses, hot springs, spas, coffee roasters, ceramics studios with a kiln, makers of stoves or thermal clothing. It also suits a dark tech site (sensors, thermal cameras, hardware) that wants a bar people remember. It is a bar to show off: one per site, at the top, on a light or a dark page.
The bar is 56 pixels tall and as wide as the screen less 32; the brand keeps its mark without the name and the button just says Book (screen readers still hear Book a visit). The links scroll sideways inside the bar, which fades out on the side where more are hidden; the chosen link slides to the middle and the heat follows it. The finger warms the film while it touches and while it drags the strip, and a tap presses and chooses at once.
No fingertip, no trails and no slide: the film appears already settled round the current link. Choosing another link, the old film fades away over the new one, already warm round the word chosen; on a phone, scrolling the strip, the heat follows the word at once. Pointing at a link only lights it up, the brand's steam does not rise and presses do not shrink anything.
GSAP 3.15.0, Familjen Grotesk (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 10 KB; the libraries: GSAP 28 KB.
Tested on 5 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.