AI CSS Animations

Floating Action Button Menu

A round plus button that turns into a close X and fans three mini action buttons out in an arc, one after another. Each action rides a rotate, translate, rotate back chain, so its icon stays upright.

Also known as: floating button animation css, speed dial button css, fab menu css.

HTML

<div class="btn-speed-dial">
  <input class="btn-speed-dial-input" type="checkbox" aria-label="Show actions">
  <span class="btn-speed-dial-fab" aria-hidden="true"><i></i><i></i></span>
  <button class="btn-speed-dial-action" type="button" aria-label="Edit" style="--a:-150deg;--i:0"><svg viewBox="0 0 24 24"><path d="M4 20h4L19 9l-4-4L4 16z"/><path d="M13 7l4 4"/></svg></button>
  <button class="btn-speed-dial-action" type="button" aria-label="Share" style="--a:-90deg;--i:1"><svg viewBox="0 0 24 24"><circle cx="17.5" cy="5.5" r="2.5"/><circle cx="6.5" cy="12" r="2.5"/><circle cx="17.5" cy="18.5" r="2.5"/><path d="M8.7 10.8l6.6-4M8.7 13.2l6.6 4"/></svg></button>
  <button class="btn-speed-dial-action" type="button" aria-label="Add photo" style="--a:-30deg;--i:2"><svg viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2.5"/><circle cx="9" cy="10" r="1.6"/><path d="M21 16l-5-5-8.5 8"/></svg></button>
</div>

CSS

.btn-speed-dial {
  --c: var(--btn-color, #3b82f6);
  position: relative;
  display: grid;
  width: 3.4em;
  height: 3.4em;
  margin-top: 3em;
  font-size: var(--btn-size, 18px);
}
.btn-speed-dial-input {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
  border-radius: 50%;
}
.btn-speed-dial-fab {
  display: grid;
  place-items: center;
  border-radius: 50%;
  background-color: var(--c);
  color: #fff;
  box-shadow: 0 0.45em 1em -0.35em var(--c);
  transition: background-color 0.3s ease, transform 0.25s cubic-bezier(0.3, 1.6, 0.5, 1);
}
.btn-speed-dial-fab i {
  grid-area: 1 / 1;
  width: 1.3em;
  height: 0.18em;
  border-radius: 0.1em;
  background: currentColor;
  transition: transform 0.45s cubic-bezier(0.3, 1.5, 0.5, 1);
}
.btn-speed-dial-fab i:last-child { transform: rotate(90deg); }
.btn-speed-dial-action {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 2.5em;
  height: 2.5em;
  margin: -1.25em;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: color-mix(in srgb, var(--c) 8%, #fff);
  color: var(--c);
  box-shadow: 0 0.3em 0.7em -0.2em rgba(15, 23, 42, 0.35), inset 0 0 0 1px color-mix(in srgb, var(--c) 20%, transparent);
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transform: rotate(var(--a)) translate(0) rotate(calc(-1 * var(--a))) scale(0.4);
  transition:
    transform 0.3s cubic-bezier(0.5, 0, 0.75, 0) calc((2 - var(--i)) * 0.04s),
    opacity 0.2s ease calc((2 - var(--i)) * 0.04s),
    visibility 0s 0.4s;
}
.btn-speed-dial-action svg {
  width: 1.15em;
  height: 1.15em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.btn-speed-dial:active .btn-speed-dial-fab { transform: scale(0.92); }
.btn-speed-dial-input:focus-visible ~ .btn-speed-dial-fab,
.btn-speed-dial-action:focus-visible {
  outline: 2px solid var(--c);
  outline-offset: 0.2em;
}
.btn-speed-dial-input:checked ~ .btn-speed-dial-fab { background-color: color-mix(in srgb, var(--c) 75%, #0f172a); }
.btn-speed-dial-input:checked ~ .btn-speed-dial-fab i:first-child { transform: rotate(135deg); }
.btn-speed-dial-input:checked ~ .btn-speed-dial-fab i:last-child { transform: rotate(225deg); }
.btn-speed-dial-input:checked ~ .btn-speed-dial-action {
  opacity: 1;
  visibility: visible;
  transform: rotate(var(--a)) translate(3.6em) rotate(calc(-1 * var(--a))) scale(1);
  transition:
    transform 0.45s cubic-bezier(0.3, 1.5, 0.5, 1) calc(var(--i) * 0.06s),
    opacity 0.2s ease calc(var(--i) * 0.06s),
    visibility 0s;
}

@media (prefers-reduced-motion: reduce) {
  .btn-speed-dial, .btn-speed-dial *, .btn-speed-dial::before, .btn-speed-dial::after, .btn-speed-dial *::before, .btn-speed-dial *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Customizing this snippet

  • Color: set --btn-color on the element or a parent. Currently previewing #3b82f6.
  • Base size: set --btn-size. Every other dimension is in em, so the whole thing scales together. Currently previewing 18px.
  • Speed: change the duration on the transition or animation shorthand. Staggered delays are relative, so they keep their rhythm.
  • Reduced motion: the rule at the end of the CSS turns the motion off for people who asked their OS for less of it. Keep it.

Similar buttons