SRH001

Animated Search Bar

A modern search input with animated suggestions and micro-interactions

JavaScript
Font Awesome Included: This snippet uses Font Awesome icons. The required stylesheet is automatically included at the top of the code block. If you are going to use this snippet in your project, add the stylesheet to your <head>.

Live Preview

Code

HTML
<!-- Font Awesome Icons; Make sure to add it in the <head> tag -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
  
<div class="animated-search">
  <div class="search-container">
    <i class="fas fa-search"></i>
    <input type="text" placeholder="Search..." class="search-input">
    <div class="search-suggestions">
      <div class="suggestion-item">JavaScript Tutorial</div>
      <div class="suggestion-item">CSS Animations</div>
      <div class="suggestion-item">HTML5 Features</div>
    </div>
  </div>
</div>
CSS
.animated-search {
  width: 100%;
  max-width: 400px;
}

.search-container {
  position: relative;
  background: var(--bg-secondary);
  border-radius: 12px;
  padding: 12px 16px;
  display: flex;
  align-items: center;
  gap: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  transition: all 0.3s ease;
}

.search-container:focus-within {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

.search-container i {
  color: var(--text-secondary);
  font-size: 16px;
  transition: transform 0.3s ease;
}

.search-container:focus-within i {
  transform: scale(1.1);
  color: var(--accent);
}

.search-input {
  border: none;
  background: none;
  color: var(--text-primary);
  font-size: 16px;
  width: 100%;
  padding: 4px 0;
}

.search-input:focus {
  outline: none;
}

.search-suggestions {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background: var(--bg-secondary);
  border-radius: 0 0 12px 12px;
  margin-top: 4px;
  padding: 8px 0;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  opacity: 0;
  transform: translateY(-10px);
  transition: all 0.3s ease;
  pointer-events: none;
  z-index: 10;
}

.search-container:focus-within .search-suggestions {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

.suggestion-item {
  padding: 8px 16px;
  font-size: 14px;
  color: var(--text-secondary);
  cursor: pointer;
  transition: all 0.2s ease;
}

.suggestion-item:hover {
  background-color: var(--bg-hover);
  color: var(--text-primary);
}

/* Dark mode styles */
@media (prefers-color-scheme: dark) {
  .search-container {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
  }
  
  .search-container:focus-within {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  }
  
  .search-suggestions {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
  }
}
JavaScript
class AnimatedSearch {
  constructor(container) {
    this.container = container;
    this.input = container.querySelector('.search-input');
    this.suggestions = container.querySelector('.search-suggestions');
    this.suggestionItems = container.querySelectorAll('.suggestion-item');
    
    this.init();
  }
  
  init() {
    // Handle input focus
    this.input.addEventListener('focus', () => {
      this.container.classList.add('focused');
    });
    
    this.input.addEventListener('blur', () => {
      // Delay hiding suggestions to allow clicking them
      setTimeout(() => {
        this.container.classList.remove('focused');
      }, 200);
    });
    
    // Handle input changes
    this.input.addEventListener('input', (e) => {
      this.handleInput(e.target.value);
    });
    
    // Handle suggestion clicks
    this.suggestionItems.forEach(item => {
      item.addEventListener('click', () => {
        this.input.value = item.textContent;
        this.container.classList.remove('focused');
        // Trigger custom event for search
        this.container.dispatchEvent(new CustomEvent('search', {
          detail: { query: item.textContent }
        }));
      });
    });
    
    // Handle keyboard navigation
    this.input.addEventListener('keydown', (e) => {
      if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
        e.preventDefault();
        this.handleKeyboardNavigation(e.key);
      } else if (e.key === 'Enter') {
        const activeItem = this.suggestions.querySelector('.active');
        if (activeItem) {
          activeItem.click();
        }
      } else if (e.key === 'Escape') {
        this.container.classList.remove('focused');
      }
    });
  }
  
  handleInput(value) {
    // Filter suggestions based on input
    const query = value.toLowerCase();
    this.suggestionItems.forEach(item => {
      const text = item.textContent.toLowerCase();
      if (text.includes(query)) {
        item.style.display = 'block';
      } else {
        item.style.display = 'none';
      }
    });
    
    // Show/hide suggestions container
    const hasVisibleSuggestions = Array.from(this.suggestionItems)
      .some(item => item.style.display !== 'none');
    this.suggestions.style.display = hasVisibleSuggestions ? 'block' : 'none';
  }
  
  handleKeyboardNavigation(key) {
    const items = Array.from(this.suggestionItems).filter(item => 
      item.style.display !== 'none'
    );
    
    if (items.length === 0) return;
    
    const currentIndex = items.findIndex(item => item.classList.contains('active'));
    let nextIndex;
    
    if (key === 'ArrowDown') {
      nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
    } else {
      nextIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
    }
    
    // Remove active class from current item
    if (currentIndex !== -1) {
      items[currentIndex].classList.remove('active');
    }
    
    // Add active class to next item
    items[nextIndex].classList.add('active');
    items[nextIndex].scrollIntoView({ block: 'nearest' });
  }
}

// Initialize the search
document.addEventListener('DOMContentLoaded', () => {
  const searchContainer = document.querySelector('.animated-search .search-container');
  if (searchContainer) {
    new AnimatedSearch(searchContainer);
  }
});