Back
Learn how to build beautiful, animated toggle buttons with icons for liking, bookmarking, and more.
<div class="toggle-buttons-container"> <!-- Like Toggle Button --> <div class="toggle-btn-wrapper"> <button class="icon-toggle-btn" id="like-button" aria-label="Like" aria-pressed="false"> <svg class="icon icon-default" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78L12 21.23l8.84-8.84a5.5 5.5 0 0 0 0-7.78z"></path> </svg> <svg class="icon icon-active" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78L12 21.23l8.84-8.84a5.5 5.5 0 0 0 0-7.78z"></path> </svg> <span class="toggle-label">Like</span> </button> </div> <!-- Additional toggle buttons follow the same pattern --> </div>Our HTML structure includes these key features: - Each toggle button is wrapped in a container for proper spacing - We use two SVG icons for each button: an outline version for the default state and a filled version for the active state - We include proper accessibility attributes (`aria-label` and `aria-pressed`) - Each button has a descriptive text label under the icon - We use unique IDs for each button to allow specific styling and behavior
document.addEventListener('DOMContentLoaded', () => { // Select all toggle buttons const toggleButtons = document.querySelectorAll('.icon-toggle-btn'); // Function to toggle the state of a button function toggleButtonState(button) { const isPressed = button.getAttribute('aria-pressed') === 'true'; const newState = !isPressed; // Update aria-pressed state button.setAttribute('aria-pressed', newState); // Add or remove active class for styling if (newState) { button.classList.add('active'); } else { button.classList.remove('active'); } // Get button ID to determine which icon is being toggled const buttonId = button.id; // You could trigger different actions based on the button id switch (buttonId) { case 'like-button': console.log(`User ${newState ? 'liked' : 'unliked'} the item`); break; case 'bookmark-button': console.log(`User ${newState ? 'saved' : 'removed'} the bookmark`); break; // Additional cases for other buttons } // Create and play animation if (newState) { button.classList.add('animate'); // Remove animation class after it completes setTimeout(() => { button.classList.remove('animate'); }, 700); // Match this to your CSS animation duration } } // Add click event listeners to all toggle buttons toggleButtons.forEach(button => { button.addEventListener('click', () => { toggleButtonState(button); }); // For accessibility: support keyboard activation button.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggleButtonState(button); } }); }); });Our JavaScript implementation provides: - A centralized function to handle the state toggle for all buttons - Proper updating of accessibility attributes when the state changes - Custom actions based on which button was toggled - Animation triggering when toggling to the active state - Support for keyboard navigation and activation - Clean handling of multiple toggle buttons on the same page
/* Icon toggle button base styles */ .icon-toggle-btn { display: flex; flex-direction: column; align-items: center; justify-content: center; background: transparent; border: none; border-radius: 8px; cursor: pointer; padding: 12px; transition: all 0.2s ease; position: relative; color: #64748b; /* Default icon color */ font-family: inherit; } /* Hide icons that shouldn't be visible */ .icon-toggle-btn .icon { opacity: 1; transition: opacity 0.2s ease, transform 0.2s ease; } .icon-toggle-btn .icon-active { position: absolute; top: 12px; opacity: 0; color: transparent; } /* Button hover and focus states */ .icon-toggle-btn:hover { background-color: #f1f5f9; color: #334155; } .icon-toggle-btn:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }Now let's style the active states of our buttons with different colors:
/* Active state */ .icon-toggle-btn.active { color: #3b82f6; /* Active color */ } .icon-toggle-btn.active .icon-default { opacity: 0; } .icon-toggle-btn.active .icon-active { opacity: 1; color: #3b82f6; } /* Button specific colors */ #like-button.active, #like-button.active .icon-active { color: #ef4444; /* Red for like */ } #bookmark-button.active, #bookmark-button.active .icon-active { color: #3b82f6; /* Blue for bookmark */ } #star-button.active, #star-button.active .icon-active { color: #eab308; /* Yellow for star */ } #notify-button.active, #notify-button.active .icon-active { color: #8b5cf6; /* Purple for notifications */ }Finally, let's add the animation for toggling and responsive styles:
/* Animation for toggling on */ @keyframes pulse { 0% { transform: scale(1); } 30% { transform: scale(0.8); } 60% { transform: scale(1.2); } 100% { transform: scale(1); } } .icon-toggle-btn.animate .icon-active { animation: pulse 0.5s ease-in-out; } /* Responsive styles */ @media (max-width: 480px) { .toggle-buttons-container { gap: 8px; } .toggle-btn-wrapper { min-width: 70px; } .icon-toggle-btn { padding: 8px; } .toggle-label { font-size: 12px; } }These icon toggle buttons provide several advantages: - Visual clarity: The combination of icon changes and color shifts clearly indicates state - Intuitive feedback: The animation provides immediate confirmation of the user's action - Compact design: The vertical layout with icons and labels uses space efficiently - Consistent system: The uniform design can be extended to any binary action - Accessibility: Proper ARIA attributes and keyboard support ensure all users can interact - Visual differentiation: Different colors for different actions help users understand the meaning These toggle buttons are perfect for social interactions (like/favorite), content management (save/bookmark), preference settings, or any feature that requires a binary state toggle with clear visual feedback.
<div class="toggle-buttons-container"> <!-- Like Toggle Button --> <div class="toggle-btn-wrapper"> <button class="icon-toggle-btn" id="like-button" aria-label="Like" aria-pressed="false"> <svg class="icon icon-default" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78L12 21.23l8.84-8.84a5.5 5.5 0 0 0 0-7.78z"></path> </svg> <svg class="icon icon-active" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78L12 21.23l8.84-8.84a5.5 5.5 0 0 0 0-7.78z"></path> </svg> <span class="toggle-label">Like</span> </button> </div> <!-- Bookmark Toggle Button --> <div class="toggle-btn-wrapper"> <button class="icon-toggle-btn" id="bookmark-button" aria-label="Bookmark" aria-pressed="false"> <svg class="icon icon-default" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path> </svg> <svg class="icon icon-active" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path> </svg> <span class="toggle-label">Save</span> </button> </div> <!-- Star Toggle Button --> <div class="toggle-btn-wrapper"> <button class="icon-toggle-btn" id="star-button" aria-label="Star" aria-pressed="false"> <svg class="icon icon-default" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon> </svg> <svg class="icon icon-active" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon> </svg> <span class="toggle-label">Star</span> </button> </div> <!-- Bell Toggle Button --> <div class="toggle-btn-wrapper"> <button class="icon-toggle-btn" id="notify-button" aria-label="Enable notifications" aria-pressed="false"> <svg class="icon icon-default" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path> <path d="M13.73 21a2 2 0 0 1-3.46 0"></path> </svg> <svg class="icon icon-active" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path> <path d="M13.73 21a2 2 0 0 1-3.46 0"></path> </svg> <span class="toggle-label">Notify</span> </button> </div> </div> <script> document.addEventListener('DOMContentLoaded', () => { // Select all toggle buttons const toggleButtons = document.querySelectorAll('.icon-toggle-btn'); // Function to toggle the state of a button function toggleButtonState(button) { const isPressed = button.getAttribute('aria-pressed') === 'true'; const newState = !isPressed; // Update aria-pressed state button.setAttribute('aria-pressed', newState); // Add or remove active class for styling if (newState) { button.classList.add('active'); } else { button.classList.remove('active'); } // Get button ID to determine which icon is being toggled const buttonId = button.id; // You could trigger different actions based on the button id switch (buttonId) { case 'like-button': console.log(`User ${newState ? 'liked' : 'unliked'} the item`); break; case 'bookmark-button': console.log(`User ${newState ? 'saved' : 'removed'} the bookmark`); break; case 'star-button': console.log(`User ${newState ? 'starred' : 'unstarred'} the item`); break; case 'notify-button': console.log(`User ${newState ? 'enabled' : 'disabled'} notifications`); break; } // Create and play animation if (newState) { button.classList.add('animate'); // Remove animation class after it completes setTimeout(() => { button.classList.remove('animate'); }, 700); // Match this to your CSS animation duration } } // Add click event listeners to all toggle buttons toggleButtons.forEach(button => { button.addEventListener('click', () => { toggleButtonState(button); }); // For accessibility: support keyboard activation button.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggleButtonState(button); } }); }); }); </script> <style> /* Container for toggle buttons */ .toggle-buttons-container { display: flex; flex-wrap: wrap; gap: 16px; max-width: 800px; margin: 0 auto; font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } /* Wrapper for each toggle button */ .toggle-btn-wrapper { display: flex; flex-direction: column; align-items: center; min-width: 90px; } /* Icon toggle button base styles */ .icon-toggle-btn { display: flex; flex-direction: column; align-items: center; justify-content: center; background: transparent; border: none; border-radius: 8px; cursor: pointer; padding: 12px; transition: all 0.2s ease; position: relative; color: #64748b; /* Default icon color */ font-family: inherit; } /* Hide icons that shouldn't be visible */ .icon-toggle-btn .icon { opacity: 1; transition: opacity 0.2s ease, transform 0.2s ease; } .icon-toggle-btn .icon-active { position: absolute; top: 12px; opacity: 0; color: transparent; } /* Button hover and focus states */ .icon-toggle-btn:hover { background-color: #f1f5f9; color: #334155; } .icon-toggle-btn:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; } /* Toggle label */ .toggle-label { margin-top: 4px; font-size: 14px; font-weight: 500; transition: color 0.2s ease; } /* Active state */ .icon-toggle-btn.active { color: #3b82f6; /* Active color */ } .icon-toggle-btn.active .icon-default { opacity: 0; } .icon-toggle-btn.active .icon-active { opacity: 1; color: #3b82f6; } /* Button specific colors */ #like-button.active, #like-button.active .icon-active { color: #ef4444; /* Red for like */ } #bookmark-button.active, #bookmark-button.active .icon-active { color: #3b82f6; /* Blue for bookmark */ } #star-button.active, #star-button.active .icon-active { color: #eab308; /* Yellow for star */ } #notify-button.active, #notify-button.active .icon-active { color: #8b5cf6; /* Purple for notifications */ } /* Animation for toggling on */ @keyframes pulse { 0% { transform: scale(1); } 30% { transform: scale(0.8); } 60% { transform: scale(1.2); } 100% { transform: scale(1); } } .icon-toggle-btn.animate .icon-active { animation: pulse 0.5s ease-in-out; } /* Responsive styles */ @media (max-width: 480px) { .toggle-buttons-container { gap: 8px; } .toggle-btn-wrapper { min-width: 70px; } .icon-toggle-btn { padding: 8px; } .toggle-label { font-size: 12px; } } </style>Thank you for reading this article.
Comments