Floating Action Button
Intermediate
Modern floating action button with smooth animations, expandable menu options, and elegant hover effects. Perfect for mobile-first applications and quick actions.
Live Preview
Code Implementation
HTML
<div class="fab-container">
<!-- Main FAB Button -->
<button class="fab-main" id="fabMain" aria-label="Main action menu">
<span class="fab-icon main-icon">+</span>
<span class="fab-icon close-icon">Γ</span>
</button>
<!-- FAB Menu Items -->
<div class="fab-menu" id="fabMenu">
<button class="fab-item" data-action="message" aria-label="Send message">
<span class="fab-icon">π¬</span>
<span class="fab-tooltip">Message</span>
</button>
<button class="fab-item" data-action="call" aria-label="Make a call">
<span class="fab-icon">π</span>
<span class="fab-tooltip">Call</span>
</button>
<button class="fab-item" data-action="email" aria-label="Send email">
<span class="fab-icon">βοΈ</span>
<span class="fab-tooltip">Email</span>
</button>
<button class="fab-item" data-action="share" aria-label="Share content">
<span class="fab-icon">π</span>
<span class="fab-tooltip">Share</span>
</button>
</div>
<!-- Background Overlay -->
<div class="fab-overlay" id="fabOverlay"></div>
</div>
CSS
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.fab-container {
position: fixed;
bottom: 2rem;
right: 2rem;
z-index: 1000;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.fab-main {
width: 56px;
height: 56px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
z-index: 1001;
overflow: hidden;
}
.fab-main::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
opacity: 0;
transition: opacity 0.3s ease;
border-radius: 50%;
}
.fab-main:hover::before {
opacity: 1;
}
.fab-main:hover {
transform: scale(1.1);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
}
.fab-main:active {
transform: scale(0.95);
}
.fab-icon {
font-size: 1.5rem;
color: white;
transition: all 0.3s ease;
position: relative;
z-index: 1;
}
.main-icon {
transform: rotate(0deg);
}
.close-icon {
position: absolute;
transform: rotate(45deg) scale(0);
opacity: 0;
}
.fab-main.active .main-icon {
transform: rotate(45deg) scale(0);
opacity: 0;
}
.fab-main.active .close-icon {
transform: rotate(0deg) scale(1);
opacity: 1;
}
.fab-menu {
position: absolute;
bottom: 70px;
right: 0;
display: flex;
flex-direction: column;
gap: 1rem;
opacity: 0;
visibility: hidden;
transform: translateY(20px);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.fab-menu.active {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.fab-item {
width: 48px;
height: 48px;
border-radius: 50%;
background: white;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
transform: scale(0);
animation-fill-mode: forwards;
}
.fab-menu.active .fab-item {
animation: fabItemAppear 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
.fab-menu.active .fab-item:nth-child(1) {
animation-delay: 0.1s;
}
.fab-menu.active .fab-item:nth-child(2) {
animation-delay: 0.15s;
}
.fab-menu.active .fab-item:nth-child(3) {
animation-delay: 0.2s;
}
.fab-menu.active .fab-item:nth-child(4) {
animation-delay: 0.25s;
}
@keyframes fabItemAppear {
0% {
transform: scale(0) rotate(-180deg);
opacity: 0;
}
100% {
transform: scale(1) rotate(0deg);
opacity: 1;
}
}
.fab-item:hover {
transform: scale(1.1);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
background: #f8f9fa;
}
.fab-item:active {
transform: scale(0.95);
}
.fab-item .fab-icon {
font-size: 1.2rem;
color: #333;
}
.fab-tooltip {
position: absolute;
right: 60px;
top: 50%;
transform: translateY(-50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 0.5rem 0.75rem;
border-radius: 4px;
font-size: 0.85rem;
white-space: nowrap;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
pointer-events: none;
}
.fab-tooltip::after {
content: '';
position: absolute;
top: 50%;
left: 100%;
transform: translateY(-50%);
border: 5px solid transparent;
border-left-color: rgba(0, 0, 0, 0.8);
}
.fab-item:hover .fab-tooltip {
opacity: 1;
visibility: visible;
transform: translateY(-50%) translateX(-5px);
}
.fab-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.3);
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
z-index: 999;
}
.fab-overlay.active {
opacity: 1;
visibility: visible;
}
/* Ripple Effect */
.fab-main::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.3);
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
}
.fab-main:active::after {
width: 120px;
height: 120px;
}
/* Pulse Animation */
@keyframes pulse {
0% {
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4), 0 0 0 0 rgba(102, 126, 234, 0.7);
}
70% {
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4), 0 0 0 10px rgba(102, 126, 234, 0);
}
100% {
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4), 0 0 0 0 rgba(102, 126, 234, 0);
}
}
.fab-main.pulse {
animation: pulse 2s infinite;
}
/* Mobile Responsive */
@media (max-width: 768px) {
.fab-container {
bottom: 1.5rem;
right: 1.5rem;
}
.fab-main {
width: 52px;
height: 52px;
}
.fab-item {
width: 44px;
height: 44px;
}
.fab-tooltip {
font-size: 0.8rem;
padding: 0.4rem 0.6rem;
}
}
/* Dark Mode Support */
@media (prefers-color-scheme: dark) {
.fab-item {
background: #2d3748;
color: white;
}
.fab-item .fab-icon {
color: white;
}
.fab-item:hover {
background: #4a5568;
}
}
JavaScript
class FloatingActionButton {
constructor() {
this.fabMain = document.getElementById('fabMain');
this.fabMenu = document.getElementById('fabMenu');
this.fabOverlay = document.getElementById('fabOverlay');
this.fabItems = document.querySelectorAll('.fab-item');
this.isOpen = false;
this.init();
}
init() {
this.setupEventListeners();
this.setupKeyboardNavigation();
this.addPulseEffect();
}
setupEventListeners() {
// Main FAB click
this.fabMain.addEventListener('click', () => {
this.toggle();
});
// Overlay click to close
this.fabOverlay.addEventListener('click', () => {
this.close();
});
// FAB item clicks
this.fabItems.forEach(item => {
item.addEventListener('click', (e) => {
this.handleItemClick(e);
});
});
// Close on escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && this.isOpen) {
this.close();
}
});
// Close when clicking outside
document.addEventListener('click', (e) => {
if (!e.target.closest('.fab-container') && this.isOpen) {
this.close();
}
});
}
setupKeyboardNavigation() {
// Make FAB items focusable
this.fabItems.forEach((item, index) => {
item.setAttribute('tabindex', '-1');
item.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
this.handleItemClick(e);
} else if (e.key === 'ArrowUp') {
e.preventDefault();
this.focusPreviousItem(index);
} else if (e.key === 'ArrowDown') {
e.preventDefault();
this.focusNextItem(index);
}
});
});
}
addPulseEffect() {
// Add pulse effect on first load
setTimeout(() => {
this.fabMain.classList.add('pulse');
setTimeout(() => {
this.fabMain.classList.remove('pulse');
}, 4000);
}, 1000);
}
toggle() {
if (this.isOpen) {
this.close();
} else {
this.open();
}
}
open() {
this.isOpen = true;
this.fabMain.classList.add('active');
this.fabMenu.classList.add('active');
this.fabOverlay.classList.add('active');
// Set focus to first menu item
setTimeout(() => {
this.fabItems[0].setAttribute('tabindex', '0');
this.fabItems[0].focus();
}, 100);
// Announce to screen readers
this.announceToScreenReader('Menu opened');
}
close() {
this.isOpen = false;
this.fabMain.classList.remove('active');
this.fabMenu.classList.remove('active');
this.fabOverlay.classList.remove('active');
// Remove focus from menu items
this.fabItems.forEach(item => {
item.setAttribute('tabindex', '-1');
});
// Return focus to main button
this.fabMain.focus();
// Announce to screen readers
this.announceToScreenReader('Menu closed');
}
handleItemClick(e) {
const action = e.currentTarget.getAttribute('data-action');
const actionName = e.currentTarget.getAttribute('aria-label');
// Add click animation
e.currentTarget.style.transform = 'scale(0.9)';
setTimeout(() => {
e.currentTarget.style.transform = '';
}, 150);
// Handle different actions
switch (action) {
case 'message':
this.handleMessage();
break;
case 'call':
this.handleCall();
break;
case 'email':
this.handleEmail();
break;
case 'share':
this.handleShare();
break;
default:
console.log(`Action: ${action}`);
}
// Announce action to screen readers
this.announceToScreenReader(`${actionName} selected`);
// Close menu after action
setTimeout(() => {
this.close();
}, 200);
}
handleMessage() {
// Simulate opening message interface
console.log('Opening message interface...');
this.showNotification('Message interface opened', 'π¬');
}
handleCall() {
// Simulate making a call
console.log('Initiating call...');
this.showNotification('Call initiated', 'π');
}
handleEmail() {
// Simulate opening email
console.log('Opening email client...');
this.showNotification('Email client opened', 'βοΈ');
}
handleShare() {
// Simulate sharing
if (navigator.share) {
navigator.share({
title: 'Floating Action Button Demo',
text: 'Check out this awesome FAB component!',
url: window.location.href
}).catch(console.error);
} else {
// Fallback for browsers without Web Share API
this.copyToClipboard(window.location.href);
this.showNotification('Link copied to clipboard', 'π');
}
}
copyToClipboard(text) {
if (navigator.clipboard) {
navigator.clipboard.writeText(text);
} else {
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
}
}
showNotification(message, icon) {
// Create and show a temporary notification
const notification = document.createElement('div');
notification.style.cssText = `
position: fixed;
top: 2rem;
right: 2rem;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 1rem 1.5rem;
border-radius: 8px;
display: flex;
align-items: center;
gap: 0.5rem;
z-index: 1002;
animation: slideInRight 0.3s ease;
`;
notification.innerHTML = `
<span style="font-size: 1.2rem;">${icon}</span>
<span>${message}</span>
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.animation = 'slideOutRight 0.3s ease forwards';
setTimeout(() => {
document.body.removeChild(notification);
}, 300);
}, 3000);
}
focusNextItem(currentIndex) {
const nextIndex = (currentIndex + 1) % this.fabItems.length;
this.fabItems[currentIndex].setAttribute('tabindex', '-1');
this.fabItems[nextIndex].setAttribute('tabindex', '0');
this.fabItems[nextIndex].focus();
}
focusPreviousItem(currentIndex) {
const prevIndex = currentIndex === 0 ? this.fabItems.length - 1 : currentIndex - 1;
this.fabItems[currentIndex].setAttribute('tabindex', '-1');
this.fabItems[prevIndex].setAttribute('tabindex', '0');
this.fabItems[prevIndex].focus();
}
announceToScreenReader(message) {
const announcement = document.createElement('div');
announcement.setAttribute('aria-live', 'polite');
announcement.setAttribute('aria-atomic', 'true');
announcement.style.cssText = `
position: absolute;
left: -10000px;
width: 1px;
height: 1px;
overflow: hidden;
`;
announcement.textContent = message;
document.body.appendChild(announcement);
setTimeout(() => {
document.body.removeChild(announcement);
}, 1000);
}
}
// Add CSS animations for notifications
const style = document.createElement('style');
style.textContent = `
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOutRight {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
`;
document.head.appendChild(style);
// Initialize the FAB
document.addEventListener('DOMContentLoaded', () => {
new FloatingActionButton();
});