webdevpuneet.com

Floating Action Button Radial Menu — HTML CSS JS

Floating Action Button Radial Menu · Buttons · Plain HTML, CSS & JS · Live preview

CategoryButtons

Copy & paste

Source Code

<div class="fab-demo">
  <div class="page-hint">Click the + button to expand the radial menu</div>

  <div class="fab-container" id="fabContainer">
    <!-- Action items (rendered behind main FAB) -->
    <div class="fab-item fab-item-1" data-label="Share" onclick="fabAction('Share')">
      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>
    </div>
    <div class="fab-item fab-item-2" data-label="Edit" onclick="fabAction('Edit')">
      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
    </div>
    <div class="fab-item fab-item-3" data-label="Upload" onclick="fabAction('Upload')">
      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><polyline points="16 16 12 12 8 16"/><line x1="12" y1="12" x2="12" y2="21"/><path d="M20.39 18.39A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.3"/></svg>
    </div>
    <div class="fab-item fab-item-4" data-label="Delete" onclick="fabAction('Delete')">
      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="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"/></svg>
    </div>
    <div class="fab-item fab-item-5" data-label="Bookmark" onclick="fabAction('Bookmark')">
      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
    </div>

    <!-- Main FAB -->
    <button class="fab-main" id="fabMain" onclick="toggleFab()" aria-label="Open actions" aria-expanded="false">
      <svg class="fab-icon-plus" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
      <svg class="fab-icon-close" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
    </button>

    <!-- Tooltip label -->
    <div class="fab-tooltip" id="fabTooltip"></div>
  </div>

  <!-- Backdrop -->
  <div class="fab-backdrop" id="fabBackdrop" onclick="toggleFab()"></div>

  <div class="fab-feedback" id="fabFeedback"></div>
</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

Radial arc positioning via hardcoded CSS translate pairs
Spring-staggered entrance with cubic-bezier(.34,1.56,.64,1) and delay increments
Plus-to-X icon crossfade with rotation on open
Backdrop blur overlay with click-to-dismiss
Dynamic tooltip positioning via getBoundingClientRect()
pointer-events: none on closed items prevents click-through
ARIA aria-expanded on the main button
Escape key keyboard dismiss

About this UI Snippet

Floating Action Button — Radial Arc Menu, Spring-Staggered Animation & Backdrop Blur

Screenshot of the Floating Action Button Radial Menu snippet rendered live

The Floating Action Button (FAB) is the centerpiece of Material Design — a persistent, elevated button that represents the primary action of a screen. This snippet extends the classic single FAB (see the simpler expanding FAB) into a radial menu: clicking the main button fans out five secondary action buttons in an arc pattern, each with a spring-animated entrance and a hover tooltip, overlaid on a blurred backdrop.

Radial arc positioning with CSS transform translate

The five action items all start at position: absolute; bottom: 0; right: 0 — stacked directly under the main FAB button. In their closed state, transform: scale(0) translate(6px,6px); opacity:0 makes them invisible. When the .open class is added to the container, each item gets a specific translate target that positions it along a 90° arc swept into the upper-left quadrant only: - Item 1: translate(0,-140px) — straight up - Item 2: translate(-54px,-129px) — 22.5° toward left - Item 3: translate(-99px,-99px) — upper-left diagonal (cos 45° × 140 ≈ 99) - Item 4: translate(-129px,-54px) — 67.5° toward left - Item 5: translate(-140px,0) — straight left

The 140px radius and 22.5° angle increments create a quarter-circle arc that only ever moves up and left — never down. That matters because the FAB is anchored to the bottom-right corner of the viewport: any item with a positive (downward) y-offset would be pushed toward or past the bottom edge of the screen and get clipped or hidden behind other fixed UI. Keeping every item's y-offset zero or negative guarantees all five stay fully visible regardless of how close to the bottom of the page the button sits. The radius is deliberately larger than the 44px item diameter would strictly require, so adjacent items clear each other with a visible gap instead of their circular hit areas overlapping. The positions are pure CSS — no JavaScript trigonometry needed because the arc positions are calculated once and hardcoded.

Spring stagger animation

Each item's transition uses cubic-bezier(.34,1.56,.64,1) — the spring easing — for both transform and opacity. The stagger is achieved with transition-delay values: item 1 at 40ms, item 2 at 80ms, item 3 at 120ms, item 4 at 160ms, item 5 at 200ms. This creates a wave of items appearing in sequence rather than all at once, making the radial expansion feel organic. The 40ms increment is just long enough to perceive as sequential but short enough to feel snappy overall.

Icon crossfade on open/close

The main FAB contains two SVG icons: a plus sign and an X. Both are position: absolute inside the button. The plus starts visible; the X starts with opacity:0; transform:rotate(-90deg) scale(.6). When open, the plus fades and rotates out (opacity:0; rotate(90deg)) while the X fades in and un-rotates to its normal position. This 90° rotation crossfade communicates the dual state of the button without requiring a text label.

Backdrop blur overlay

The backdrop is a position:fixed; inset:0 div with background:rgba(15,23,42,.3); backdrop-filter:blur(2px). When visible it dims and blurs the page content, visually isolating the FAB menu — the same backdrop technique used by the modal snippet. The backdrop also acts as a click target — clicking it closes the menu. The pointer-events:none on the hidden state prevents it from blocking interaction with the page when the FAB is closed.

Dynamic tooltip positioning

Rather than CSS-only tooltips, the tooltip element is repositioned in JavaScript using getBoundingClientRect() to get the hovered item's screen position, then setting the tooltip's right and bottom based on window.innerWidth - rect.right and window.innerHeight - rect.top. This ensures the tooltip always appears to the right of the arc item regardless of which item is hovered, without needing individual tooltip elements per item.

Keyboard and accessibility

The main button has aria-expanded toggled and aria-label="Open actions". Pressing Escape closes the menu. A production implementation would also add aria-label to each action item and manage focus trap behavior so keyboard users can navigate the arc items with Tab/arrow keys.

React export pattern

In React: const [open, setOpen] = useState(false). Each item is a mapped array of { id, icon, label, onClick } objects. The container div gets className={fab-container ${open ? 'open' : ''}}. The backdrop onClick handler calls setOpen(false). Since the arc positions are pure CSS classes (.fab-item-1 through .fab-item-5), the React component simply passes the index to generate the class name: className={fab-item fab-item-${i+1}}.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA purple FAB button appears in the bottom-right corner. The page background shows a hint text.
  2. 2
    Click the FABFive white icon buttons fan out in an arc pattern from the main button, each with a spring animation. A blurred backdrop appears.
  3. 3
    Hover over an action itemA tooltip appears near the item showing its label (Share, Edit, Upload, Delete, or Bookmark).
  4. 4
    Click an actionA confirmation message appears at the bottom of the screen. The menu collapses back.
  5. 5
    Dismiss with backdrop or EscapeClick the blurred backdrop or press Escape to close the menu without taking an action.
  6. 6
    Customize actionsReplace the SVG icons and data-label attributes. Update the onclick="fabAction('...')" calls with your own handlers.

Real-world uses

Common Use Cases

Mobile-style primary action hub
Mobile-first web apps use a FAB as the single "create" entry point — expanding into New Document, New Folder, Upload, and Scan options.
Canvas and drawing tools
Design tools place a FAB in the corner for shape, text, image, and component insertion — keeping the canvas clean while making tools accessible.
Developer tools quick actions
IDE extensions and dev dashboards use FABs for New File, Run, Debug, and Terminal actions — surfacing frequently used commands without toolbar clutter.
Project management screens
Kanban boards and task managers use a FAB to add new cards, columns, members, or attachments — one button that expands to the right context action.
Navigation shortcut menu
Single-page apps without a sidebar use a FAB menu for section navigation — Home, Search, Profile, Settings — accessible from any scroll position.
Related: Media Session API Controls
See the Media Session API Controls for a related buttons pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Adjust the translate values in the CSS. Keep the y-offset zero or negative for a bottom-right anchored FAB, otherwise items get pushed off the bottom of the screen. Divide your arc (e.g. 90°) by the number of items minus one, then calculate each offset using x = -radius * sin(angle) and y = -radius * cos(angle). For 4 items across a 90° upward-left arc with radius 140: (0,-140), (-99,-99), (-140,0) plus one more at the 30°/60° increment. Keep the radius comfortably larger than the item diameter so neighboring buttons don't overlap.

Remove the tooltip div. Add a <span class="fab-label"> inside each .fab-item. Style with position:absolute; right:52px; white-space:nowrap; font-size:12px; background:#1e293b; color:#fff; padding:4px 8px; border-radius:4px. The labels appear to the left of each icon in the arc.

Create <RadialFAB actions={[{ icon, label, onClick }]} />. Use useState(false) for open state. Map actions to <div className={fab-item fab-item-${i+1}} onClick={() => { onAction(i); setOpen(false); }}>. Pass className={fab-container ${open ? 'open' : ''}} to the container.

Track scroll direction with let lastY = 0 in a scroll event listener. When window.scrollY > lastY (scrolling down), add a hidden class with transform:translateY(100px); opacity:0 to .fab-container. When scrolling up, remove it. Update lastY each event.

Open the Export menu (or the Test Exports preview) in the snippet toolbar. It generates a plain React component, a React + Tailwind version where the button, radial-item, and scrim styles become utility classes, a Vue 3 single-file component, and an Angular standalone component. Each converter preserves the markup, the spring-staggered open animation, and the backdrop and keyboard-dismiss behaviour, so the speed-dial works identically across React, Vue, and Angular. Keep the open state in component state and render the action items from an array passed in as a prop or input rather than hardcoding the five buttons.