32 CSS Floating Action Button Designs 06 / 32

Fan Arc FAB Menu

Fan and arc style FAB menu with radial spread using JS angle math, available in two demo layouts.

CSS + JS MIT licensed
Live Demo Open in tab
Open in playground

The code

<div class="fb06">
<h1>Fan Arc FAB Menu</h1>
<p class="fb06-sub">Radial floating action button menu — actions spread in a configurable arc on click. CSS transitions + a tiny 15-line JS layout engine.</p>

<div class="fb06-demos">

  <!-- Demo 1: Upward fan (90°) -->
  <div class="fb06-well" id="fb06-well1">
    <span class="fb06-well-tag">Upward 90° arc · 5 items</span>

    <div class="fb06-fan-fab" id="fb06-fan1" data-arc="90" data-start="135" data-radius="88">
      <div class="fb06-fan-item fb06-closed" data-index="0">
        <button class="fb06-fan-btn fb06-fan-btn--a" aria-label="Share">
          <svg viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11A3 3 0 0021 5a3 3 0 00-3-3 3 3 0 00-3 3c0 .24.04.47.09.7L8.04 9.81A3 3 0 005 9a3 3 0 00-3 3 3 3 0 003 3 3 3 0 003.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92S19.61 16.08 18 16.08z"/></svg>
        </button>
        <span class="fb06-fan-lbl">Share</span>
      </div>
      <div class="fb06-fan-item fb06-closed" data-index="1">
        <button class="fb06-fan-btn fb06-fan-btn--b" aria-label="Camera">
          <svg viewBox="0 0 24 24"><path d="M12 15.2A3.2 3.2 0 018.8 12 3.2 3.2 0 0112 8.8a3.2 3.2 0 013.2 3.2A3.2 3.2 0 0112 15.2M12 7a5 5 0 00-5 5 5 5 0 005 5 5 5 0 005-5 5 5 0 00-5-5m6.5-1H5.5A2.5 2.5 0 003 8.5V19a2.5 2.5 0 002.5 2.5h13A2.5 2.5 0 0021 19V8.5A2.5 2.5 0 0018.5 6zM9 4l.83-1H14.2L15 4H9z"/></svg>
        </button>
        <span class="fb06-fan-lbl">Camera</span>
      </div>
      <div class="fb06-fan-item fb06-closed" data-index="2">
        <button class="fb06-fan-btn fb06-fan-btn--c" aria-label="Bookmark">
          <svg viewBox="0 0 24 24"><path d="M17 3H7a2 2 0 00-2 2v16l7-3 7 3V5a2 2 0 00-2-2z"/></svg>
        </button>
        <span class="fb06-fan-lbl">Bookmark</span>
      </div>
      <div class="fb06-fan-item fb06-closed" data-index="3">
        <button class="fb06-fan-btn fb06-fan-btn--d" aria-label="Favorite">
          <svg viewBox="0 0 24 24"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5A5.5 5.5 0 0112 5.09 5.5 5.5 0 0122 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
        </button>
        <span class="fb06-fan-lbl">Like</span>
      </div>
      <div class="fb06-fan-item fb06-closed" data-index="4">
        <button class="fb06-fan-btn fb06-fan-btn--e" aria-label="More">
          <svg viewBox="0 0 24 24"><path d="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></svg>
        </button>
        <span class="fb06-fan-lbl">More</span>
      </div>

      <button class="fb06-fan-trigger" aria-label="Toggle actions" aria-expanded="false">
        <svg viewBox="0 0 24 24"><path d="M19 11h-6V5h-2v6H5v2h6v6h2v-6h6z"/></svg>
      </button>
    </div>
  </div>

  <!-- Demo 2: Corner fan (positioned like a real FAB) -->
  <div class="fb06-well fb06-corner-well" id="fb06-well2">
    <span class="fb06-well-tag">Corner placement · 4 items · 120° arc</span>

    <div class="fb06-fan-fab fb06-fan-fab--corner" id="fb06-fan2" data-arc="120" data-start="180" data-radius="90">
      <div class="fb06-fan-item fb06-closed" data-index="0">
        <button class="fb06-fan-btn fb06-fan-btn--a" aria-label="New post">
          <svg viewBox="0 0 24 24"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1 1 0 000-1.41l-2.34-2.34a1 1 0 00-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg>
        </button>
        <span class="fb06-fan-lbl">Edit</span>
      </div>
      <div class="fb06-fan-item fb06-closed" data-index="1">
        <button class="fb06-fan-btn fb06-fan-btn--c" aria-label="Attach">
          <svg viewBox="0 0 24 24"><path d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5a2.5 2.5 0 015 0v10.5c0 .83-.67 1.5-1.5 1.5s-1.5-.67-1.5-1.5V6H9v9.5a3.5 3.5 0 007 0V5c0-2.76-2.24-5-5-5S6 2.24 6 5v12.5c0 3.87 3.13 7 7 7s7-3.13 7-7V6h-3.5z"/></svg>
        </button>
        <span class="fb06-fan-lbl">Attach</span>
      </div>
      <div class="fb06-fan-item fb06-closed" data-index="2">
        <button class="fb06-fan-btn fb06-fan-btn--b" aria-label="Gallery">
          <svg viewBox="0 0 24 24"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>
        </button>
        <span class="fb06-fan-lbl">Gallery</span>
      </div>
      <div class="fb06-fan-item fb06-closed" data-index="3">
        <button class="fb06-fan-btn fb06-fan-btn--e" aria-label="Location">
          <svg viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 010-5 2.5 2.5 0 010 5z"/></svg>
        </button>
        <span class="fb06-fan-lbl">Location</span>
      </div>

      <button class="fb06-fan-trigger" style="background:linear-gradient(135deg,#8b5cf6,#d946ef);box-shadow:0 6px 22px rgba(139,92,246,.45),inset 0 1px 0 rgba(255,255,255,.18)" aria-label="Toggle actions" aria-expanded="false">
        <svg viewBox="0 0 24 24"><path d="M19 11h-6V5h-2v6H5v2h6v6h2v-6h6z"/></svg>
      </button>
    </div>
  </div>

</div>
</div>
.fb06, .fb06 *, .fb06 *::before, .fb06 *::after { box-sizing: border-box; margin: 0; padding: 0; }
.fb06 {
    font-family: 'Plus Jakarta Sans', sans-serif;
    background: #0c0c10;
    min-height: 100vh;
    display: grid;
    place-items: center;
    padding: 40px 20px;
    color: #e4e4e7;
  }

  h1 {
    font-size: clamp(1.6rem, 5vw, 2.4rem);
    font-weight: 700;
    text-align: center;
    letter-spacing: -.022em;
    margin-bottom: 8px;
  }
  p.fb06-sub {
    text-align: center;
    color: #52525b;
    font-size: .9rem;
    max-width: 42ch;
    margin: 0 auto 60px;
    line-height: 1.6;
  }

  .fb06-demos {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 24px;
    width: min(880px, 100%);
  }

  .fb06-well {
    background: #141418;
    border: 1px solid #27272a;
    border-radius: 22px;
    height: 400px;
    position: relative;
    overflow: hidden;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding: 40px;
  }
  .fb06-well-tag {
    position: absolute;
    top: 16px;
    left: 18px;
    font-size: .68rem;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: #3f3f46;
  }

  /* ══ FAN FAB ══ */
  .fb06-fan-fab {
    position: relative;
    width: 62px;
    height: 62px;
  }

  /* main trigger */
  .fb06-fan-trigger {
    position: relative;
    z-index: 10;
    width: 62px;
    height: 62px;
    border-radius: 50%;
    border: none;
    cursor: pointer;
    background: linear-gradient(135deg, #f97316, #ea580c);
    box-shadow: 0 6px 22px rgba(249,115,22,.45), inset 0 1px 0 rgba(255,255,255,.18);
    display: grid;
    place-items: center;
    transition: transform .25s cubic-bezier(.34,1.56,.64,1), box-shadow .2s;
  }
  .fb06-fan-trigger:hover { box-shadow: 0 10px 30px rgba(249,115,22,.6), inset 0 1px 0 rgba(255,255,255,.18); }
  .fb06-fan-trigger.fb06-open { transform: rotate(45deg); }
  .fb06-fan-trigger svg { width: 26px; height: 26px; fill: #fff; pointer-events: none; }

  /* action items */
  .fb06-fan-item {
    position: absolute;
    bottom: 0;
    left: 50%;
    transform-origin: center 31px; /* half of 62px = pivot at btn center */
    transition:
      transform .3s cubic-bezier(.34,1.56,.64,1),
      opacity .22s ease;
    opacity: 0;
  }
  /* closed: all stacked under trigger */
  .fb06-fan-item.fb06-closed { transform: translateX(-50%) translateY(0) scale(.5); }
  /* open: spread via CSS custom properties set by JS */
  .fb06-fan-item.fb06-open {
    opacity: 1;
  }

  .fb06-fan-btn {
    width: 50px;
    height: 50px;
    border-radius: 50%;
    border: none;
    cursor: pointer;
    display: grid;
    place-items: center;
    box-shadow: 0 4px 14px rgba(0,0,0,.5);
    transition: transform .15s ease;
    position: relative;
    transform: translateX(-50%);
  }
  .fb06-fan-btn:hover { transform: translateX(-50%) scale(1.15); }
  .fb06-fan-btn svg { width: 20px; height: 20px; fill: #fff; }

  /* label tooltip */
  .fb06-fan-lbl {
    position: absolute;
    right: calc(100% + 10px);
    top: 50%;
    transform: translateY(-50%);
    background: #1f1f24;
    border: 1px solid #3f3f46;
    color: #a1a1aa;
    font-size: .72rem;
    font-weight: 600;
    padding: 4px 10px;
    border-radius: 6px;
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transition: opacity .15s ease;
  }
  .fb06-fan-btn:hover + .fb06-fan-lbl,
  .fb06-fan-btn:focus + .fb06-fan-lbl { opacity: 1; }

  /* color variants */
  .fb06-fan-btn--a { background: linear-gradient(135deg, #6366f1, #8b5cf6); box-shadow: 0 4px 14px rgba(99,102,241,.5); }
  .fb06-fan-btn--b { background: linear-gradient(135deg, #06b6d4, #0ea5e9); box-shadow: 0 4px 14px rgba(6,182,212,.5); }
  .fb06-fan-btn--c { background: linear-gradient(135deg, #10b981, #059669); box-shadow: 0 4px 14px rgba(16,185,129,.5); }
  .fb06-fan-btn--d { background: linear-gradient(135deg, #f43f5e, #e11d48); box-shadow: 0 4px 14px rgba(244,63,94,.5); }
  .fb06-fan-btn--e { background: linear-gradient(135deg, #f59e0b, #d97706); box-shadow: 0 4px 14px rgba(245,158,11,.5); }

  /* ── Grid of demo variants ── */
  .fb06-corner-well {
    position: relative;
    overflow: hidden;
  }
  .fb06-fan-fab--corner {
    position: absolute;
    bottom: 28px;
    right: 28px;
  }

  @media (prefers-reduced-motion: reduce) {
    .fb06-fan-item, .fb06-fan-trigger { transition: none; }
  }
document.querySelectorAll('.fb06-fan-fab').forEach(fab => {
    const arc      = parseFloat(fab.dataset.arc);    // total degrees
    const start    = parseFloat(fab.dataset.start);  // starting angle (deg)
    const radius   = parseFloat(fab.dataset.radius);
    const items    = Array.from(fab.querySelectorAll('.fb06-fan-item'));
    const trigger  = fab.querySelector('.fb06-fan-trigger');
    let isOpen = false;
    const count = items.length;

    function openFan() {
      items.forEach((item, i) => {
        const angleDeg = count > 1
          ? start - (arc / (count - 1)) * i
          : start;
        const rad = angleDeg * Math.PI / 180;
        const x = Math.cos(rad) * radius;
        const y = Math.sin(rad) * radius;
        item.classList.remove('fb06-closed');
        item.classList.add('fb06-open');
        item.style.transitionDelay = (i * 0.04) + 's';
        item.style.transform = `translateX(calc(-50% + ${x}px)) translateY(${-y}px) scale(1)`;
      });
    }

    function closeFan() {
      items.forEach((item, i) => {
        item.classList.remove('fb06-open');
        item.classList.add('fb06-closed');
        item.style.transitionDelay = ((count - 1 - i) * 0.03) + 's';
        item.style.transform = '';
      });
    }

    trigger.addEventListener('click', () => {
      isOpen = !isOpen;
      trigger.classList.toggle('fb06-open', isOpen);
      trigger.setAttribute('aria-expanded', isOpen);
      isOpen ? openFan() : closeFan();
    });
  });

Search CodeFronts

Loading…