<main>
  <p>Floating action buttons as pop-ups. A button with <code>popover=manual</code> as the container button.</p>
  <ul>
    <li>Menu is opened with <code>popovertoggletarget</code>.</li>
    <li>Use <code>autofocus</code> to focus the first menu item on open.</li>
    <li>Use <code>:has()</code> to twist the icon on open.</li>
  </ul>
</main>
<button class="fab secondary" popover="manual" popovertarget="menu">
  <i class="material-icons">add</i>
</button>
<div id="menu" class="fab__menu" popover="auto" style="--count: 3;">
  <ul class="fab__menu-items">
    <li class="fab__menu-item">
      <button autofocus class="fab" style="--index: 0;" popoverhidetarget="menu">
        <i class="material-icons">chat</i>
      </button>
    </li>
    <li class="fab__menu-item">
      <button autofocus class="fab" style="--index: 1;" popoverhidetarget="menu">
        <i class="material-icons">photo_camera</i>
      </button>
    </li>
    <li class="fab__menu-item">
      <button autofocus class="fab" style="--index: 2;" popoverhidetarget="menu">
        <i class="material-icons">pin_drop</i>
      </button>
    </li>
  </ul>
</div>
@layer demo {
  .fab[popover] {
    height: var(--fab-size);
    width: var(--fab-size);
    border-radius: 50%;
    top: 100%;
    left: 100%;
    transform: translate(-150%, -150%);
    padding: 0;
    border: 0;
    margin: 0;
    background: var(--text-1);
    color: var(--surface-1);
  }

  .fab[popover] i {
    transition: transform var(--transition);
    transform: rotate(calc(var(--twist, 0) * 45deg));
  }

  main {
    display: grid;
    gap: var(--size-4);
    line-height: 1.5;
  }

  #menu {
    top: 50%;
    left: calc(100% - var(--fab-size));
    top: calc(100% - var(--fab-size));
    margin: 0;
    padding: 0;
    border: 0;
    transform: translate(-50%, calc(-100% - (var(--fab-size) * 0.5)));
    overflow: visible;
    background: transparent;
    padding: var(--size-6) 0;
  }

  #menu:open {
    --open: 1;
  }

  .fab__menu-items {
    gap: var(--size-4);
    list-style-type: none;
    display: grid;
    padding: 0;
    margin: 0;
  }

  .fab__menu-item {
    padding: 0;
  }

  #menu button {
    --tint-color: transparent;
    color: var(--text-2);
    background: var(--surface-2);
    outline-color: var(--text-2);
    border-radius: 50%;
    transition: transform var(--transition) calc(var(--index) * var(--step));
    transform: scale(var(--open, 0));
  }

  #menu:open button {
    transition-delay: calc((var(--count) - var(--index)) * var(--step));
  }
}

@layer base {
  :root {
    --step: 0.075s;
    --transition: 0.15s;
    --fab-size: calc(56 * 1.25px);
  }
  :root:has(#menu:open) {
    --twist: 1;
  }

  *,
  *:after,
  *:before {
    box-sizing: border-box;
  }

  body {
    min-height: 100vh;
    display: grid;
    place-items: center;
    gap: var(--size-4);
    font-family: "Google Sans", sans-serif, system-ui;
  }

  :where([popover]) {
    margin: auto;
    border-width: initial;
    border-style: solid;
  }
}
document.querySelector(".fab").showPopover();

External CSS

  1. https://codepen.io/web-dot-dev/pen/XWqWYgB.css
  2. https://codepen.io/web-dot-dev/pen/ZExZWBQ.css

External JavaScript

  1. https://codepen.io/web-dot-dev/pen/XWqWYgB.js
  2. https://codepen.io/web-dot-dev/pen/ZExZWBQ.js