Autocomplete Search Dropdown
Search field over a grouped list: rows filter as you type with the matched characters marked, a highlight slides under the arrow keys, and Enter fills the field.
About This Component
A search field with a real results dropdown: a static, grouped list that actually filters as you type, with the matched run of characters marked in each label and any heading left with nothing under it dropping out with its group. The surviving rows stagger back in on every keystroke, one highlight slides between them under the arrow keys, Enter fills the field, and a dead end gets an animated empty state rather than a blank panel.
Read the full effect overview
A single self-contained component: one HTML file, one stylesheet, one script, vanilla JavaScript and core GSAP, no framework and no plugins. Ships in three looks, vault, corporate and brutal, switched with one data-variant attribute.
What's Included
- Substring filter over a static list, with the matched characters wrapped in mark
- Results grouped under headings, and a group hides itself when it empties
- Surviving rows stagger back in on every keystroke
- One highlight slides between rows for arrow keys, hover and click alike
- Enter fills the field and flashes it in the variant's accent
- Animated empty state that names the term that found nothing
- combobox / listbox / option roles with aria-activedescendant
- Three shipped variants: vault, corporate, brutal
Perfect For
- Site and docs search with results split by section
- Admin pickers for customers, products or tickets
- Location, currency and tag fields that need grouping
- Any input where a plain datalist looks unfinished
How It Works
Filtering and marking
Each keystroke walks the rows, tests the query against the row's data-label and toggles the hidden attribute, which the stylesheet restates as display:none because the row rule sets display:flex. The label is then re-rendered from data-label with the matched run wrapped in a mark element, so repeated filtering never compounds earlier markup, and each group's heading hides itself when nothing under it survived.
The sliding highlight
One absolutely positioned bar keeps left:0 and top:0 in CSS and takes its whole box from the active row's offsetLeft, offsetTop, offsetWidth and offsetHeight, so no CSS inset can double up with the measured offset. Rows are positioned only to paint over the bar, which leaves the scroll box as the single offset parent every measurement is taken against.
Keyboard model
Focus never leaves the input. Arrow keys move an index over the visible rows only, wrapping at both ends, and the input carries aria-activedescendant so assistive technology follows the same row the highlight is under. Enter copies the active row's label into the field and re-filters, and the field flashes in whichever accent the current variant defines.