Vai al contenuto
animorio
EN
Tutti i componenti

Il menu del tesserino

Menu, Fisica, 3D, TransizioniGSAP, Solo CSSImpegnativaPeso compresso: Demo 19 KBGSAP 28 KBGratisScarica lo zip

Il menu dell'account di un'app per i progetti come un tesserino su un avvolgibile: l'avatar è la faccia della bobina, e premuto lascia uscire il tesserino sul suo cordino, che lo prende con un piccolo rimbalzo mentre lui scende dondolando dal punto in cui era inclinato all'indietro. Il tesserino è il menu; Switch workspace lo fa girare sul moschettone e le aree di lavoro sono sul retro, Sign out lo gira per chiedere conferma e poi la clip si apre e il tesserino cade, mentre il cordino rientra di scatto.

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 ID badge menu</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/host-grotesk@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <!-- the portrait, drawn once and used on the reel and on the badge -->
  <svg class="defs" aria-hidden="true">
    <symbol id="portrait" viewBox="0 0 64 64">
      <rect width="64" height="64" fill="#cfe3db"/>
      <path d="M14 46c-2-6-2-13 0-19 3-9 10-14 18-14s15 5 18 14c2 6 2 13 0 19-3 2-7 3-10 3H24c-3 0-7-1-10-3z" fill="#2a1c16"/>
      <path d="M27.5 40h9v11h-9z" fill="#a96d4d"/>
      <path d="M9 64c1.5-9 9-14.5 23-14.5S53.5 55 55 64z" fill="#e7a43c"/>
      <path d="M25.6 50.3c1.8 3.5 4 5.1 6.4 5.1s4.6-1.6 6.4-5.1" fill="none" stroke="#c98425" stroke-width="1.6" stroke-linecap="round"/>
      <ellipse cx="32" cy="30.5" rx="10.6" ry="12.6" fill="#c48a64"/>
      <path d="M21.3 30c-.6-9.6 4.4-16 10.7-16 6.6 0 11.4 5.6 10.7 15.2-3.4-1.6-6.6-4.6-8.4-8.6-2.2 4.6-7.4 8.2-13 9.4z" fill="#2a1c16"/>
      <ellipse cx="25.4" cy="36.4" rx="2.2" ry="1.4" fill="#e0866b" opacity="0.4"/>
      <ellipse cx="38.6" cy="36.4" rx="2.2" ry="1.4" fill="#e0866b" opacity="0.4"/>
      <g fill="none" stroke="#1c1715" stroke-width="1.3" stroke-linecap="round">
        <circle cx="27.6" cy="32" r="3.7"/>
        <circle cx="36.4" cy="32" r="3.7"/>
        <path d="M31.3 31.5q.7-.7 1.4 0"/>
        <path d="M26.3 32.6q1.3-1.1 2.6 0M35.1 32.6q1.3-1.1 2.6 0"/>
      </g>
      <path d="M29.6 38.2q2.4 1.8 4.8 0" fill="none" stroke="#7a3d2a" stroke-width="1.3" stroke-linecap="round"/>
    </symbol>
    <symbol id="nobody" viewBox="0 0 64 64">
      <rect width="64" height="64" fill="#ececf0"/>
      <circle cx="32" cy="27" r="8.5" fill="none" stroke="#a3a5ae" stroke-width="2.6"/>
      <path d="M15.5 52c2.6-8 9-12 16.5-12s13.9 4 16.5 12" fill="none" stroke="#a3a5ae" stroke-width="2.6" stroke-linecap="round"/>
    </symbol>
  </svg>

  <main class="stage">
    <!-- the account menu of a design tool, as an ID badge on a retractable reel. The reel is the button, with the
         person's portrait on its face; pressed, the badge comes out on its cord and swings into place, and the badge is
         the menu. Switch workspace and Sign out turn the badge over on its swivel: the second level is on its back -->
    <div class="account" data-account>
      <!-- the cord and its split ring, drawn by the script -->
      <svg class="cord" aria-hidden="true" data-cord>
        <line class="cord__base" data-cord-line/>
        <line class="cord__braid" data-cord-line/>
        <g class="ring" data-ring>
          <circle r="4.4"/>
          <path class="ring__glint" d="M-3.2-1.9a3.8 3.8 0 0 1 3.4-2.3"/>
        </g>
      </svg>

      <!-- the badge's shadow on the wall behind it -->
      <span class="shade" aria-hidden="true" data-shade><span class="shade__card"></span></span>

      <div class="badge" data-badge>
        <div class="badge__card" data-card inert>
          <!-- the front: the workspace's band, the person, the menu -->
          <section class="face face--front" data-face="front">
            <div class="band" data-band>
              <span class="slot" aria-hidden="true"></span>
              <span class="band__logo" aria-hidden="true" data-band-logo><svg viewBox="0 0 24 24"><path d="M3.5 10c2.1-2.2 4.3-2.2 6.4 0s4.3 2.2 6.4 0 2.6-1.4 4.2-1.4M3.5 15.5c2.1-2.2 4.3-2.2 6.4 0s4.3 2.2 6.4 0 2.6-1.4 4.2-1.4"/></svg></span>
              <span class="band__name" data-band-name>Tidewell Studio</span>
              <span class="band__role" data-band-role>Admin</span>
            </div>
            <div class="who">
              <svg class="who__photo" aria-hidden="true"><use href="#portrait"/></svg>
              <p class="who__name" id="badge-name">Ines Marlow</p>
              <p class="who__mail" id="badge-mail">ines@tidewell.studio</p>
            </div>
            <div class="menu" role="menu" id="badge-menu" aria-labelledby="badge-name badge-mail" tabindex="-1" data-menu="main">
              <span class="menu__hl" aria-hidden="true" data-hl></span>
              <a class="row" role="menuitem" href="#" tabindex="-1" data-item="profile">
                <svg class="row__icon" viewBox="0 0 24 24" aria-hidden="true"><g data-nod><circle cx="12" cy="8.4" r="3.6"/></g><path d="M5 19.6c1.2-3.6 3.9-5.5 7-5.5s5.8 1.9 7 5.5"/></svg>
                <span class="row__label" data-label>Profile</span>
              </a>
              <a class="row" role="menuitem" href="#" tabindex="-1" data-item="settings">
                <svg class="row__icon" viewBox="0 0 24 24" aria-hidden="true"><g data-spin><path d="M10.46 5.58L10.88 3.67L13.12 3.67L13.54 5.58A6.6 6.6 0 0 1 15.45 6.37L17.1 5.32L18.68 6.9L17.63 8.55A6.6 6.6 0 0 1 18.42 10.46L20.33 10.88L20.33 13.12L18.42 13.54A6.6 6.6 0 0 1 17.63 15.45L18.68 17.1L17.1 18.68L15.45 17.63A6.6 6.6 0 0 1 13.54 18.42L13.12 20.33L10.88 20.33L10.46 18.42A6.6 6.6 0 0 1 8.55 17.63L6.9 18.68L5.32 17.1L6.37 15.45A6.6 6.6 0 0 1 5.58 13.54L3.67 13.12L3.67 10.88L5.58 10.46A6.6 6.6 0 0 1 6.37 8.55L5.32 6.9L6.9 5.32L8.55 6.37A6.6 6.6 0 0 1 10.46 5.58Z"/><circle cx="12" cy="12" r="2.7"/></g></svg>
                <span class="row__label" data-label>Settings</span>
                <span class="row__meta" data-keys></span>
              </a>
              <a class="row" role="menuitem" href="#" tabindex="-1" data-item="billing">
                <svg class="row__icon" viewBox="0 0 24 24" aria-hidden="true"><g data-swipe><rect x="3.5" y="6" width="17" height="12" rx="2.2"/><path d="M3.5 10h17M7 14.5h3.2"/></g></svg>
                <span class="row__label" data-label>Billing</span>
                <span class="row__meta" data-band-plan>Team plan</span>
              </a>
              <button class="row" type="button" role="menuitem" tabindex="-1" aria-haspopup="menu" aria-expanded="false" aria-controls="badge-workspaces" data-item="workspace">
                <svg class="row__icon" viewBox="0 0 24 24" aria-hidden="true"><rect data-swap-a x="3.8" y="3.8" width="9.4" height="9.4" rx="2.2"/><rect data-swap-b x="10.8" y="10.8" width="9.4" height="9.4" rx="2.2"/></svg>
                <span class="row__label" data-label>Switch workspace</span>
                <svg class="row__chevron" viewBox="0 0 24 24" aria-hidden="true" data-chevron><path d="M9.5 6l6 6-6 6"/></svg>
              </button>
              <a class="row" role="menuitem" href="#" tabindex="-1" data-item="help">
                <svg class="row__icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8.6"/><g data-ask><path d="M9.6 9.7a2.5 2.5 0 1 1 3.5 2.3c-.7.3-1.1.9-1.1 1.6v.3"/><path d="M12 16.7v.1"/></g></svg>
                <span class="row__label" data-label>Help &amp; support</span>
              </a>
              <div class="menu__rule" role="separator"></div>
              <button class="row row--danger" type="button" role="menuitem" tabindex="-1" aria-haspopup="menu" aria-expanded="false" aria-controls="badge-signout" data-item="signout">
                <svg class="row__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M13.5 4.5H7a1.6 1.6 0 0 0-1.6 1.6v11.8A1.6 1.6 0 0 0 7 19.5h6.5"/><g data-leave><path d="M10.5 12h9.5M17 8.8l3.2 3.2-3.2 3.2"/></g></svg>
                <span class="row__label" data-label>Sign out</span>
              </button>
            </div>
            <span class="face__sheen" aria-hidden="true" data-sheen></span>
            <span class="face__dim" aria-hidden="true" data-dim></span>
          </section>

          <!-- the back: the workspaces, or the question before signing out -->
          <section class="face face--back" data-face="back">
            <div class="back__top"><span class="slot" aria-hidden="true"></span></div>

            <div class="page" data-page="workspaces">
              <div class="menu" role="menu" id="badge-workspaces" aria-label="Switch workspace" tabindex="-1" data-menu="workspaces">
                <span class="menu__hl" aria-hidden="true" data-hl></span>
                <button class="row row--back" type="button" role="menuitem" tabindex="-1" aria-label="Back to the account menu" data-item="back">
                  <svg class="row__icon" viewBox="0 0 24 24" aria-hidden="true" data-back><path d="M14.5 6l-6 6 6 6"/></svg>
                  <span class="row__label" data-label>Workspaces</span>
                </button>
                <div class="menu__rule" role="separator"></div>
                <button class="row row--ws" type="button" role="menuitemradio" aria-checked="true" tabindex="-1" style="--ws: #0d5c58" data-ws="tidewell" data-role="Admin" data-plan="Team plan">
                  <span class="ws__logo" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M3.5 10c2.1-2.2 4.3-2.2 6.4 0s4.3 2.2 6.4 0 2.6-1.4 4.2-1.4M3.5 15.5c2.1-2.2 4.3-2.2 6.4 0s4.3 2.2 6.4 0 2.6-1.4 4.2-1.4"/></svg></span>
                  <span class="ws__text"><span class="ws__name" data-label>Tidewell Studio</span><span class="ws__meta">14 members</span></span>
                  <svg class="row__check" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.6l4.3 4.3L19 7.2"/></svg>
                </button>
                <button class="row row--ws" type="button" role="menuitemradio" aria-checked="false" tabindex="-1" style="--ws: #3b3f99" data-ws="ferncastle" data-role="Guest" data-plan="Pro plan">
                  <span class="ws__logo" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M12 20.5V4.5M12 9.2 8 6.6M12 12.8l-4.8-2.8M12 16.4l-3.4-2.4M12 9.2l4-2.6M12 12.8l4.8-2.8M12 16.4l3.4-2.4"/></svg></span>
                  <span class="ws__text"><span class="ws__name" data-label>Ferncastle</span><span class="ws__meta">Client &middot; 6 members</span></span>
                  <svg class="row__check" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.6l4.3 4.3L19 7.2"/></svg>
                </button>
                <button class="row row--ws" type="button" role="menuitemradio" aria-checked="false" tabindex="-1" style="--ws: #7c3a6b" data-ws="sketchbook" data-role="Owner" data-plan="Free">
                  <span class="ws__logo" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M5 19l1.2-4.4 9.4-9.4a2 2 0 0 1 2.8 0l.4.4a2 2 0 0 1 0 2.8l-9.4 9.4zM13.8 7l3.2 3.2"/></svg></span>
                  <span class="ws__text"><span class="ws__name" data-label>Sketchbook</span><span class="ws__meta">Personal &middot; just you</span></span>
                  <svg class="row__check" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.6l4.3 4.3L19 7.2"/></svg>
                </button>
                <div class="menu__rule" role="separator"></div>
                <button class="row" type="button" role="menuitem" tabindex="-1" data-item="new">
                  <svg class="row__icon" viewBox="0 0 24 24" aria-hidden="true" data-plus><path d="M12 5.5v13M5.5 12h13"/></svg>
                  <span class="row__label" data-label>New workspace</span>
                </button>
              </div>
            </div>

            <div class="page page--ask" data-page="signout" hidden>
              <svg class="ask__door" viewBox="0 0 48 48" aria-hidden="true" data-door>
                <path class="ask__frame" d="M26 8.5H13.5a3 3 0 0 0-3 3v25a3 3 0 0 0 3 3H26"/>
                <path class="ask__arrow" d="M20.5 24h19M33.5 17.8l6.2 6.2-6.2 6.2"/>
              </svg>
              <p class="ask__title" id="badge-ask">Sign out of <span data-ask-ws>Tidewell Studio</span>?</p>
              <p class="ask__text" id="badge-ask-text">You will need to sign in again on this browser. Your files stay put.</p>
              <div class="menu menu--ask" role="menu" id="badge-signout" aria-labelledby="badge-ask" aria-describedby="badge-ask-text" tabindex="-1" data-menu="signout">
                <span class="menu__hl" aria-hidden="true" data-hl></span>
                <button class="row pill pill--danger" type="button" role="menuitem" tabindex="-1" data-item="confirm"><span class="row__label" data-label>Sign out</span></button>
                <button class="row pill" type="button" role="menuitem" tabindex="-1" data-item="cancel"><span class="row__label" data-label>Cancel</span></button>
              </div>
            </div>

            <span class="back__stripe" aria-hidden="true"></span>
            <p class="back__print" aria-hidden="true">If found, please return to the front desk.</p>
            <span class="face__sheen" aria-hidden="true" data-sheen></span>
            <span class="face__dim" aria-hidden="true" data-dim></span>
          </section>

          <!-- the clip strap through the slot, seen from the front and from the back -->
          <span class="strap strap--front" aria-hidden="true"><span class="strap__snap"></span></span>
          <span class="strap strap--back" aria-hidden="true"><span class="strap__snap"></span></span>
          <!-- the thickness of the card, seen when it is edge-on -->
          <span class="edge edge--left" aria-hidden="true"></span>
          <span class="edge edge--right" aria-hidden="true"></span>
        </div>
      </div>

      <!-- the reel: the button, with the portrait on its domed face -->
      <button class="reel" type="button" aria-haspopup="menu" aria-expanded="false" aria-controls="badge-menu" aria-label="Account: Ines Marlow" data-reel data-autoclic>
        <span class="reel__body" data-reel-body>
          <span class="reel__face">
            <svg class="reel__portrait" aria-hidden="true" data-face-in><use href="#portrait"/></svg>
            <svg class="reel__portrait" aria-hidden="true" data-face-out><use href="#nobody"/></svg>
          </span>
          <span class="reel__dome" aria-hidden="true"></span>
        </span>
        <span class="reel__nozzle" aria-hidden="true"></span>
      </button>
      <p class="account__note" aria-hidden="true" data-note>Signed out &middot; <b>Sign in</b></p>
    </div>
    <p class="sr" aria-live="polite" data-say></p>
  </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.

54 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 menu del tesserino» da Animorio e adattala al mio sito

Cosa fa

Su una pagina bianca c'è solo l'avatar di Ines Marlow, un ritratto disegnato sotto la cupola di resina di un avvolgibile da tesserino, con l'anello sottile nel colore dell'area di lavoro e, sotto, il beccuccio da cui esce il cordino. Col puntatore sopra la bobina si alza un poco e l'anello del cordino si affaccia dal beccuccio. Premuta, la bobina cede appena e il tesserino esce: cade, il cordino intrecciato lo prende con un piccolo rimbalzo e lui scende in 3D dal punto in cui era inclinato all'indietro, va un po' oltre verso di te e torna, dondolando di lato prima di fermarsi. È una tessera di plastica bianca con gli angoli tondi, la fessura, la linguetta trasparente col bottone a pressione, la sua ombra sul muro e un riflesso che scivola sulla plastica quando si muove: in alto la fascia dell'area di lavoro, Tidewell Studio, col suo marchio e il ruolo (Admin), poi la foto, il nome e la mail, e sei voci: Profile, Settings, Billing col piano, Switch workspace, Help & support e Sign out. Un'evidenziazione nel colore dell'area di lavoro scivola da una voce all'altra e ogni icona fa la sua mossa: la testa annuisce, l'ingranaggio gira, i due quadrati si scambiano, la freccia esce dalla porta. Switch workspace gira il tesserino sul moschettone, con una frustata e un'oscillazione che si spegne: sul retro bianco, con la banda magnetica e la scritta If found, please return to the front desk, ci sono le aree di lavoro. Scegliendone un'altra il tesserino torna davanti già stampato coi colori nuovi, fascia, marchio, ruolo e piano. Sign out lo gira sul retro per chiedere Sign out of Tidewell Studio?; confermato, la clip si apre, il tesserino cade inclinandosi e svanisce, il cordino rientra di scatto nella bobina, che dà un colpo, e il ritratto lascia il posto a una faccia vuota con Signed out · Sign in. Il tesserino si prende anche per la fascia e si tira fuori: il cordino si allunga, e lasciato la bobina lo riavvolge e lui dondola. Premuto, oscilla appena, come una tessera appesa a un cordino.

Come funziona

Due punti e un'asta

L'anello e il centro del tesserino sono due punti con una massa (0,3 e 1) uniti da un'asta rigida: ogni passo si sommano gravità e forze, i punti si muovono, poi l'asta li riporta alla loro distanza spostandoli in proporzione all'inverso della massa e le velocità si rileggono dalle posizioni (position-based dynamics). Così il tesserino dondola come un pendolo appeso all'anello senza che si scrivano equazioni di angoli. Il conto gira sul ticker di GSAP in passi di 4 millesimi, col fotogramma limitato a 1/30 di secondo, e si ferma quando tutto è fermo.

Un cordino che tira soltanto

Il cordino è una molla rigida (CATCH) con uno smorzatore sulla velocità lungo il cordino, ma solo quando è più lungo di CORD: allentato non spinge. Il tesserino parte dentro la bobina, cade libero e il cordino lo prende di colpo: il rimbalzo viene da lì. All'equilibrio è allungato di quanto pesa, (1,3 · GRAVITY) / CATCH. Tirato con la mano la bobina lascia uscire quello che serve; lasciato, la lunghezza torna a CORD su una molla smorzata al punto critico e il cordino, tirando, porta su il tesserino che dondola.

L'inclinazione e la rotazione

Il tesserino si inclina avanti e indietro come una tessera appesa per il bordo alto: un pendolo fisico lungo D + H² / (12 D), con D dall'anello al centro. Parte inclinato di TIP gradi con una piccola spinta e scende per conto suo. La rotazione sul moschettone è una molla (TWIST) verso 0 o 180 gradi: girandolo si aggiunge una frustata alla velocità, e lei va oltre e torna. Premendo il tesserino, inclinazione e rotazione ricevono un colpetto, più forte quanto più in basso o di lato si preme.

Una trasformazione sola

Il tesserino è un contenitore senza misura con preserve-3d, le due facce a backface-visibility hidden (il retro già girato di 180 gradi) e due listelli larghi 2 pixel per lo spessore. A ogni fotogramma riceve una sola transform: la posizione dell'anello, il dondolio letto dall'asta, poi una perspective() centrata sul tesserino con translateY prima e dopo, l'inclinazione intorno all'anello e la rotazione intorno al suo asse. L'opacità va su ogni faccia, mai sul contenitore: appiattirebbe il 3D. Un velo scuro scurisce la faccia che si gira o si china verso di te, e un riflesso a gradiente scivola con gli angoli.

Fermo e nitido

Quando velocità, angoli e allungamento sono sotto una soglia, tutto va esattamente al suo posto e il tesserino si disegna piatto: solo un translate arrotondato e, per il retro, la faccia posteriore senza rotazione. Così le parole sono nitide. Appena qualcosa lo muove torna in 3D e si ridisegna subito, nello stesso fotogramma.

Il menu

La bobina è un button con aria-haspopup e aria-expanded; davanti c'è un role menu con menuitem, sul retro due menu: le aree di lavoro con menuitemradio e la domanda prima di uscire. Frecce, Home, Fine, Invio, Esc, freccia destra e sinistra per entrare e uscire dal retro, e le prime lettere funzionano come in un menu nativo; la faccia nascosta è inert e il focus passa sull'altra quando arriva davanti. Esc, Tab o un clic fuori riportano il focus alla bobina. Scegliendo un'area di lavoro la fascia, il marchio, il ruolo e il piano si ristampano mentre il davanti è girato.

Valori da cambiare

NomeFileDi partenzaCosa cambia
CORDscript.js28Quanto cordino lascia uscire la bobina quando il tesserino pende, in pixel.
GRAVITYscript.js6400Con quanta forza il tesserino cade e dondola, in pixel al secondo quadrato.
CATCHscript.js[2400, 0.3]Il cordino che prende il tesserino: quanto è rigido e quanto frena il rimbalzo (1 = niente rimbalzo).
TIPscript.js70Di quanti gradi è inclinato all'indietro il tesserino quando esce dalla bobina.
LEANscript.js5Di quanti gradi pende di lato quando esce, e quindi quanto dondola.
AIRscript.js3.2Quanto in fretta si spegne il dondolio.
TWISTscript.js[170, 0.42]Il moschettone che gira il tesserino: quanto è rigido e quanto frena l'oscillazione (1 = niente).
ROCKscript.js1Quanto oscilla il tesserino quando lo si preme (0 = per niente).
GLIDEscript.js0.22I secondi dell'evidenziazione che va da una voce all'altra.

Provali nel Playground

Quando usarla

Per il menu dell'account di uno strumento per i progetti, uno studio, un coworking, l'app di un evento o di una conferenza: dove avere il proprio tesserino ha senso e il menu si apre poche volte al giorno, così il suo piccolo spettacolo non stanca. Il tesserino pende sotto la bobina: in una barra in alto a destra serve un po' di spazio a sinistra, o una bobina spostata verso il centro.

Sul telefono

Tutto è largo 264 pixel e sta in 390 senza sforare; sotto i 360 la scena si rimpicciolisce un poco. Il tocco fa come il clic e accende la voce prima di sceglierla; il tesserino si tira col dito dalla fascia o dalla foto (lì touch-action è none), mentre le voci restano da toccare. La scorciatoia di Settings sul telefono non c'è.

Con il movimento ridotto

Niente caduta, rimbalzo, dondolio né rotazione: il tesserino compare già appeso con una dissolvenza, passa sul retro e torna sul davanti con una dissolvenza fra le due facce, e uscendo svanisce sul posto. Niente colpetti premendolo, niente trascinamento, niente mosse delle icone; l'evidenziazione salta da una voce all'altra.

Trappole

  • L'ombra sul muro fatta con un box-shadow su un elemento trasparente disegnava una seconda tessera fantasma: il box-shadow non si dipinge sotto l'elemento, così l'area spostata restava bianca e incorniciata dall'ombra. L'ombra è una macchia piena e sfocata (filter blur) un poco più piccola del tesserino.
  • Un'opacità sul contenitore in preserve-3d lo appiattisce e il retro sparirebbe: la dissolvenza va su ogni faccia, sulle linguette e sui bordi, uno per uno.
  • Il tesserino fermo si disegna piatto per avere le parole nitide; ripartendo, se tolto il piatto si aspettava il fotogramma dopo per la transform 3D, per un fotogramma si vedeva il davanti al posto del retro e il clic su un'area di lavoro finiva sulla faccia inert e si perdeva. Ora si ridisegna subito.
  • Aprendo, il tesserino diventava visibile prima del primo disegno: per un fotogramma compariva intero nell'angolo in alto a sinistra. Si disegna nello stesso momento in cui si mostra, con l'opacità a zero.
  • Un clic dell'anteprima e quello di un lettore di schermo hanno entrambi detail 0: per distinguerli si guarda isTrusted, falso solo per il clic dello script, che fa partire la dimostrazione.
  • Invio sulle voci farebbe due scelte, quella del tasto e il clic che il browser genera: il keydown fa preventDefault e sceglie lui.
  • Tirando il tesserino col mouse si selezionava il testo della fascia: user-select none sul tesserino e preventDefault sul pointerdown dove lo si prende.
  • La scritta Signed out sta dove passa il cordino: al rientro sparisce subito invece di dissolversi, così il cordino non la copre mai.
  • Le mosse delle icone: passando in fretta su una voce, la mossa di ritorno, più corta, finiva prima di quella d'andata e l'icona restava girata. Prima di ogni mossa si fermano quelle in corso sugli stessi pezzi.

Librerie

GSAP 3.15.0, Host Grotesk (font variabile) 5.3.0.

Compressi con gzip, i tre file della demo pesano 19 KB; le librerie: GSAP 28 KB.

Collaudo

Provata il 6 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