Vai al contenuto
animorio
EN
Tutti i componenti

Il pulsante della buonanotte

Pulsanti, SVG, HoverSolo CSS, GSAPImpegnativaPeso compresso: Demo 11 KBGSAP 28 KBGratisScarica lo zip

Il Log out di un'app come una pillola di cielo con un piccolo ritratto disegnato in SVG nella punta: sveglio, gli occhi seguono il puntatore e sbatte le palpebre a momenti casuali. Al clic le palpebre si fanno pesanti, uno sbadiglio, il cielo nella pillola scorre dal giorno al tramonto alla notte, il sole tramonta, sale la luna con poche stelle disegnate e il ritratto si addormenta con la testa piegata, respirando, mentre salgono piccole z disegnate: Signed out · Sleep well. Un altro clic lo sveglia di soprassalto e torna il sole: Good morning, Ada.

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 goodnight button</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the goodnight button: the Log out of an app, a pill of sky with a small drawn portrait in its round left end. The
         portrait is awake: its eyes follow the pointer and it blinks now and then. A click logs out: the eyelids grow
         heavy, a yawn, the sky inside the pill runs from day to night, the moon comes up and the portrait falls asleep
         with its head on one side, breathing. Another click signs in: it wakes with a start and the sun comes back -->
    <div class="night" data-night>
      <button class="night__btn" type="button" data-btn data-autoclic>
        <span class="night__fill" aria-hidden="true" data-fill>
          <span class="night__sky" data-sky></span>
          <span class="night__dark" data-dark></span>
        </span>
        <span class="night__clip" aria-hidden="true">
          <svg class="night__heavens" viewBox="0 0 40 60" data-heavens>
            <defs>
              <mask id="nb-crescent">
                <circle cx="20" cy="23" r="7.4" fill="#fff" />
                <circle cx="23.6" cy="20" r="6.6" fill="#000" />
              </mask>
            </defs>
            <g class="night__stars" data-stars>
              <path transform="translate(5 12) scale(0.9)" d="M0 -2.4Q0.35 -0.35 2.4 0Q0.35 0.35 0 2.4Q-0.35 0.35 -2.4 0Q-0.35 -0.35 0 -2.4Z" />
              <path transform="translate(34 42) scale(0.7)" d="M0 -2.4Q0.35 -0.35 2.4 0Q0.35 0.35 0 2.4Q-0.35 0.35 -2.4 0Q-0.35 -0.35 0 -2.4Z" />
              <path transform="translate(9 46) scale(0.55)" d="M0 -2.4Q0.35 -0.35 2.4 0Q0.35 0.35 0 2.4Q-0.35 0.35 -2.4 0Q-0.35 -0.35 0 -2.4Z" />
              <path transform="translate(33 8) scale(0.5)" d="M0 -2.4Q0.35 -0.35 2.4 0Q0.35 0.35 0 2.4Q-0.35 0.35 -2.4 0Q-0.35 -0.35 0 -2.4Z" />
            </g>
            <g data-sun>
              <g class="night__rays" data-rays>
                <path d="M20 12.2v-2.6M20 33.8v2.6M9.2 23h-2.6M30.8 23h2.6M12.4 15.4l-1.8-1.8M27.6 30.6l1.8 1.8M12.4 30.6l-1.8 1.8M27.6 15.4l1.8-1.8" />
              </g>
              <circle class="night__sun" cx="20" cy="23" r="7" />
            </g>
            <g data-moon>
              <rect class="night__moon" x="10" y="13" width="20" height="20" mask="url(#nb-crescent)" />
            </g>
          </svg>
          <svg class="night__zs" viewBox="0 0 14 21" data-zs>
            <path d="M0 0h3.4L0 3.8h3.4" />
            <path d="M0 0h3.4L0 3.8h3.4" />
            <path d="M0 0h3.4L0 3.8h3.4" />
          </svg>
        </span>
        <span class="night__portrait" aria-hidden="true">
          <svg viewBox="0 0 50 50" data-portrait>
            <defs>
              <clipPath id="nb-round"><circle cx="25" cy="25" r="23.6" /></clipPath>
              <clipPath id="nb-eye"><path d="M-2.7 0Q0 -2.7 2.7 0Q0 2.1 -2.7 0Z" /></clipPath>
            </defs>
            <g clip-path="url(#nb-round)">
              <circle class="pt-back" cx="25" cy="25" r="24" />
              <g data-body>
                <path class="pt-sweater" d="M2 52C3 41.5 12.5 37.6 25 37.6S47 41.5 48 52Z" />
                <path class="pt-neck" d="M21.3 30.5h7.4v6.6q-3.7 2.8 -7.4 0Z" />
                <path class="pt-collar" d="M19.4 38.2Q25 43 30.6 38.2" />
              </g>
              <g data-head>
                <path class="pt-hair" d="M12.2 26C11.2 14.2 17 8.6 25 8.6S38.8 14.2 37.8 26C37.6 31 36.4 33.8 34.6 34.6L15.4 34.6C13.6 33.8 12.4 31 12.2 26Z" />
                <ellipse class="pt-skin" cx="15.2" cy="25.4" rx="1.8" ry="2.5" />
                <ellipse class="pt-skin" cx="34.8" cy="25.4" rx="1.8" ry="2.5" />
                <ellipse class="pt-skin" cx="25" cy="24.6" rx="9.7" ry="11.1" />
                <g data-face>
                  <ellipse class="pt-cheek" cx="18.6" cy="28.6" rx="2" ry="1.2" />
                  <ellipse class="pt-cheek" cx="31.4" cy="28.6" rx="2" ry="1.2" />
                  <g class="pt-eye" transform="translate(21 24.6)">
                    <path class="pt-white" d="M-2.7 0Q0 -2.7 2.7 0Q0 2.1 -2.7 0Z" />
                    <g clip-path="url(#nb-eye)">
                      <g data-pupil>
                        <circle class="pt-iris" r="1.45" />
                        <circle class="pt-glint" cx="-0.45" cy="-0.5" r="0.42" />
                      </g>
                      <rect class="pt-lid" x="-3" y="-2.6" width="6" height="0" data-lid />
                    </g>
                    <path class="pt-lash" d="M-2.8 0Q0 -1.2 2.8 0" data-lash />
                  </g>
                  <g class="pt-eye" transform="translate(29 24.6)">
                    <path class="pt-white" d="M-2.7 0Q0 -2.7 2.7 0Q0 2.1 -2.7 0Z" />
                    <g clip-path="url(#nb-eye)">
                      <g data-pupil>
                        <circle class="pt-iris" r="1.45" />
                        <circle class="pt-glint" cx="-0.45" cy="-0.5" r="0.42" />
                      </g>
                      <rect class="pt-lid" x="-3" y="-2.6" width="6" height="0" data-lid />
                    </g>
                    <path class="pt-lash" d="M-2.8 0Q0 -1.2 2.8 0" data-lash />
                  </g>
                  <path class="pt-brow" d="M18.6 20.6Q21 19.4 23.2 20.4" data-brow />
                  <path class="pt-brow" d="M26.8 20.4Q29 19.4 31.4 20.6" data-brow />
                  <path class="pt-nose" d="M25.3 25.8Q26.2 27.6 24.9 28" />
                  <path class="pt-smile" d="M23 30.6Q25 32 27 30.6" data-smile />
                  <ellipse class="pt-yawn" cx="25" cy="31.2" rx="0" ry="0" data-yawn />
                </g>
                <path class="pt-hair" d="M14.8 23C14.6 14.6 19.6 11.4 25.6 11.4C31 11.4 35.4 14.4 35.6 22C32.6 18.4 28.6 16.6 24.2 17.4C20.4 18.2 17.2 20.2 14.8 23Z" data-fringe />
              </g>
              <rect class="pt-moonlight" x="0" y="0" width="50" height="50" data-moonlight />
            </g>
            <circle class="pt-ring" cx="25" cy="25" r="23.8" />
          </svg>
        </span>
        <span class="night__label">
          <span class="night__word" data-word data-wide="Log out">Log out</span>
        </span>
      </button>
      <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.

30 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 pulsante della buonanotte» da Animorio e adattala al mio sito

Cosa fa

Su un fondo bianco una pillola alta 60 pixel fatta di cielo di giorno, azzurro in alto e quasi bianco in basso, con un filo di luce sul bordo e un'ombra morbida. Nella punta tonda di sinistra, dentro un anello bianco, il ritratto di una persona disegnata: un caschetto scuro con la frangia, il viso con le guance rosate, il naso in una linea, un sorriso e un maglione color terracotta. Al centro, in Mona Sans blu notte, Log out; nella punta di destra un sole giallo coi suoi raggi. All'arrivo il pulsante sale piano e il ritratto apre gli occhi. Gli occhi seguono il puntatore dappertutto nella pagina: le pupille si spostano negli occhi con uno scatto vivo, il viso le segue un poco, la frangia meno, la testa gira appena, così il ritratto ha profondità. Sbatte le palpebre a momenti casuali, ogni tanto due volte di fila. Col mouse sopra sorride di più e alza le sopracciglia, la scritta si allarga e il cielo si gonfia con un rimbalzo. Al clic la scritta diventa Logging out…: le palpebre si fanno pesanti e le sopracciglia cedono, uno sbadiglio apre la bocca e manda la testa indietro, poi la bocca si chiude e le palpebre calano; intanto il cielo scorre dentro la pillola, dal giorno a un tramonto pesca, poi malva, poi blu notte, il sole scende fuori dalla pillola, la luna sale al suo posto e compaiono quattro piccole stelle disegnate, ferme; la scritta diventa chiara e il ritratto, illuminato di blu dalla luna, si addormenta con la testa piegata da un lato e respira piano, le spalle che si alzano e si abbassano, mentre piccole z disegnate salgono e svaniscono accanto a lui: Signed out · Sleep well, poi Sign in. Un altro clic: si sveglia di soprassalto, gli occhi si spalancano, le sopracciglia saltano, la testa torna dritta con un sobbalzo, il cielo torna giù veloce, la luna scende e il sole risale con un rimbalzo girando i raggi: Signing in…, poi Good morning, Ada con un gran sorriso, poi di nuovo Log out. Con la tastiera il fuoco mostra un anello blu notte, la scritta si allarga e il ritratto sorride; Invio o Spazio funzionano come il clic. Al telefono gli occhi seguono il dito e il tocco esce o entra.

Come funziona

Il ritratto in SVG

Il ritratto è un SVG di 50 unità ritagliato in un cerchio: il maglione e il collo in un gruppo, la testa (capelli dietro, orecchie, viso, i lineamenti, la frangia davanti) in un altro che si piega intorno al collo. Ogni occhio è una mandorla bianca con dentro, ritagliate dalla stessa mandorla, la pupilla col suo riflesso e la palpebra: un rettangolo color pelle che cresce in altezza. Il bordo della palpebra è una curva sola che scende con lei e si rovescia: arcuata da aperta, la curva del sonno da chiusa.

Il viso come numeri

Il viso è un oggetto di numeri (lo sguardo, le palpebre, l'ammiccare, il sorriso, lo sbadiglio, le sopracciglia, l'inclinazione, il sobbalzo, il respiro, la luce della luna) che GSAP anima e una funzione scrive nell'SVG: trasformazioni, l'altezza delle palpebre, i tracciati del bordo, delle sopracciglia e della bocca ricostruiti a ogni fotogramma. Una chiave fatta dei numeri arrotondati al centesimo dice se qualcosa è cambiato: se no, non si scrive niente e il ciclo si ferma.

Lo sguardo

La direzione dal centro del ritratto al puntatore, piena oltre 90 pixel e proporzionale più vicino, è la meta dello sguardo; lo sguardo disegnato la segue su una molla svelta (4,2 oscillazioni al secondo, smorzamento 0,62), così gli occhi scattano e si posano. Le pupille si spostano di 1,1 unità, i lineamenti di 0,9, la frangia di 0,45 e la testa gira di 1,2 gradi: la parallasse fra gli strati dà la profondità. LOOK le scala tutte.

Le palpebre a caso

Un delayedCall di GSAP richiama sé stesso a un momento casuale fra i due numeri di BLINK; l'ammiccare scende in 0,07 secondi e risale in 0,15, e una volta su cinque circa lo rifà subito. È un numero a parte dalle palpebre pesanti, e conta il più chiuso dei due, così un ammiccare non riapre gli occhi di chi ha sonno.

Il cielo che scorre

Il cielo è un gradiente alto quattro pillole (giorno, tramonto pesca, malva, notte) dentro il fondo con overflow: hidden; uscire lo fa salire di yPercent −75 in DUSK secondi, ed entrare lo riporta giù. Il sole scende di 60 unità, la luna (un rettangolo con una maschera a falce: un cerchio bianco meno uno nero spostato) sale al suo posto, le stelle a quattro punte entrano con back.out e restano ferme, il colore delle parole passa per una variabile CSS animata da GSAP e un velo blu sul ritratto fa la luce della luna.

Gli stati

Il pulsante è un button vero, con aria-disabled mentre esce o entra e una zona aria-live che dice Logging out…, Signed out · Sleep well, Signing in… e Good morning, Ada; tutto è simulato coi tempi di GSAP. Addormentato, il respiro è un tween in yoyo infinito e le z una timeline ripetuta di tre lettere disegnate; svegliandosi si fermano. La pillola prende la larghezza misurata delle parole con back.out lasciando a destra il posto del sole, e quando si stringe aspetta che le vecchie parole siano sparite.

Valori da cambiare

NomeFileDi partenzaCosa cambia
LOG_OUTscript.jsLog outLa scritta a riposo, da dentro.
LOGGING_OUTscript.jsLogging out…La scritta mentre esce; la dice anche la zona aria-live.
SLEEP_WELLscript.jsSigned out · Sleep wellLa scritta appena uscito, prima di Sign in.
SIGN_INscript.jsSign inLa scritta a riposo, da fuori.
SIGNING_INscript.jsSigning in…La scritta mentre entra.
MORNINGscript.jsGood morning, AdaIl saluto appena entrato. L'accesso è simulato: in un sito vero qui va il nome che il tuo server restituisce.
DUSKscript.js1.8I secondi in cui il cielo passa dal giorno alla notte.
BLINKscript.js[1.6, 4.8]Fra quanti secondi, al minimo e al massimo, arriva il prossimo battito di palpebre.
LOOKscript.js1Quanto gli occhi e il viso si girano verso il puntatore: 0 restano fermi.
TILTscript.js13Quanto si piega la testa da un lato quando dorme, in gradi.
BREATHscript.js3.4I secondi di un respiro nel sonno.

Provali nel Playground

Quando usarla

Per l'uscita di un'app serale o tranquilla: sonno e meditazione, lettura, musica, un diario, un'app per bambini o per la famiglia, o semplicemente un prodotto con un tono gentile. Il ritratto va sostituito con l'avatar dell'utente o con la mascotte del prodotto; l'uscita vera va collegata al proprio server: qui è simulata.

Sul telefono

La pillola è larga fra circa 190 e 300 pixel e a 390 ci sta sempre; se le parole sono troppo lunghe il carattere si rimpicciolisce. Gli occhi seguono il dito appoggiato sulla pagina, il tocco esce o entra; il ritratto, il sole e la luna restano della stessa misura.

Con il movimento ridotto

Gli occhi non seguono il puntatore e non sbattono, niente sbadiglio, niente respiro né z, la pillola non si gonfia e la scritta non si allarga. Al clic il ritratto svanisce e ricompare già addormentato, la notte arriva in dissolvenza sopra il giorno, il sole svanisce e la luna e le stelle compaiono in dissolvenza; un altro clic fa tornare il giorno e il ritratto sveglio nello stesso modo.

Trappole

  • Il sole coperto da un alone tondo e chiaro, sul cielo azzurro, sembrava un pallino luminoso: ora è un disco giallo coi raggi disegnati, e i raggi girano solo quando risale.
  • Tramontando di 46 unità, l'alone del sole spuntava ancora dal bordo basso della pillola nella notte: ora sole e luna scendono di 60.
  • Le z erano grandi meno di quattro pixel e color notte su notte: ora sono più grandi, chiare, e salgono da accanto alla testa senza mai toccare le parole.
  • La pillola si restringeva da Good morning, Ada a Log out mentre la scritta vecchia svaniva, e per un attimo le sue ultime lettere passavano sotto il sole: ora quando si stringe aspetta che le parole vecchie siano andate.
  • Un battito di palpebre che finiva con le palpebre aperte riapriva gli occhi di chi sbadigliava: l'ammiccare è un numero a parte e si disegna il più chiuso dei due.
  • GSAP gira gli elementi SVG intorno al loro riquadro: i raggi del sole si giravano attorno a un angolo. Si usa svgOrigin col centro del sole.

Librerie

GSAP 3.15.0, Mona Sans (font variabile) 5.3.0.

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

Collaudo

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