
L’indicatore a lancetta
Contatori, Fisica, SVG
NuovaGratis
Nello stile minimal, su bianco: il modulo d’accesso di un’officina che ripara computer, una card sottile con email e password e il pulsante Sign in. Ai quattro angoli quattro piccole viti d’acciaio a croce stanno allentate e storte; ogni lettera scritta ne stringe una un poco, su una molla, finché con l’email giusta e la password lunga abbastanza sono tutte a filo, le croci allineate. Sign in assesta la card con uno scatto leggero, una luce passa sulle teste e arriva You’re in; una password troppo corta allenta di nuovo le viti e la card trema.
Una cartella che funziona da sola: index.html, style.css, script.js e le librerie da CDN. Nelle schede React e Vue la stessa demo è già un componente, che spegne le sue animazioni quando esce dalla pagina.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>The login form with tightening screws</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">
<!-- the sign in card of a computer repair shop, on white. Four small cross-head screws sit loose and crooked in its
corners; every character typed turns one of them a little tighter, and once the email and the password are good
all four are flush and lined up. Sign in seats the panel with a soft click; a short password loosens them again -->
<form class="panel" data-panel data-autoclic novalidate>
<span class="screw screw--tl" data-screw aria-hidden="true">
<span class="screw__shadow" data-shadow></span>
<svg class="screw__head" viewBox="0 0 20 20" data-head>
<defs>
<radialGradient id="metal0" cx="35%" cy="30%" r="75%">
<stop offset="0" stop-color="#fbfbfa"/>
<stop offset=".55" stop-color="#cfcfcb"/>
<stop offset="1" stop-color="#9c9c97"/>
</radialGradient>
<clipPath id="cap0"><circle cx="10" cy="10" r="8.4"/></clipPath>
</defs>
<circle cx="10" cy="10" r="8.4" fill="url(#metal0)"/>
<circle cx="10" cy="10" r="8.4" fill="none" stroke="#8d8d88" stroke-width=".7"/>
<circle cx="10" cy="10" r="6.9" fill="none" stroke="#ffffff" stroke-opacity=".55" stroke-width=".5"/>
<g data-slot>
<path d="M10 5.1v9.8M5.1 10h9.8" stroke="#ffffff" stroke-opacity=".8" stroke-width="2.2" stroke-linecap="round" transform="translate(.35 .45)"/>
<path d="M10 5.1v9.8M5.1 10h9.8" stroke="#55554f" stroke-width="1.9" stroke-linecap="round"/>
</g>
<g clip-path="url(#cap0)"><rect class="screw__glint" data-glint x="-14" y="-4" width="5" height="28" fill="#ffffff" opacity="0" transform="rotate(28 10 10)"/></g>
<ellipse cx="7.3" cy="6.4" rx="2.6" ry="1.5" fill="#ffffff" opacity=".7" transform="rotate(-32 7.3 6.4)"/>
</svg>
</span>
<span class="screw screw--tr" data-screw aria-hidden="true">
<span class="screw__shadow" data-shadow></span>
<svg class="screw__head" viewBox="0 0 20 20" data-head>
<defs>
<radialGradient id="metal1" cx="35%" cy="30%" r="75%">
<stop offset="0" stop-color="#fbfbfa"/>
<stop offset=".55" stop-color="#cfcfcb"/>
<stop offset="1" stop-color="#9c9c97"/>
</radialGradient>
<clipPath id="cap1"><circle cx="10" cy="10" r="8.4"/></clipPath>
</defs>
<circle cx="10" cy="10" r="8.4" fill="url(#metal1)"/>
<circle cx="10" cy="10" r="8.4" fill="none" stroke="#8d8d88" stroke-width=".7"/>
<circle cx="10" cy="10" r="6.9" fill="none" stroke="#ffffff" stroke-opacity=".55" stroke-width=".5"/>
<g data-slot>
<path d="M10 5.1v9.8M5.1 10h9.8" stroke="#ffffff" stroke-opacity=".8" stroke-width="2.2" stroke-linecap="round" transform="translate(.35 .45)"/>
<path d="M10 5.1v9.8M5.1 10h9.8" stroke="#55554f" stroke-width="1.9" stroke-linecap="round"/>
</g>
<g clip-path="url(#cap1)"><rect class="screw__glint" data-glint x="-14" y="-4" width="5" height="28" fill="#ffffff" opacity="0" transform="rotate(28 10 10)"/></g>
<ellipse cx="7.3" cy="6.4" rx="2.6" ry="1.5" fill="#ffffff" opacity=".7" transform="rotate(-32 7.3 6.4)"/>
</svg>
</span>
<span class="screw screw--bl" data-screw aria-hidden="true">
<span class="screw__shadow" data-shadow></span>
<svg class="screw__head" viewBox="0 0 20 20" data-head>
<defs>
<radialGradient id="metal2" cx="35%" cy="30%" r="75%">
<stop offset="0" stop-color="#fbfbfa"/>
<stop offset=".55" stop-color="#cfcfcb"/>
<stop offset="1" stop-color="#9c9c97"/>
</radialGradient>
<clipPath id="cap2"><circle cx="10" cy="10" r="8.4"/></clipPath>
</defs>
<circle cx="10" cy="10" r="8.4" fill="url(#metal2)"/>
<circle cx="10" cy="10" r="8.4" fill="none" stroke="#8d8d88" stroke-width=".7"/>
<circle cx="10" cy="10" r="6.9" fill="none" stroke="#ffffff" stroke-opacity=".55" stroke-width=".5"/>
<g data-slot>
<path d="M10 5.1v9.8M5.1 10h9.8" stroke="#ffffff" stroke-opacity=".8" stroke-width="2.2" stroke-linecap="round" transform="translate(.35 .45)"/>
<path d="M10 5.1v9.8M5.1 10h9.8" stroke="#55554f" stroke-width="1.9" stroke-linecap="round"/>
</g>
<g clip-path="url(#cap2)"><rect class="screw__glint" data-glint x="-14" y="-4" width="5" height="28" fill="#ffffff" opacity="0" transform="rotate(28 10 10)"/></g>
<ellipse cx="7.3" cy="6.4" rx="2.6" ry="1.5" fill="#ffffff" opacity=".7" transform="rotate(-32 7.3 6.4)"/>
</svg>
</span>
<span class="screw screw--br" data-screw aria-hidden="true">
<span class="screw__shadow" data-shadow></span>
<svg class="screw__head" viewBox="0 0 20 20" data-head>
<defs>
<radialGradient id="metal3" cx="35%" cy="30%" r="75%">
<stop offset="0" stop-color="#fbfbfa"/>
<stop offset=".55" stop-color="#cfcfcb"/>
<stop offset="1" stop-color="#9c9c97"/>
</radialGradient>
<clipPath id="cap3"><circle cx="10" cy="10" r="8.4"/></clipPath>
</defs>
<circle cx="10" cy="10" r="8.4" fill="url(#metal3)"/>
<circle cx="10" cy="10" r="8.4" fill="none" stroke="#8d8d88" stroke-width=".7"/>
<circle cx="10" cy="10" r="6.9" fill="none" stroke="#ffffff" stroke-opacity=".55" stroke-width=".5"/>
<g data-slot>
<path d="M10 5.1v9.8M5.1 10h9.8" stroke="#ffffff" stroke-opacity=".8" stroke-width="2.2" stroke-linecap="round" transform="translate(.35 .45)"/>
<path d="M10 5.1v9.8M5.1 10h9.8" stroke="#55554f" stroke-width="1.9" stroke-linecap="round"/>
</g>
<g clip-path="url(#cap3)"><rect class="screw__glint" data-glint x="-14" y="-4" width="5" height="28" fill="#ffffff" opacity="0" transform="rotate(28 10 10)"/></g>
<ellipse cx="7.3" cy="6.4" rx="2.6" ry="1.5" fill="#ffffff" opacity=".7" transform="rotate(-32 7.3 6.4)"/>
</svg>
</span>
<div class="panel__body" data-body>
<p class="brand" data-part><span data-brand>Bitfix repairs</span></p>
<h1 class="title" data-part data-title>Sign in</h1>
<p class="sub" data-part data-sub>Check on your repair</p>
<label class="field" data-field="email" data-part>
<span class="field__label">Email</span>
<span class="field__box">
<input class="field__input" type="email" name="email" autocomplete="email" spellcheck="false" data-input="email" placeholder="you@example.com">
</span>
<span class="field__line" aria-hidden="true"><span data-line></span></span>
</label>
<label class="field" data-field="pass" data-part>
<span class="field__label">Password</span>
<span class="field__box">
<input class="field__input" type="password" name="password" autocomplete="current-password" data-input="pass" placeholder="At least 6 characters">
<button class="eye" type="button" data-eye aria-label="Show password" aria-pressed="false">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path class="eye__lid" data-lid d="M2.8 12c2.4-3.9 5.5-5.8 9.2-5.8s6.8 1.9 9.2 5.8c-2.4 3.9-5.5 5.8-9.2 5.8S5.2 15.9 2.8 12Z"/>
<circle class="eye__ball" data-ball cx="12" cy="12" r="2.6"/>
<path class="eye__slash" data-slash d="M4.5 19.5 19.5 4.5"/>
</svg>
</button>
</span>
<span class="field__line" aria-hidden="true"><span data-line></span></span>
<span class="field__msg" data-msg aria-live="polite"></span>
</label>
<button class="go" type="submit" data-go>
<span class="go__bg" aria-hidden="true"></span>
<span class="go__text" data-go-text>Sign in</span>
</button>
</div>
<div class="done" data-done aria-hidden="true">
<svg class="done__tick" viewBox="0 0 40 40" aria-hidden="true">
<circle cx="20" cy="20" r="17" data-ring/>
<path d="M12.5 20.5 18 26l10-11.5" data-tick/>
</svg>
<p class="done__title" data-done-title>You’re in</p>
<p class="done__sub" data-done-sub>Your laptop is on the bench.</p>
</div>
<p class="sr" aria-live="polite" data-live></p>
</form>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body>
</html>Cambia valori, colori e testi con le manopole e guarda l’animazione rispondere dal vivo. Oppure scrivi tu nel codice. Poi scarica la cartella con le tue modifiche.
24 valori da cambiare
Col connettore di Animorio, Claude Code la prende da qui con tutta la sua guida e la adatta al tuo progetto. Incolla questa richiesta:
Prendi «Il modulo d’accesso con le viti che si stringono» da Animorio e adattala al mio sitoOgni vite è un disco sfocato per l’ombra, la testa in SVG con un gradiente radiale d’acciaio e, dentro la testa, il gruppo della croce, l’unico che gira. Il riflesso sulla testa sta fuori da quel gruppo: la luce resta ferma mentre la vite gira sotto di lei, ed è quello che la fa sembrare di metallo.
Ogni vite ha un numero p da 0 a 1. A 0 la croce è girata di LOOSE gradi in senso antiorario (ognuna per la sua parte), la testa è più grande dell’8% e spostata verso la luce, l’ombra lontana; a 1 tutto torna a filo. GSAP porta p al valore nuovo con back.out(2.2), così ogni giro supera il segno di poco e si assesta.
L’email vale da 0 a 0,8 contando le lettere fino a sedici, e 1 solo quando è un indirizzo vero; la password vale le lettere diviso MIN_PASS. Ogni coppia di viti si divide il valore a metà: la prima si stringe fino a 0,5, la seconda da 0,5 a 1, così si stringono una dopo l’altra.
Sign in giusto: la card scende di 1,2 pixel in 7 centesimi e torna con back.out(3), ogni testa passa da 0,92 alla sua misura e una striscia bianca, ritagliata nel cerchio della testa, la attraversa. Sbagliato: ogni p scende di circa 0,4 con power3.out e la card fa un tremolio che si spegne con elastic.out.
È un form vero con due input con l’etichetta, il pulsante dell’occhio con aria-pressed e una riga aria-live che dice l’errore o l’accesso. La mano finta dell’anteprima si ferma appena si tocca o si scrive davvero. Col movimento ridotto le viti cambiano posizione senza giri e la spunta compare in dissolvenza.
| Nome | File | Di partenza | Cosa cambia |
|---|---|---|---|
BRAND | script.js | Bitfix repairs | Il nome piccolo dell’officina sopra il titolo. |
TITLE | script.js | Sign in | Il titolo della card e del pulsante. |
SUB | script.js | Check on your repair | La riga sotto il titolo. |
DONE_TITLE | script.js | You’re in | Il titolo ad accesso fatto. |
DONE_SUB | script.js | Your laptop is on the bench. | La riga ad accesso fatto. |
MIN_PASS | script.js | 6 | Quanti caratteri vuole la password. |
LOOSE | script.js | 150 | Di quanti gradi è girata una vite allentata. |
LIFT | script.js | 1.6 | Quanto sporge una vite allentata, in pixel. |
TURN | script.js | 0.42 | I secondi di un giro di vite. |
SHAKE | script.js | 4 | Quanto trema la card al rifiuto, in pixel. |
Per l’accesso di un sito di assistenza, di un’officina, di un servizio di riparazioni o di qualunque area clienti dove si vuole dire «tutto a posto» con un gesto artigiano.
La card si stringe fino a 16 pixel dai bordi e le distanze verticali si misurano anche sull’altezza dello schermo, così sta intera anche in una casella bassa. Il tocco scrive e preme come il mouse; il giro della vite sotto il puntatore c’è solo col mouse.
Le viti passano da allentate a strette senza girare, la card non trema, i campi e la spunta compaiono in dissolvenza.
GSAP 3.15.0, Archivo (font variabile) 5.3.0.
Compressi con gzip, i tre file della demo pesano 8,5 KB; le librerie: GSAP 28 KB.
Provata il 24 ottobre 2026
Puoi usarla nei tuoi progetti e in quelli dei tuoi clienti. Non puoi rivenderla né ridistribuirla così com’è. GSAP resta sotto la sua licenza Standard. Licenza completa.
Con l’email e la password del tuo account.
oppure con l’email
Hai fatto l’accesso come .
Con l’account scarichi lo zip di ogni animazione e di ogni pacchetto, gratis.