Vai al contenuto
animorio
EN
Tutti i componenti

Il menu dell’ascensore

Menu, SVG, Transizioni, FisicaGSAP, Solo CSSImpegnativaPeso compresso: Demo 9,6 KBGSAP 28 KBGratisScarica lo zip

Un menu a tutto schermo che arriva come un ascensore d’acciaio: a riposo c’è solo il pulsante di chiamata, d’acciaio spazzolato con un anello di luce che si accende. Premuto, compare la cabina e le porte telescopiche a due velocità si aprono, con le ante davanti che corrono il doppio di quelle dietro; sopra, un quadrante a lancetta con i nomi delle sezioni al posto dei piani. Scegliendo una voce le porte si chiudono, la lancetta viaggia fino a quella sezione con un rimbalzo e le porte si riaprono.

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 elevator menu</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 elevator menu: at rest only a brushed steel call button with a ring of light. Pressed, the lift arrives:
         a steel portal with a floor dial over two speed telescopic doors, which open on the cabin and the menu.
         Choosing a section, the doors shut, the needle travels to it on the dial and the doors open again -->
    <div class="lift" data-lift>
      <!-- the call button: the Menu toggle. Open, it moves up into the portal and closes the doors -->
      <button class="call" type="button" aria-expanded="false" aria-controls="lift-menu" data-call data-autoclic>
        <span class="call__plate" aria-hidden="true">
          <span class="call__ring"></span>
          <span class="call__face">
            <svg class="call__icon call__icon--open" viewBox="0 0 24 24"><path d="M12 5v14"/><path d="M3.5 12L9 7.5v9zM20.5 12L15 7.5v9z"/></svg>
            <svg class="call__icon call__icon--close" viewBox="0 0 24 24"><path d="M12 5v14"/><path d="M9.5 12L4 7.5v9zM14.5 12L20 7.5v9z"/></svg>
          </span>
          <span class="call__label" data-word>Menu</span>
        </span>
        <span class="sr" data-name>Open the menu</span>
      </button>

      <!-- the portal: the dial, the doors, the cabin behind them -->
      <div class="car" data-car inert>
        <div class="car__head">
          <svg class="dial" viewBox="0 0 300 168" aria-hidden="true" data-dial>
            <defs>
              <radialGradient id="lift-face" cx="0.5" cy="1" r="1">
                <stop offset="0" stop-color="#2a2622"/>
                <stop offset="0.7" stop-color="#14120f"/>
                <stop offset="1" stop-color="#0b0a09"/>
              </radialGradient>
              <linearGradient id="lift-brass" x1="0" x2="1" y1="0" y2="1">
                <stop offset="0" stop-color="#f6dc9c"/>
                <stop offset="0.45" stop-color="#c2963f"/>
                <stop offset="1" stop-color="#6e4f1a"/>
              </linearGradient>
              <filter id="lift-glow" x="-40%" y="-80%" width="180%" height="260%">
                <feGaussianBlur stdDeviation="2.4" result="b"/>
                <feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
              </filter>
            </defs>
            <path class="dial__rim" d="M4 160A146 146 0 0 1 296 160Z"/>
            <path class="dial__face" d="M12 158A138 138 0 0 1 288 158Z" fill="url(#lift-face)"/>
            <g class="dial__ticks" data-ticks></g>
            <g class="dial__names" data-names></g>
            <path class="dial__dir" d="M34 139l8 11 8-11z" data-dir="-1"/>
            <path class="dial__dir" d="M250 150l8-11 8 11z" data-dir="1"/>
            <g class="dial__needle" data-needle>
              <path d="M150 158L147.4 153 150 66 152.6 153Z" fill="url(#lift-brass)"/>
              <path d="M150 158l-5 9h10z" fill="#6e4f1a"/>
            </g>
            <path class="dial__hub" d="M128 160A22 22 0 0 1 172 160Z" fill="url(#lift-brass)"/>
          </svg>
          <span class="car__slot" aria-hidden="true" data-slot></span>
        </div>

        <div class="car__opening" data-opening>
          <div class="cabin">
            <span class="cabin__light" aria-hidden="true"></span>
            <span class="cabin__rail" aria-hidden="true"></span>
            <nav class="menu" id="lift-menu" aria-label="Main" data-menu>
              <ul class="menu__list">
                <li><a class="floor" href="#" aria-current="page" data-floor><span class="floor__name">Lobby</span><span class="floor__note">Start here</span></a></li>
                <li><a class="floor" href="#" data-floor><span class="floor__name">Work</span><span class="floor__note">Selected projects</span></a></li>
                <li><a class="floor" href="#" data-floor><span class="floor__name">Studio</span><span class="floor__note">Who we are</span></a></li>
                <li><a class="floor" href="#" data-floor><span class="floor__name">Journal</span><span class="floor__note">Notes and essays</span></a></li>
                <li><a class="floor" href="#" data-floor><span class="floor__name">Contact</span><span class="floor__note">Say hello</span></a></li>
              </ul>
            </nav>
          </div>
          <!-- two speed telescopic doors: on each side a slow panel and a fast one in front of it -->
          <span class="door door--slow door--left" aria-hidden="true" data-door="-1"><span class="door__sheen"></span></span>
          <span class="door door--slow door--right" aria-hidden="true" data-door="1"><span class="door__sheen"></span></span>
          <span class="door door--fast door--left" aria-hidden="true" data-door="-2"><span class="door__sheen"></span></span>
          <span class="door door--fast door--right" aria-hidden="true" data-door="2"><span class="door__sheen"></span></span>
          <span class="car__pass" aria-hidden="true" data-pass></span>
        </div>
        <span class="car__sill" aria-hidden="true"></span>
      </div>
      <p class="sr" aria-live="polite" data-live></p>
    </div>
  </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.

29 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 dell’ascensore» da Animorio e adattala al mio sito

Cosa fa

Su un fondo nero una piccola piastra d’acciaio spazzolato con un pulsante tondo, la faccia tornita a cerchi concentrici e il simbolo delle porte che si aprono, e intorno un anello di vetro spento; sotto, inciso, MENU. Col mouse sopra l’anello si scalda di ambra e il riflesso della faccia gira. Premendo l’anello si accende di luce ambra; intorno arriva un portale d’acciaio spazzolato e la piastra sale nel suo angolo in alto a destra, dove ora dice CLOSE col simbolo delle porte che si chiudono. Sopra le porte c’è un quadrante a mezzaluna, scuro, con le tacche e i nomi Lobby, Work, Studio, Journal e Contact al posto dei piani, e una lancetta d’ottone su Lobby, che è acceso. Le porte si aprono dal centro: su ogni lato l’anta davanti corre il doppio di quella dietro e alla fine sono una sull’altra, fuori dalla vista, mentre un riflesso resta fermo nella stanza e le ante gli scorrono sotto. Dentro c’è la cabina, con le pareti d’acciaio scuro, una luce calda dal soffitto, il corrimano, e il menu: i cinque nomi grandi e stretti, con una riga piccola sotto ciascuno; quello della sezione corrente è ambra. Scegliendone un altro le porte si chiudono, si accende la freccia del verso, una luce passa sulle porte per ogni piano e la lancetta viaggia sul quadrante: i nomi che passa si accendono uno dopo l’altro, e arrivando supera un poco il suo nome e torna con un rimbalzo; la cabina fa un piccolo sobbalzo e le porte si riaprono sulla voce nuova in ambra. Esc o Close chiudono le porte, il portale se ne va e il pulsante torna al centro.

Come funziona

Acciaio spazzolato in CSS

Le porte, il portale e la piastra sono acciaio fatto solo di gradienti: una sfumatura di base nel verso della lunghezza e tre repeating-linear-gradient sottili, chiari e scuri, con passi di 3, 7 e 13 pixel che non si ripetono insieme, così le righe della spazzolatura sembrano irregolari. La faccia del pulsante è un conic-gradient (l’acciaio tornito) sotto un repeating-radial-gradient di cerchi; il suo angolo è una proprietà registrata con @property, così sotto il puntatore il riflesso gira in una transizione.

Porte telescopiche a due velocità

Le quattro ante sono larghe un quarto dell’apertura. Un solo numero va da 0 (chiuse) a 1 (aperte): le ante dietro si spostano di una larghezza d’anta per quel numero, quelle davanti di due, quindi nello stesso tempo corrono il doppio e alla fine stanno una sull’altra fuori dall’apertura, che taglia con overflow: hidden. Le ante davanti hanno l’ombra sul bordo e la gomma dove si incontrano.

Il riflesso che resta fermo

Dentro ogni anta (con overflow: hidden) c’è uno strato col riflesso, largo quanto tutta l’apertura e spostato a sinistra di quanto l’anta è lontana dal bordo (--px); a ogni fotogramma lo script lo trasla all’indietro di quanto si sposta l’anta, con un transform che lavora sul compositore senza ridipingere. Così il riflesso sta fermo nella stanza, come in uno specchio vero, e sono le ante a scorrergli sotto; le quattro ante insieme lo mostrano intero quando sono chiuse.

La lancetta su una molla

Il quadrante è un SVG: le tacche e i nomi si mettono sull’arco dalle voci del menu, a SWEEP gradi in tutto, fuori dalla corsa della lancetta così nessun nome è mai coperto. Viaggiando, una timeline sposta il bersaglio della lancetta da un piano all’altro in FLOOR secondi per piano, e la lancetta lo segue su una molla smorzata (STIFF, DAMP) in un ciclo a passi di 1/30 di secondo: resta un poco indietro, supera il nome e torna. Il nome sotto la lancetta si accende, come i piani di un vecchio indicatore.

La sequenza e il fuoco

GSAP fa le tre scene: l’arrivo (il portale compare e il pulsante vola nel suo posto con un FLIP, misurato fra la sua posizione e quella di un segnaposto nel portale), il viaggio (porte chiuse, freccia accesa, una luce che passa sulle porte per ogni piano, il sobbalzo dell’arrivo, porte aperte) e la partenza. Il menu è inert finché le porte non sono aperte a metà, il pulsante ha aria-expanded, Esc chiude e rimette il fuoco sul pulsante, e chi usa la tastiera trova il fuoco sulla voce corrente appena le porte si aprono.

Valori da cambiare

NomeFileDi partenzaCosa cambia
OPENscript.js1.15I secondi che le porte impiegano ad aprirsi.
SHUTscript.js0.8I secondi che impiegano a chiudersi.
FLOORscript.js0.4I secondi di viaggio per ogni piano.
STIFFscript.js120Quanto forte la molla tira la lancetta dove è la cabina.
DAMPscript.js0.3Quanto è frenata la lancetta: basso, rimbalza di più arrivando.
SWEEPscript.js140L’ampiezza dell’arco del quadrante, in gradi.
SHEENscript.js1Quanto è forte il riflesso sulle porte (0 = niente).

Provali nel Playground

Quando usarla

Per il sito di un hotel, di uno studio di architettura, di un palazzo d’uffici, di un’agenzia con un debole per il design d’epoca: dove aprire il menu può essere un piccolo viaggio e l’acciaio c’entra. Le sezioni devono essere poche (da tre a sei) perché stiano sul quadrante. Per un sito di tutti i giorni è troppo: il viaggio chiede qualche secondo.

Sul telefono

Il portale prende tutta la larghezza e diventa alto, con montanti più sottili, un quadrante più piccolo e il pulsante Close accanto; le ante restano quattro e il menu sta nella cabina senza sforare. Il tocco apre, sceglie e chiude; l’anello si accende alla pressione invece che al passaggio del mouse.

Con il movimento ridotto

Niente scorre: il portale e il pulsante compaiono in dissolvenza già al loro posto, le porte svaniscono invece di aprirsi e ricompaiono invece di chiudersi, la lancetta salta sul piano nuovo senza molla e senza viaggio, senza la luce che passa e senza sobbalzo.

Trappole

  • Con la lancetta lunga fino al bordo, passando copriva i nomi dei piani: i nomi stanno fuori, sull’anello esterno, e la lancetta e le tacche dentro.
  • Fra le due ante davanti, chiuse, restava un filo di un pixel da cui si vedevano le scritte della cabina: le ante davanti sono larghe un pixel in più e si sovrappongono al centro.
  • Il riflesso disegnato sull’anta scorreva con lei e la porta sembrava una figurina: va traslato all’indietro di quanto si sposta l’anta, così resta fermo nella stanza; con background-position ridipingeva le quattro ante a ogni fotogramma, con un transform no.
  • Riaprendo il menu, il segnaposto del pulsante si misurava col portale ancora rimpicciolito dalla chiusura e il pulsante finiva storto: il portale torna alla sua misura prima di misurare.
  • Le voci del menu, piene dall’inizio, si vedevano tagliate fra le ante che si aprivano: la loro opacità segue l’apertura e arriva piena solo quando il varco è largo.
  • Il quadrante alto quanto il pannello finiva sotto il bordo dell’apertura, con le frecce tagliate: ha un’altezza sua, un po’ meno del pannello.

Librerie

GSAP 3.15.0, Archivo (font variabile) 5.3.0.

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

Collaudo

Provata il 5 ottobre 2026

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

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