A modern search input with animated suggestions and micro-interactions
<head>
.<!-- 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>
.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);
}
}
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);
}
});