Back to the library

Toggle — flip chip

Variant: Flip

No track at all: a chip that turns over, oat on one face and clay on the other. The press leans 9deg into the turn so the outcome is legible from the trajectory, then the half turn overshoots 7deg and settles. Rotation is the one place a bit of bounce is correct — it is the physical reading of a thing with mass being spun. Use it where a state change deserves to be felt.

control

The source

A complete HTML document. Paste it into an empty .html file and it plays — no stylesheet, script, font, or image to fetch.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Toggle — flip chip</title>
    <style>
      :root {
        --ivory: #faf9f5;
        --slate: #141413;
        --clay: #d97757;
        --oat: #e3dacc;
        --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
        --ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);
      }

      * {
        box-sizing: border-box;
        margin: 0;
        padding: 0;
      }

      body {
        min-height: 100vh;
        display: grid;
        place-items: center;
        background: var(--ivory);
        font-family:
          system-ui,
          -apple-system,
          'Segoe UI',
          Roboto,
          sans-serif;
        color: var(--slate);
      }

      .stage {
        width: 84px;
        height: 84px;
        perspective: 520px;
      }

      .chip {
        position: relative;
        width: 100%;
        height: 100%;
        transform-style: preserve-3d;
        animation: chip-flip 4.2s infinite;
      }

      .face {
        position: absolute;
        inset: 0;
        display: grid;
        place-items: center;
        border-radius: 24px;
        backface-visibility: hidden;
        box-shadow: 0 2px 6px rgba(20, 20, 19, 0.14);
      }

      .face.off {
        background: var(--oat);
      }

      .face.on {
        background: var(--clay);
        transform: rotateY(180deg);
      }

      .bar {
        width: 28px;
        height: 4px;
        border-radius: 2px;
        background: var(--slate);
        opacity: 0.55;
      }

      .tick {
        width: 34px;
        height: 34px;
        fill: none;
        stroke: var(--ivory);
        stroke-width: 3.5;
        stroke-linecap: round;
        stroke-linejoin: round;
      }

      /* The press leans 9deg into the turn before it starts: a trajectory the
         eye can read the outcome from, rather than a state that just swaps.
         Rotation is the one place Apple ships damping below 1.0, so the half
         turn overshoots 7deg and settles back — 143ms of anticipation, 256ms
         out, 109ms to rest.
         Turning it back off is not that half-turn played in reverse. Switching
         something on is a decision and gets the full lean; switching it off is
         a dismissal, so the anticipation drops to 101ms, the turn to 214ms, the
         overshoot from 7deg to 4deg and the settle to 84ms. Same gesture, less
         ceremony — 399ms against 508ms. */
      @keyframes chip-flip {
        0%,
        9.5% {
          transform: rotateY(0deg) scale(1);
          animation-timing-function: var(--ease-out);
        }
        12.9% {
          transform: rotateY(9deg) scale(0.95);
          animation-timing-function: var(--ease-drawer);
        }
        19% {
          transform: rotateY(187deg) scale(1);
          animation-timing-function: var(--ease-out);
        }
        21.6%,
        52.4% {
          transform: rotateY(180deg) scale(1);
          animation-timing-function: var(--ease-out);
        }
        54.8% {
          transform: rotateY(174deg) scale(0.96);
          animation-timing-function: var(--ease-drawer);
        }
        59.9% {
          transform: rotateY(-4deg) scale(1);
          animation-timing-function: var(--ease-out);
        }
        61.9%,
        100% {
          transform: rotateY(0deg) scale(1);
        }
      }

      @media (prefers-reduced-motion: reduce) {
        /* No axis to spin around: both faces sit flat and cross-fade, which
           keeps the colour change that carries the meaning. */
        .chip {
          animation: none;
        }

        .face {
          backface-visibility: visible;
        }

        .face.on {
          transform: none;
          animation: face-in 4.2s ease infinite;
        }

        @keyframes face-in {
          0%,
          12.9% {
            opacity: 0;
          }
          19%,
          54.8% {
            opacity: 1;
          }
          59.9%,
          100% {
            opacity: 0;
          }
        }
      }
    </style>
  </head>
  <body>
    <div class="stage" role="img" aria-label="State chip flipping between off and on">
      <div class="chip">
        <div class="face off"><div class="bar"></div></div>
        <div class="face on">
          <svg class="tick" viewBox="0 0 24 24" aria-hidden="true">
            <path d="M5 12.5 L10 17.5 L19 7" />
          </svg>
        </div>
      </div>
    </div>
  </body>
</html>

The variant family

Same idea, one axis moved. Compare them side by side before you commit.