Vai al contenuto
animorio
EN
Tutti i componenti

Il modulo d’accesso con le viti che si stringono

Moduli, SVG, FisicaGSAPMediaPeso compresso: Demo 8,5 KBGSAP 28 KBGratisScarica lo zip

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.

Il codice

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>

Playground

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

Oppure chiedila a Claude Code

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 sito

Cosa fa

Su un fondo bianco una card bianca col contorno sottile e un’ombra morbida: in alto il nome piccolo dell’officina, Bitfix repairs, poi Sign in e Check on your repair; sotto due campi con l’etichetta, Email e Password, ognuno su una riga sottile, il pulsante dell’occhio per mostrare la password e una pillola nera Sign in. Ai quattro angoli quattro viti d’acciaio a croce, ognuna nel suo incavo: entrano cadendo e girando, e restano allentate, un poco fuori dalla card, con l’ombra lunga e le croci girate ognuna per conto suo. Scrivendo l’email si stringono le due viti in alto, una dopo l’altra, a ogni lettera un piccolo giro su una molla; con la password quelle in basso. Quando una vite arriva a filo la sua testa cede di un pixel e torna. Sign in con tutto a posto assesta la card un pixel più giù con uno scatto, ogni testa prende la pressione e una luce le attraversa una dopo l’altra; i campi salgono via e al loro posto si disegna un cerchio con la spunta vermiglia, You’re in e Your laptop is on the bench. Con una password più corta di sei caratteri le viti si allentano di nuovo, ognuna di quanto vuole, la card trema e sotto la password compare l’errore in vermiglio. Nell’anteprima una mano finta scrive da sola: l’email, una volta su due una password troppo corta, poi quella giusta.

Come funziona

Una vite in tre strati

Ogni 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.

Quanto è stretta

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.

Le lettere stringono

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.

Lo scatto e il rifiuto

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.

Accessibile

È 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.

Valori da cambiare

NomeFileDi partenzaCosa cambia
BRANDscript.jsBitfix repairsIl nome piccolo dell’officina sopra il titolo.
TITLEscript.jsSign inIl titolo della card e del pulsante.
SUBscript.jsCheck on your repairLa riga sotto il titolo.
DONE_TITLEscript.jsYou’re inIl titolo ad accesso fatto.
DONE_SUBscript.jsYour laptop is on the bench.La riga ad accesso fatto.
MIN_PASSscript.js6Quanti caratteri vuole la password.
LOOSEscript.js150Di quanti gradi è girata una vite allentata.
LIFTscript.js1.6Quanto sporge una vite allentata, in pixel.
TURNscript.js0.42I secondi di un giro di vite.
SHAKEscript.js4Quanto trema la card al rifiuto, in pixel.

Provali nel Playground

Quando usarla

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.

Sul telefono

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.

Con il movimento ridotto

Le viti passano da allentate a strette senza girare, la card non trema, i campi e la spunta compaiono in dissolvenza.

Trappole

  • Il riflesso non deve girare con la vite: se sta dentro il gruppo che ruota, la testa sembra di plastica. Va fuori, e gira solo la croce.
  • Le viti si stringono a coppie: se tutte e quattro seguissero lo stesso valore, ogni lettera farebbe girare tutto insieme e non si leggerebbe il conto.
  • La croce è simmetrica di 90 gradi: un giro di un quarto non si vede. Lo scatto finale quindi non è un giro ma la testa che cede e la luce che passa.

Librerie

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.

Collaudo

Provata il 24 ottobre 2026

  • Computer: Fatto
  • Telefono: Fatto
  • Movimento ridotto: Fatto
  • Safari su iPhone: Da fare

Licenza d’uso

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.

Tutti i componenti