SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/floating-action-buttons

7 CSS Floating Action Buttons

3 Pure CSS4 with JSPublished 2026-10-05Free · no sign-up

Seven floating buttons for the main action on a screen: a classic round FAB, a speed dial with labelled mini actions, an extended button that shrinks as you scroll, a chat launcher with an unread badge, a back-to-top button, actions that fan out in an arc and a FAB docked in a bottom bar.

01. Classic Floating Action Button

FABMobile

A round button pinned to the bottom-right corner of a mock app screen with position: absolute; on a real page use position: fixed. It carries one primary action, has an aria-label because the plus icon has no text, and a shadow lifts it above the content.

View code
<div class="fab1"><div class="scr"><p>Notes</p><span></span><span></span><span></span></div><button aria-label="New note"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg></button></div>
.fab1 { box-sizing: border-box; position: relative; width: 240px; max-width: 100%; height: 300px; border: 1px solid #e4e7ec; border-radius: 22px; background: #fff; overflow: hidden; font-family: Inter, system-ui, sans-serif; }
.fab1 .scr { padding: 16px; } .fab1 p { margin: 0 0 12px; font-weight: 800; color: #101828; }
.fab1 .scr span { display: block; height: 44px; margin-bottom: 10px; border-radius: 10px; background: #f2f4f7; }
.fab1 button { position: absolute; right: 16px; bottom: 16px; display: grid; place-items: center; width: 56px; height: 56px; border: 0; border-radius: 18px; background: #2563eb; cursor: pointer; box-shadow: 0 8px 18px -6px rgba(37,99,235,.7); transition: transform .2s; }
.fab1 svg { width: 26px; height: 26px; stroke: #fff; stroke-width: 2.4; stroke-linecap: round; }
.fab1 button:hover { transform: scale(1.06); }
.fab1 button:focus-visible { outline: 3px solid #93c5fd; outline-offset: 3px; }

02. Speed Dial with Mini Actions

FABMenu

The main button opens a stack of three smaller labelled actions above it. aria-expanded on the toggle reports the state, the plus rotates into a cross, Escape closes the menu and returns focus to the toggle, and each mini action has a visible text label beside it.

View code
<div class="fab2"><ul id="fab2m" hidden><li><span>Photo</span><button aria-label="Add photo">📷</button></li><li><span>Link</span><button aria-label="Add link">🔗</button></li><li><span>Note</span><button aria-label="Add note">📝</button></li></ul><button class="main" aria-expanded="false" aria-controls="fab2m" aria-label="Add item"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg></button></div>
.fab2 { position: relative; width: 220px; max-width: 100%; height: 320px; font-family: Inter, system-ui, sans-serif; }
.fab2 ul { position: absolute; right: 6px; bottom: 74px; display: grid; gap: 10px; margin: 0; padding: 0; list-style: none; }
.fab2 li { display: flex; align-items: center; justify-content: flex-end; gap: 10px; animation: fab2 .2s ease both; }
.fab2 li:nth-child(2) { animation-delay: .04s; } .fab2 li:nth-child(1) { animation-delay: .08s; }
.fab2 li span { padding: 4px 8px; border-radius: 6px; background: #1f2740; color: #e2e8ff; font-size: 13px; }
.fab2 li button { width: 44px; height: 44px; border: 0; border-radius: 50%; background: #e0e7ff; font-size: 18px; cursor: pointer; }
.fab2 .main { position: absolute; right: 0; bottom: 0; display: grid; place-items: center; width: 58px; height: 58px; border: 0; border-radius: 50%; background: #6366f1; cursor: pointer; box-shadow: 0 8px 22px -6px #6366f1; }
.fab2 .main svg { width: 26px; height: 26px; stroke: #fff; stroke-width: 2.4; stroke-linecap: round; transition: transform .25s; }
.fab2 .main[aria-expanded="true"] svg { transform: rotate(45deg); }
.fab2 button:focus-visible { outline: 3px solid #c7d2fe; outline-offset: 3px; }
@keyframes fab2 { from { opacity: 0; transform: translateY(10px) scale(.9); } }
@media (prefers-reduced-motion: reduce) { .fab2 li { animation: none; } .fab2 .main svg { transition: none; } }
var m = document.querySelector('.fab2 .main'), list = document.getElementById('fab2m');
function set(open) { m.setAttribute('aria-expanded', open); list.hidden = !open; }
m.addEventListener('click', function () { set(m.getAttribute('aria-expanded') !== 'true'); });
document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && !list.hidden) { set(false); m.focus(); } });

03. Extended FAB That Shrinks on Scroll

FABScroll

An extended button with an icon and the word Compose, which collapses to just the icon once the list is scrolled and expands again at the top. The label stays in the button as text inside a collapsing span, so its accessible name never changes.

View code
<div class="fab3"><div class="list" tabindex="0" aria-label="Inbox"><p>Inbox</p><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div><button><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 20h4L19 9l-4-4L4 16z"/></svg><span class="l">Compose</span></button></div>
.fab3 { box-sizing: border-box; position: relative; width: 240px; max-width: 100%; height: 300px; border: 1px solid #e4e7ec; border-radius: 22px; background: #fff; overflow: hidden; font-family: Inter, system-ui, sans-serif; }
.fab3 .list { height: 100%; overflow-y: auto; padding: 16px; box-sizing: border-box; }
.fab3 .list:focus-visible { outline: 3px solid #93c5fd; outline-offset: -3px; }
.fab3 p { margin: 0 0 12px; font-weight: 800; color: #101828; }
.fab3 .list span { display: block; height: 52px; margin-bottom: 10px; border-radius: 10px; background: #f2f4f7; }
.fab3 button { position: absolute; right: 14px; bottom: 14px; display: flex; align-items: center; height: 54px; padding: 0 15px; border: 0; border-radius: 16px; background: #fde68a; color: #422006; font: 700 15px Inter, system-ui, sans-serif; cursor: pointer; box-shadow: 0 8px 18px -8px rgba(66,32,6,.5); }
.fab3 svg { width: 24px; height: 24px; flex: none; fill: none; stroke: currentColor; stroke-width: 2; stroke-linejoin: round; }
.fab3 .l { max-width: 90px; margin-left: 8px; overflow: hidden; white-space: nowrap; transition: max-width .25s ease, margin .25s ease; }
.fab3.small .l { max-width: 0; margin-left: 0; }
.fab3 button:focus-visible { outline: 3px solid #b45309; outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .fab3 .l { transition: none; } }
var w = document.querySelector('.fab3'), l = w.querySelector('.list');
l.addEventListener('scroll', function () { w.classList.toggle('small', l.scrollTop > 20); });

04. Chat Launcher with Unread Badge

FABChat

A support chat launcher with a red badge showing two unread messages. The badge is aria-hidden and the count is in the button's accessible name, Open chat, 2 unread messages, so it is not read as a stray number.

View code
<button class="fab4" aria-label="Open chat, 2 unread messages"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 5h16v11H9l-5 4z"/></svg><span class="b" aria-hidden="true">2</span></button>
.fab4 { position: relative; display: grid; place-items: center; width: 62px; height: 62px; border: 0; border-radius: 50%; background: #0f766e; cursor: pointer; box-shadow: 0 10px 24px -8px rgba(15,118,110,.7); }
.fab4 svg { width: 28px; height: 28px; fill: #fff; }
.fab4 .b { position: absolute; top: -2px; right: -2px; display: grid; place-items: center; min-width: 22px; height: 22px; padding: 0 5px; box-sizing: border-box; border: 2px solid #f3f5f9; border-radius: 11px; background: #dc2626; color: #fff; font: 800 12px Inter, system-ui, sans-serif; }
.fab4:focus-visible { outline: 3px solid #5eead4; outline-offset: 3px; }

05. Back to Top Button

FABScroll

A small button that fades in after the content has been scrolled and scrolls back to the top when pressed, smoothly unless reduced motion is requested. It is hidden with visibility as well as opacity, so it cannot be tabbed to while invisible.

View code
<div class="fab5"><div class="pg" tabindex="0" aria-label="Article"><h3>Field notes</h3><p>Scroll this panel to see the button appear.</p><i></i><i></i><i></i><i></i><i></i><i></i></div><button aria-label="Back to top"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 19V5M6 11l6-6 6 6"/></svg></button></div>
.fab5 { box-sizing: border-box; position: relative; width: 240px; max-width: 100%; height: 300px; border: 1px solid #28324f; border-radius: 18px; background: #121828; overflow: hidden; font-family: Inter, system-ui, sans-serif; }
.fab5 .pg { height: 100%; overflow-y: auto; padding: 16px; box-sizing: border-box; color: #cbd5f5; }
.fab5 .pg:focus-visible { outline: 2px solid #818cf8; outline-offset: -2px; }
.fab5 h3 { margin: 0 0 6px; color: #fff; } .fab5 p { margin: 0 0 12px; font-size: 14px; }
.fab5 i { display: block; height: 60px; margin-bottom: 10px; border-radius: 10px; background: #1b2338; }
.fab5 button { position: absolute; right: 14px; bottom: 14px; display: grid; place-items: center; width: 44px; height: 44px; border: 1px solid #3a4670; border-radius: 50%; background: #1e2640; cursor: pointer; opacity: 0; visibility: hidden; transform: translateY(8px); transition: opacity .25s, transform .25s, visibility .25s; }
.fab5.show button { opacity: 1; visibility: visible; transform: none; }
.fab5 svg { width: 20px; height: 20px; fill: none; stroke: #e0e7ff; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.fab5 button:focus-visible { outline: 2px solid #818cf8; outline-offset: 3px; }
var w = document.querySelector('.fab5'), pg = w.querySelector('.pg'), b = w.querySelector('button');
pg.addEventListener('scroll', function () { w.classList.toggle('show', pg.scrollTop > 60); });
b.addEventListener('click', function () { pg.scrollTo({ top: 0, behavior: matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth' }); pg.focus(); });

06. Actions That Fan Out in an Arc

FABRadial

Pressing the main button fans three actions out in a quarter circle. Each action's final position is set with a custom property angle and rotate-translate-rotate, so the icons stay upright. The actions are only focusable while open because the list is hidden when closed.

View code
<div class="fab6"><ul id="fab6m" hidden><li style="--a:0deg"><button aria-label="Share">↗</button></li><li style="--a:45deg"><button aria-label="Favourite">♥</button></li><li style="--a:90deg"><button aria-label="Download">↓</button></li></ul><button class="main" aria-expanded="false" aria-controls="fab6m" aria-label="More actions">+</button></div>
.fab6 { position: relative; width: 220px; max-width: 100%; height: 280px; font-family: Inter, system-ui, sans-serif; }
.fab6 ul { margin: 0; padding: 0; list-style: none; }
.fab6 li { position: absolute; right: 7px; bottom: 7px; transform: rotate(calc(-1 * var(--a))) translateX(-96px) rotate(var(--a)); animation: fab6 .25s ease both; }
.fab6 li button { width: 44px; height: 44px; border: 0; border-radius: 50%; background: #f472b6; color: #2a0a1a; font-size: 18px; font-weight: 800; cursor: pointer; }
.fab6 .main { position: absolute; right: 0; bottom: 0; width: 58px; height: 58px; border: 0; border-radius: 50%; background: #db2777; color: #fff; font-size: 30px; line-height: 1; cursor: pointer; transition: transform .25s; }
.fab6 .main[aria-expanded="true"] { transform: rotate(45deg); }
.fab6 button:focus-visible { outline: 3px solid #fbcfe8; outline-offset: 3px; }
@keyframes fab6 { from { transform: none; opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .fab6 li { animation: none; } .fab6 .main { transition: none; } }
var m = document.querySelector('.fab6 .main'), l = document.getElementById('fab6m');
m.addEventListener('click', function () { var o = m.getAttribute('aria-expanded') !== 'true'; m.setAttribute('aria-expanded', o); l.hidden = !o; });

07. FAB Docked in a Bottom Bar

FABNavigation

A bottom app bar with the action button sitting in a notch in the middle. The notch is a radial-gradient mask cut out of the bar, so the background shows through around the button, and the bar's links are a proper navigation landmark.

View code
<div class="fab7"><nav aria-label="App"><a href="#home" aria-current="page">Home</a><a href="#search">Search</a><span class="gap" aria-hidden="true"></span><a href="#saved">Saved</a><a href="#me">Me</a></nav><button aria-label="Scan a receipt">+</button></div>
.fab7 { position: relative; width: 300px; max-width: 100%; height: 110px; font-family: Inter, system-ui, sans-serif; }
.fab7 nav { position: absolute; left: 0; right: 0; bottom: 0; display: flex; align-items: center; justify-content: space-around; height: 62px; border-radius: 18px; background: #101828; -webkit-mask: radial-gradient(circle 36px at 50% 0, transparent 98%, #000); mask: radial-gradient(circle 36px at 50% 0, transparent 98%, #000); }
.fab7 a { color: #98a2b3; font-size: 13px; font-weight: 600; text-decoration: none; }
.fab7 a[aria-current] { color: #fff; }
.fab7 .gap { width: 56px; }
.fab7 button { position: absolute; left: 50%; bottom: 34px; transform: translateX(-50%); width: 58px; height: 58px; border: 0; border-radius: 50%; background: #f79009; color: #101828; font: 700 30px/1 Inter, system-ui, sans-serif; cursor: pointer; box-shadow: 0 8px 18px -6px rgba(247,144,9,.7); }
.fab7 a:focus-visible, .fab7 button:focus-visible { outline: 3px solid #84adff; outline-offset: 3px; border-radius: 6px; }

About these CSS Floating Action Buttons

One action, the most important one

A floating action button is for the single most common action on a screen, such as compose, add or chat. Two floating buttons compete with each other; secondary actions belong in a speed dial or the page itself.

Fixed in real pages

These demos use position: absolute inside a mock phone frame. On a real page use position: fixed with right and bottom offsets, plus env(safe-area-inset-bottom) so it clears the iPhone home indicator.

Name the icon

A plus or chat icon has no text, so the button needs an aria-label that says what it does, such as New note or Open chat, 2 unread messages.

Menus that open from a FAB

A speed dial is a disclosure: the main button has aria-expanded and aria-controls, the hidden actions are not focusable while closed, and Escape closes the menu and returns focus to the button.

Frequently asked questions

Will a fixed button cover my content?

It can. Add bottom padding to the page equal to the button's height plus its offset so the last content can scroll clear.

Do FABs belong on desktop?

They come from mobile design. On wide screens a normal button in the header or toolbar is often clearer.

How do I hide it while typing?

Hide it when an input is focused, or use the extended FAB pattern that shrinks on scroll.