webdevpuneet.com

FAB Speed Dial Menu — Free HTML CSS JS Floating Action Button Snippet

FAB Speed Dial Menu · Buttons · Plain HTML, CSS & JS · Live preview

CategoryButtons

Copy & paste

Source Code

<div class="speed-dial" id="speedDial">
  <div class="dial-actions" id="dialActions">
    <button class="dial-item" style="--i: 0" onclick="handleAction('share')" aria-label="Share">
      <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="M8.6 10.6l6.8-3.8M8.6 13.4l6.8 3.8"/></svg>
      <span class="dial-tooltip">Share</span>
    </button>
    <button class="dial-item" style="--i: 1" onclick="handleAction('edit')" aria-label="Edit">
      <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>
      <span class="dial-tooltip">Edit</span>
    </button>
    <button class="dial-item danger" style="--i: 2" onclick="handleAction('delete')" aria-label="Delete">
      <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6M14 11v6"/></svg>
      <span class="dial-tooltip">Delete</span>
    </button>
  </div>

  <button class="dial-main" id="dialMain" onclick="toggleDial()" aria-label="Open actions menu" aria-expanded="false">
    <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
      <line x1="12" y1="5" x2="12" y2="19"/>
      <line x1="5" y1="12" x2="19" y2="12"/>
    </svg>
  </button>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Staggered cascade animation driven by a per-item CSS custom property and transition-delay
Main FAB icon rotates 45 degrees to visually become an X — no icon swap or extra markup
Hidden items use opacity, scale, and pointer-events together so they are unclickable while closed
Per-item hover tooltips keep icon-only buttons understandable without permanent labels
Closes on: clicking the main button again, clicking an action, or clicking outside the whole component
aria-expanded kept in sync with open state; every icon-only button has a descriptive aria-label
Any number of action items can be added just by giving each a unique --i stagger index
Pure CSS transitions — no animation library or JavaScript animation loop
Compact fixed footprint when closed — only the single 56px main button occupies space
No framework, no FAB component library, no build step required

About this UI Snippet

FAB Speed Dial Menu — HTML, CSS & JavaScript Floating Action Button

Screenshot of the FAB Speed Dial Menu snippet rendered live

A speed dial is a floating action button (FAB) that, instead of triggering a single action directly, expands into a small vertical stack of related actions — share, edit, delete — when tapped. It's a compact way to expose several secondary actions from one persistent, low-footprint control, popularized by Material Design and now common across mobile and web apps.

This snippet builds the full interaction in plain HTML, CSS, and vanilla JavaScript.

How the stagger animation works

Each mini action button (.dial-item) carries an inline custom property, style="--i: 0", --i: 1, --i: 2, numbering its position in the stack. Every item starts hidden — opacity: 0, transform: scale(0.4) translateY(10px), and pointer-events: none so it can't be clicked while invisible. When .speed-dial gets the .open class, all three items animate to full opacity and scale, but each one's transition-delay is set to calc(var(--i) * 0.05s) — so item 0 starts immediately, item 1 waits 50ms, item 2 waits 100ms. The result is a cascading stagger where the buttons pop in one after another rather than all snapping into view simultaneously.

How the icon-to-X rotation works

The main FAB shows a plus-sign SVG (two crossed lines) by default. A single CSS rule, .speed-dial.open .dial-main svg { transform: rotate(45deg) }, rotates that whole icon 45 degrees when the dial is open — a horizontal and vertical line rotated 45° together visually form an X, so no separate icon swap or extra markup is needed; it's the exact same SVG, just rotated.

How closing works

Three things close the dial: clicking the main FAB again (which just toggles the .open class off), clicking any action item (each calls handleAction(), which also calls toggleDial() after handling the action), and clicking anywhere outside the entire .speed-dial container — handled by a single document-level click listener that checks speedDial.contains(e.target).

Tooltips on each mini button

Each .dial-item contains a .dial-tooltip span positioned absolutely to its left, shown only on :hover, so small icon-only buttons still communicate their exact purpose without adding permanent visible labels that would clutter the compact FAB stack.

Accessibility

The main button's aria-expanded attribute is kept in sync with the dial's open state on every toggle, and each mini action button has a descriptive aria-label since its content is icon-only.

Step by step

How to Use

  1. 1
    Load the snippetClick "FAB Speed Dial Menu" in the sidebar Library tab. The preview shows a single indigo circular button in the bottom-right corner.
  2. 2
    Click the FABClick it to see three mini action buttons cascade upward with a staggered pop-in animation, and the plus icon rotate into an X.
  3. 3
    Click an action or outsideClick one of the mini buttons to trigger its action and auto-close the dial, or click anywhere else on the page to close it without acting.
  4. 4
    Add or remove actionsIn the HTML panel, add or remove .dial-item buttons, giving each a unique --i value in its inline style to control its stagger order.
  5. 5
    Wire up real actionsIn the JS panel, replace the console.log inside handleAction with real logic per action name, such as opening a share sheet or a confirmation dialog.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for React, or "Tailwind" for React + Tailwind CSS.

Real-world uses

Common Use Cases

FAB
Compact multi-action toolbars
Expose several secondary actions like share, edit, and delete from a single persistent floating button without cluttering the main UI.
Learn CSS custom-property-driven stagger animations
Study how a single --i variable per item, combined with calc() in transition-delay, produces a cascading animation with no per-item JavaScript timing code.
Prototype mobile-style action menus
Drop this into a dashboard or content-management prototype where a Material Design-style speed dial fits the product's interaction language.
Match your brand's FAB styling
Adjust the main button color, mini-button size, and stagger timing to fit your product's existing floating-action-button conventions.
Build accessible expandable icon menus
The aria-expanded and aria-label wiring here is a reusable reference for any icon-only expandable control elsewhere in your UI.
Wire actions into a real feature set
Replace the placeholder handleAction console.log with real navigation, API calls, or confirmation modals tied to your app's actual share/edit/delete features.
Related: Local Font Access Picker
See the Local Font Access Picker for a related buttons pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Each mini button has an inline CSS custom property like --i: 0, --i: 1, --i: 2. When the dial opens, every item transitions to visible, but its transition-delay is computed as calc(var(--i) * 0.05s) — so higher-indexed items start their animation slightly later, creating a cascade purely through CSS, with no setTimeout or animation-timing JavaScript involved.

It is the same SVG the whole time — two crossed lines forming a plus sign. A single CSS rule rotates that icon 45 degrees when the dial is open, and a plus rotated 45 degrees visually reads as an X. There is no icon swap or second SVG.

Copy an existing .dial-item button in the HTML panel, give it a new aria-label, icon, and tooltip text, and set its inline style to --i: 3 (continuing the sequence) so it staggers in after the third item.

Even though opacity: 0 makes an element invisible, it remains clickable and focusable by default. pointer-events: none additionally removes it from mouse interaction while hidden, preventing accidental clicks on an invisible button stacked behind the main FAB.

Three things: clicking the main FAB again toggles it closed directly; clicking any mini action button calls handleAction, which also closes the dial after running; and a document-level click listener closes it whenever a click lands outside the entire .speed-dial container.

Yes. Change flex-direction on .dial-actions and adjust the fixed positioning and translateY direction in .dial-item's initial transform to match — for example flex-direction: row and translateX instead of translateY for a horizontal dial.

The main button and each mini action are real <button> elements, reachable via Tab and activatable with Enter or Space, and aria-expanded on the main button reflects open/closed state. For full keyboard support, also consider closing the dial on Escape and moving focus into it when opened via keyboard.

Yes — the component makes no assumptions about the specific actions. Swap the icons, labels, and handleAction logic for any set of two to five short, related actions you want to expose from one compact floating control.