34 CSS Floating Buttons
06 / 34

All 34 in 34 CSS Floating Buttons

  1. 01Material Design 3 Floating Action Button (FAB)
  2. 02iOS-Style Compose Floating Button
  3. 03WhatsApp Floating Message Button CSS
  4. 04Intercom / Drift Chat Bubble Floating Button
  5. 05Accessible WAI-ARIA Floating Action Button (WCAG 2.2)
  6. 06Extended FAB with Icon and Label (Material Design 3)
  7. 07Speed Dial Radial Menu FAB with Backdrop
  8. 08Pure CSS FAB Speed Dial (No JavaScript)
  9. 09Material Gradient Floating Action Button CSS
  10. 10Vertical and Horizontal Speed Dial FAB Menu CSS
  11. 11Morphing FAB Button That Expands to Pill CSS
  12. 12Scroll-to-Top Floating Button with SVG Progress Ring
  13. 13Floating Chat Widget FAB with Notification Panel
  14. 14Fan and Arc Radial Spread FAB Menu CSS
  15. 15Apple Squircle Superellipse Floating Button CSS
  16. 16Social Share Floating Button Cluster CSS
  17. 17Glassmorphism Floating Action Button with Backdrop Blur
  18. 18Cyberpunk Neon Glow Floating Button CSS
  19. 19Floating Compose Button with Tooltip on Hover CSS
  20. 20Draggable Floating Action Button with Corner Snap CSS
  21. 21Notification Bell Floating Button with Badge Counter
  22. 22Floating Cart Button with Live Badge Counter CSS
  23. 23Magnetic Cursor Follower Floating Button CSS
  24. 24Dark Mode Theme Toggle Floating Button CSS
  25. 25Classic Gradient Circle Plus FAB CSS
  26. 26Pulse Ring Attention-Getter Floating Button CSS
  27. 27Pill-Shaped FAB with Icon and Text Label CSS
  28. 28Neo-Brutalist Hard Shadow Floating Button CSS
  29. 29Floating Button with Loading Spinner State CSS
  30. 30Draggable Floating Button with 6-Dot Grip Handle CSS
  31. 31Voice Recording Microphone Floating Button CSS
  32. 32Aurora Gradient Drift Premium Floating Button CSS
  33. 33Quick Emoji Reply Reaction Floating Button CSS
  34. 34Modern Squircle Rounded Square FAB CSS
Light JSMIT licensed

Extended FAB with Icon and Label (Material Design 3)

Material's extended FAB — 56px tall, icon plus label, 16px radius — with the behaviour that makes it worth using: it collapses to an icon-only FAB as the user scrolls down and re-extends when they scroll back up, so the label sells the action on arrival and the button stops eating screen width once reading starts.

Live Demo
Try it

Nucleo: 44,000+ SVG icons in five families · All Icons Bundle $149

Nucleo · Affiliate link

Get the Nucleo bundle

The code

<section class="fb-06" aria-label="Extended floating action button demo">
  <div class="fb-06__frame">
    <div class="fb-06__scroll" id="fb-06-scroll" tabindex="0" aria-label="Scrollable article, scroll to collapse the button">
      <div class="fb-06__hero">
        <p class="fb-06__eyebrow">Material 3 · extended FAB</p>
        <h2 class="fb-06__title">Scroll down. Watch it shrink.</h2>
        <p class="fb-06__sub">The label sells the action on arrival, then gets out of the way once reading starts. Scroll back up and it returns.</p>
      </div>
      <article class="fb-06__body">
        <p>An extended FAB is 56 CSS pixels tall with a 16px corner radius, 16px of leading padding, 20px trailing, and a 12px gap between icon and label. Those numbers are the spec, and they are the difference between a Material button and a rounded rectangle.</p>
        <p>The collapse is direction-based, not position-based: sampling <code>scrollTop</code> against the previous frame with a small dead zone means a jittery trackpad never flaps the state, and a user who scrolls two pixels never sees the button twitch.</p>
        <p>Because the label is clipped rather than removed, the accessible name is stable in both states. A screen-reader user who tabs to the button after scrolling hears exactly the same thing as one who tabs to it at the top of the page.</p>
        <p>Keep the right edge pinned. If the pill shrinks from both sides, the tap target slides out from under a thumb that is already reaching for it — a small thing that feels broken without being nameable.</p>
        <p>The same technique drives search bars that collapse into an icon, save buttons that shrink after the first edit, and toolbars that condense on scroll. Once the grid track is doing the measuring, the state machine is trivial.</p>
      </article>
    </div>
    <button class="fb-06__efab" id="fb-06-efab" type="button" data-state="extended">
      <span class="fb-06__ico" aria-hidden="true"><svg viewBox="0 0 24 24" focusable="false"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25z" fill="currentColor"/><path d="M20.71 7.04a1 1 0 0 0 0-1.41l-2.34-2.34a1 1 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" fill="currentColor"/></svg></span>
      <span class="fb-06__label">Compose</span>
    </button>
  </div>
  <p class="fb-06__chip">grid track + padding transition · rAF scroll sampling</p>
</section>
.fb-06 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  background: var(--fb-06-bg);
  --fb-06-bg: oklch(0.96 0.014 265);
  --fb-06-container: oklch(0.55 0.18 265);
  --fb-06-on: oklch(0.99 0.01 265);
  --fb-06-ink: oklch(0.23 0.025 265);
  --fb-06-mut: oklch(0.53 0.02 265);
  --fb-06-card: oklch(1 0 0);
  font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
  color: var(--fb-06-ink);
  display: grid;
  place-items: center;
  align-content: center;
  gap: 16px;
  padding: clamp(18px,4vw,44px);
}

.fb-06 *,
.fb-06 *::before,
.fb-06 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.fb-06__frame {
  position: relative;
  width: min(100%,560px);
  height: min(72svh,620px);
  border-radius: 28px;
  overflow: hidden;
  background: var(--fb-06-card);
  border: 1px solid oklch(0.23 0.025 265/.1);
  box-shadow: 0 24px 60px -30px oklch(0.23 0.025 265/.7);
}

.fb-06__scroll {
  height: 100%;
  overflow: auto;
  scroll-behavior: smooth;
  padding: 0 0 92px;
  scrollbar-width: thin;
}

.fb-06__scroll:focus-visible {
  outline: 3px solid var(--fb-06-container);
  outline-offset: -3px;
}

.fb-06__hero {
  padding: clamp(26px,5vw,44px) clamp(20px,4vw,34px) 22px;
  display: grid;
  gap: 10px;
  background: linear-gradient(160deg,oklch(0.93 0.04 265),oklch(0.98 0.01 265));
}

.fb-06__eyebrow {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--fb-06-container);
}

.fb-06__title {
  font-size: clamp(24px,4vw,34px);
  line-height: 1.08;
  letter-spacing: -.03em;
  font-weight: 700;
  text-wrap: balance;
}

.fb-06__sub {
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--fb-06-mut);
  text-wrap: pretty;
}

.fb-06__body {
  display: grid;
  gap: 15px;
  padding: 22px clamp(20px,4vw,34px) 0;
}

.fb-06__body p {
  font-size: 14.5px;
  line-height: 1.72;
  color: oklch(0.36 0.02 265);
  text-wrap: pretty;
}

.fb-06__body code {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: .85em;
  background: oklch(0.55 0.18 265/.1);
  padding: 2px 6px;
  border-radius: 6px;
}

.fb-06__efab {
  position: absolute;
  right: 20px;
  bottom: 20px;
  z-index: 3;
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 12px;
  block-size: 56px;
  padding-inline: 16px 20px;
  border: 0;
  border-radius: 16px;
  background: var(--fb-06-container);
  color: var(--fb-06-on);
  font-size: 15px;
  font-weight: 640;
  letter-spacing: -.01em;
  cursor: pointer;
  box-shadow: 0 8px 24px -8px oklch(0.4 0.16 265/.8),0 2px 5px oklch(0.3 0.1 265/.28);
  transition: grid-template-columns .32s cubic-bezier(.2,.8,.2,1),gap .32s cubic-bezier(.2,.8,.2,1),padding-inline .32s cubic-bezier(.2,.8,.2,1),box-shadow .25s ease,transform .2s ease;
}

.fb-06__efab[data-state="collapsed"] {
  grid-template-columns: auto 0fr;
  gap: 0;
  padding-inline: 16px;
}

.fb-06__ico {
  display: grid;
  place-items: center;
}

.fb-06__ico svg {
  width: 24px;
  height: 24px;
}

.fb-06__label {
  overflow: hidden;
  white-space: nowrap;
}

.fb-06__efab:hover {
  box-shadow: 0 12px 30px -8px oklch(0.4 0.16 265/.85);
  transform: translateY(-1px);
}

.fb-06__efab:active {
  transform: scale(.96);
}

.fb-06__efab:focus-visible {
  outline: 3px solid oklch(0.4 0.16 265);
  outline-offset: 3px;
}

.fb-06__chip {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11px;
  color: var(--fb-06-mut);
  padding: 8px 14px;
  border: 1px solid oklch(0.23 0.025 265/.14);
  border-radius: 99px;
}

@media (prefers-reduced-motion: reduce) {
  .fb-06__efab {
    transition: none;
  }

  .fb-06__scroll {
    scroll-behavior: auto;
  }
}
(() => {
  const scroller = document.getElementById('fb-06-scroll');
  const efab = document.getElementById('fb-06-efab');
  if (!scroller || !efab || efab.dataset.fbWired) return;
  efab.dataset.fbWired = '1';
  let last = 0, ticking = false;
  const update = () => {
    const y = scroller.scrollTop;
    if (Math.abs(y - last) > 12) {
      efab.dataset.state = y > last && y > 40 ? 'collapsed' : 'extended';
      last = y;
    }
    ticking = false;
  };
  scroller.addEventListener('scroll', () => {
    if (!ticking) { ticking = true; requestAnimationFrame(update); }
  }, { passive: true });
  efab.addEventListener('click', () => {
    efab.dataset.state = efab.dataset.state === 'collapsed' ? 'extended' : 'collapsed';
  });
})();
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Floating Button from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Extended FAB with Icon and Label (Material Design 3)
Source: https://codefronts.com/components/css-floating-buttons/extended-fab-with-icon-and-label-material-design-3/

Material's extended FAB — 56px tall, icon plus label, 16px radius — with the behaviour that makes it worth using: it collapses to an icon-only FAB as the user scrolls down and re-extends when they scroll back up, so the label sells the action on arrival and the button stops eating screen width once reading starts.
## HTML
```html
<section class="fb-06" aria-label="Extended floating action button demo">
  <div class="fb-06__frame">
    <div class="fb-06__scroll" id="fb-06-scroll" tabindex="0" aria-label="Scrollable article, scroll to collapse the button">
      <div class="fb-06__hero">
        <p class="fb-06__eyebrow">Material 3 · extended FAB</p>
        <h2 class="fb-06__title">Scroll down. Watch it shrink.</h2>
        <p class="fb-06__sub">The label sells the action on arrival, then gets out of the way once reading starts. Scroll back up and it returns.</p>
      </div>
      <article class="fb-06__body">
        <p>An extended FAB is 56 CSS pixels tall with a 16px corner radius, 16px of leading padding, 20px trailing, and a 12px gap between icon and label. Those numbers are the spec, and they are the difference between a Material button and a rounded rectangle.</p>
        <p>The collapse is direction-based, not position-based: sampling <code>scrollTop</code> against the previous frame with a small dead zone means a jittery trackpad never flaps the state, and a user who scrolls two pixels never sees the button twitch.</p>
        <p>Because the label is clipped rather than removed, the accessible name is stable in both states. A screen-reader user who tabs to the button after scrolling hears exactly the same thing as one who tabs to it at the top of the page.</p>
        <p>Keep the right edge pinned. If the pill shrinks from both sides, the tap target slides out from under a thumb that is already reaching for it — a small thing that feels broken without being nameable.</p>
        <p>The same technique drives search bars that collapse into an icon, save buttons that shrink after the first edit, and toolbars that condense on scroll. Once the grid track is doing the measuring, the state machine is trivial.</p>
      </article>
    </div>
    <button class="fb-06__efab" id="fb-06-efab" type="button" data-state="extended">
      <span class="fb-06__ico" aria-hidden="true"><svg viewBox="0 0 24 24" focusable="false"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25z" fill="currentColor"/><path d="M20.71 7.04a1 1 0 0 0 0-1.41l-2.34-2.34a1 1 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" fill="currentColor"/></svg></span>
      <span class="fb-06__label">Compose</span>
    </button>
  </div>
  <p class="fb-06__chip">grid track + padding transition · rAF scroll sampling</p>
</section>
```
## CSS
```css
.fb-06 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  background: var(--fb-06-bg);
  --fb-06-bg: oklch(0.96 0.014 265);
  --fb-06-container: oklch(0.55 0.18 265);
  --fb-06-on: oklch(0.99 0.01 265);
  --fb-06-ink: oklch(0.23 0.025 265);
  --fb-06-mut: oklch(0.53 0.02 265);
  --fb-06-card: oklch(1 0 0);
  font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
  color: var(--fb-06-ink);
  display: grid;
  place-items: center;
  align-content: center;
  gap: 16px;
  padding: clamp(18px,4vw,44px);
}

.fb-06 *,
.fb-06 *::before,
.fb-06 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.fb-06__frame {
  position: relative;
  width: min(100%,560px);
  height: min(72svh,620px);
  border-radius: 28px;
  overflow: hidden;
  background: var(--fb-06-card);
  border: 1px solid oklch(0.23 0.025 265/.1);
  box-shadow: 0 24px 60px -30px oklch(0.23 0.025 265/.7);
}

.fb-06__scroll {
  height: 100%;
  overflow: auto;
  scroll-behavior: smooth;
  padding: 0 0 92px;
  scrollbar-width: thin;
}

.fb-06__scroll:focus-visible {
  outline: 3px solid var(--fb-06-container);
  outline-offset: -3px;
}

.fb-06__hero {
  padding: clamp(26px,5vw,44px) clamp(20px,4vw,34px) 22px;
  display: grid;
  gap: 10px;
  background: linear-gradient(160deg,oklch(0.93 0.04 265),oklch(0.98 0.01 265));
}

.fb-06__eyebrow {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--fb-06-container);
}

.fb-06__title {
  font-size: clamp(24px,4vw,34px);
  line-height: 1.08;
  letter-spacing: -.03em;
  font-weight: 700;
  text-wrap: balance;
}

.fb-06__sub {
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--fb-06-mut);
  text-wrap: pretty;
}

.fb-06__body {
  display: grid;
  gap: 15px;
  padding: 22px clamp(20px,4vw,34px) 0;
}

.fb-06__body p {
  font-size: 14.5px;
  line-height: 1.72;
  color: oklch(0.36 0.02 265);
  text-wrap: pretty;
}

.fb-06__body code {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: .85em;
  background: oklch(0.55 0.18 265/.1);
  padding: 2px 6px;
  border-radius: 6px;
}

.fb-06__efab {
  position: absolute;
  right: 20px;
  bottom: 20px;
  z-index: 3;
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 12px;
  block-size: 56px;
  padding-inline: 16px 20px;
  border: 0;
  border-radius: 16px;
  background: var(--fb-06-container);
  color: var(--fb-06-on);
  font-size: 15px;
  font-weight: 640;
  letter-spacing: -.01em;
  cursor: pointer;
  box-shadow: 0 8px 24px -8px oklch(0.4 0.16 265/.8),0 2px 5px oklch(0.3 0.1 265/.28);
  transition: grid-template-columns .32s cubic-bezier(.2,.8,.2,1),gap .32s cubic-bezier(.2,.8,.2,1),padding-inline .32s cubic-bezier(.2,.8,.2,1),box-shadow .25s ease,transform .2s ease;
}

.fb-06__efab[data-state="collapsed"] {
  grid-template-columns: auto 0fr;
  gap: 0;
  padding-inline: 16px;
}

.fb-06__ico {
  display: grid;
  place-items: center;
}

.fb-06__ico svg {
  width: 24px;
  height: 24px;
}

.fb-06__label {
  overflow: hidden;
  white-space: nowrap;
}

.fb-06__efab:hover {
  box-shadow: 0 12px 30px -8px oklch(0.4 0.16 265/.85);
  transform: translateY(-1px);
}

.fb-06__efab:active {
  transform: scale(.96);
}

.fb-06__efab:focus-visible {
  outline: 3px solid oklch(0.4 0.16 265);
  outline-offset: 3px;
}

.fb-06__chip {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11px;
  color: var(--fb-06-mut);
  padding: 8px 14px;
  border: 1px solid oklch(0.23 0.025 265/.14);
  border-radius: 99px;
}

@media (prefers-reduced-motion: reduce) {
  .fb-06__efab {
    transition: none;
  }

  .fb-06__scroll {
    scroll-behavior: auto;
  }
}
```

## JavaScript
```js
(() => {
  const scroller = document.getElementById('fb-06-scroll');
  const efab = document.getElementById('fb-06-efab');
  if (!scroller || !efab || efab.dataset.fbWired) return;
  efab.dataset.fbWired = '1';
  let last = 0, ticking = false;
  const update = () => {
    const y = scroller.scrollTop;
    if (Math.abs(y - last) > 12) {
      efab.dataset.state = y > last && y > 40 ? 'collapsed' : 'extended';
      last = y;
    }
    ticking = false;
  };
  scroller.addEventListener('scroll', () => {
    if (!ticking) { ticking = true; requestAnimationFrame(update); }
  }, { passive: true });
  efab.addEventListener('click', () => {
    efab.dataset.state = efab.dataset.state === 'collapsed' ? 'extended' : 'collapsed';
  });
})();
```

How this works

Same 0fr → 1fr grid track as a hover-expand pill, but driven by a data attribute that a scroll listener sets:

.efab{ display:grid; grid-template-columns:auto 1fr;
       transition:grid-template-columns .3s, gap .3s, padding-inline .3s }
.efab[data-state="collapsed"]{ grid-template-columns:auto 0fr; gap:0; padding-inline:16px }

The listener is deliberately dumb: compare current scroll to the last sample, apply a 12px dead zone so a trackpad wobble does not flap the state, and read it inside requestAnimationFrame so you never force layout on the scroll thread. Roughly ten lines, no library.

The label uses white-space:nowrap and lives in an overflow-clipped track, so it is present in the accessibility tree in both states. Icon-only FABs still need the label — collapsing the visual does not collapse the name.

Note the padding transition: an extended FAB is 16px/20px padding, an icon FAB is centred. Animating padding-inline alongside the track is what keeps the icon from sliding as the pill shrinks.

Make it yours

  • Collapse threshold: raise the 12px dead zone to 40px for a calmer button on long articles.
  • Lowered variant: M3 also defines a flat extended FAB for bottom app bars — drop the shadow, add a 1px outline in the container colour.
  • Always-extended: delete the scroll listener and the collapsed rule; the pill still hovers, presses and focuses correctly.
  • Two-line label: extended FABs are single-line by spec, but a 12px secondary line works if you raise the height to 64px and keep the icon vertically centred.

Gotchas — read before shipping

  • Do not collapse based on scroll position alone — collapse on direction. Position-based collapsing feels arbitrary to users who scroll a little and stop.
  • A collapsing FAB that also moves horizontally shifts the tap target under the user's thumb mid-scroll. Keep the right edge pinned and let the button grow leftward.
  • Never remove the label text on collapse; clip it. Removing it changes the accessible name mid-session.
  • Scroll listeners without rAF batching are a classic jank source on low-end Android. Sample, don't measure repeatedly.

Browser support

ChromeSafariFirefoxEdge
114+17.5+121+114+

Floor set by oklch(), text-wrap as this demo is written. The core technique itself goes back further — Chrome 111+.

Animatable grid tracks land in Firefox 127; earlier versions snap between states. Everything else is baseline. A CSS-only version using scroll-driven animations is possible in Chrome 115+ but is not yet cross-browser.

Techniques used in this demo

Search CodeFronts

Loading…