von der GraphQL-Query bis zur barrierefreien Alpine-Komponente
Die Standard-Suchbox von Hyvä springt erst nach Enter zur Ergebnisliste und zeigt während der Eingabe keine Vorschläge. Live-Search-Autocomplete im Hyvä-Header schließt genau diese Lücke: eine GraphQL-Suggestion-Query liefert Produkte, Kategorien und Preise, eine schlanke Alpine.js-Komponente steuert Debounce, Request-Cancellation und Tastatur-Navigation, und ein CSP-konformes Rendering hält den Shop dabei sicher und performant.
Inhaltsverzeichnis
- 1. Ausgangslage: Standard-Suche vs. Live-Search-Autocomplete
- 2. Architektur der Kopfzeilen-Suche in Hyvä
- 3. GraphQL-Autocomplete-Query
- 4. Alpine.js-Autocomplete-Komponente
- 5. Tastatur-Navigation und Barrierefreiheit
- 6. Rendering der Vorschläge
- 7. CSP-Konformität
- 8. Performance
- 9. Tracking und Analytics
- 10. Zusammenfassung
- 11. FAQ
1. Ausgangslage: Standard-Suche vs. Live-Search-Autocomplete
Hyvä liefert im Grundzustand eine schlichte Quick-Search-Box, die auf den Standard-Magento-Suchcontroller zeigt und erst bei Enter zur Ergebnisliste springt, ohne echte Vorschläge während der Eingabe. Für viele Shops reicht das nicht: Kunden erwarten aus dem Amazon- und Google-Alltag, dass beim Tippen sofort passende Produkte, Kategorien und Preise erscheinen. Genau diese Lücke schließt Live-Search-Autocomplete im Hyvä-Header, eine erweiterte Suche, die per GraphQL im Hintergrund Vorschläge nachlädt, während der Nutzer noch tippt.
Wer Adobe Live Search oder eine Elasticsearch- beziehungsweise OpenSearch-basierte Suche im Einsatz hat, bekommt über die GraphQL-Schnittstelle bereits relevanzsortierte Ergebnisse, muss aber selbst entscheiden, wie diese Daten im Frontend ankommen. Ohne eigene Autocomplete-Komponente bleibt die Leistungsfähigkeit der Suchengine ungenutzt, weil der Header weiterhin nur ein einfaches Eingabefeld zeigt. Autocomplete im Hyvä-Header verbindet die leistungsfähige Backend-Suche mit einer Frontend-Komponente, die Vorschläge, Tastatur-Navigation und Barrierefreiheit sauber zusammenführt.
Der Aufwand für eine solche Erweiterung ist überschaubar, wenn man sich an Hyväs bestehende Struktur hält: Statt die Suche komplett neu zu bauen, wird die vorhandene header-search-Komponente um Alpine.js-State, eine GraphQL-Query und ein paar CSP-konforme Script-Blöcke ergänzt. Dieser Artikel zeigt genau diesen Weg, von der Architektur über die Query bis zur barrierefreien Live-Search-Autocomplete-Komponente im Hyvä-Header.
2. Architektur der Kopfzeilen-Suche in Hyvä
Die Kopfzeilen-Suche liegt in Hyvä standardmäßig unter templates/header/search.phtml innerhalb des Magento_Search-Moduls des Themes und wird über header.phtml als Child-Block eingebunden. Für Live-Search-Autocomplete im Hyvä-Header überschreibt man diese Datei im eigenen Theme und ersetzt das reine Eingabefeld durch einen Alpine-x-data-Container, der Query-String, Ergebnisliste, Ladezustand und aktiven Index verwaltet.
Layout-technisch bindet man die Autocomplete-Komponente über default.xml beziehungsweise ein Block-Reference im Header-Container ein, sodass die Reihenfolge der Kindelemente unverändert bleibt und $block->getChildNames() weiterhin zuverlässig iteriert. Ein eigenes ViewModel liefert Konfigurationswerte wie Mindestzeichenzahl, Debounce-Zeit und maximale Trefferanzahl aus dem Adminbereich an die phtml-Datei, statt Werte hart im Template zu verdrahten.
Der folgende Ausschnitt zeigt die grundlegende Struktur: ein x-data-Wurzelelement mit combobox-Rolle, ein Eingabefeld mit ARIA-Attributen und eine Ergebnisliste, die erst im offenen Zustand sichtbar wird. Diese Struktur bildet das Grundgerüst, auf dem alle weiteren Abschnitte dieser Live-Search-Autocomplete-Integration aufbauen.
<?php
/** @var \Hyva\Theme\ViewModel\HyvaCsp $hyvaCsp */
/** @var \Mironsoft\SearchAutocomplete\ViewModel\SearchAutocompleteConfig $searchConfig */
$hyvaCsp = $viewModels->require(\Hyva\Theme\ViewModel\HyvaCsp::class);
$searchConfig = $viewModels->require(\Mironsoft\SearchAutocomplete\ViewModel\SearchAutocompleteConfig::class);
?>
<div class="relative"
x-data="hyvaLiveSearchAutocomplete({
minChars: <?= (int) $searchConfig->getMinQueryLength() ?>,
debounceMs: <?= (int) $searchConfig->getDebounceMs() ?>,
maxSuggestions: <?= (int) $searchConfig->getMaxSuggestions() ?>
})"
@click.outside="closeSuggestions()"
@keydown.escape.window="closeSuggestions()">
<label for="live-search-autocomplete-input" class="sr-only">
<?= $escaper->escapeHtml(__('Search')) ?>
</label>
<input
id="live-search-autocomplete-input"
type="search"
name="q"
autocomplete="off"
role="combobox"
aria-controls="live-search-autocomplete-listbox"
aria-autocomplete="list"
:aria-expanded="open ? 'true' : 'false'"
:aria-activedescendant="activeIndex > -1 ? 'suggestion-' + activeIndex : null"
x-model="query"
@input.debounce="fetchSuggestions()"
@keydown.arrow-down.prevent="moveActive(1)"
@keydown.arrow-up.prevent="moveActive(-1)"
@keydown.enter.prevent="selectActive()"
class="w-full rounded-lg border border-gray-300 px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-500"
placeholder="<?= $escaper->escapeHtmlAttr(__('Search products...')) ?>"
>
<ul
id="live-search-autocomplete-listbox"
role="listbox"
x-show="open && (results.length > 0 || !loading)"
x-cloak
class="absolute z-30 mt-2 w-full rounded-xl border border-gray-200 bg-white shadow-lg"
>
<template x-for="(item, index) in results" :key="item.sku">
<li
:id="'suggestion-' + index"
role="option"
:aria-selected="index === activeIndex"
@mouseenter="activeIndex = index"
@click="selectResult(item)"
:class="{ 'bg-orange-50': index === activeIndex }"
class="flex items-center gap-3 px-4 py-2 cursor-pointer"
>
<img :src="item.thumbnail" :alt="item.name" class="h-10 w-10 object-cover rounded" loading="lazy">
<div class="flex flex-col">
<span class="text-sm text-gray-800" x-text="item.name"></span>
<span class="text-xs text-gray-500" x-text="item.price"></span>
</div>
</li>
</template>
</ul>
</div>
Das ViewModel dahinter folgt dem in Hyvä üblichen Muster einer schlanken Klasse, die ArgumentInterface implementiert und ausschließlich Konfigurationswerte bereitstellt, keine Geschäftslogik und keine direkten GraphQL-Aufrufe.
<?php
declare(strict_types=1);
namespace Mironsoft\SearchAutocomplete\ViewModel;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\View\Element\Block\ArgumentInterface;
use Magento\Store\Model\ScopeInterface;
/**
* Provides Live Search autocomplete configuration values to the header search template.
*/
class SearchAutocompleteConfig implements ArgumentInterface
{
private const XML_PATH_MIN_QUERY_LENGTH = 'mironsoft_searchautocomplete/general/min_query_length';
private const XML_PATH_DEBOUNCE_MS = 'mironsoft_searchautocomplete/general/debounce_ms';
private const XML_PATH_MAX_SUGGESTIONS = 'mironsoft_searchautocomplete/general/max_suggestions';
/**
* @param ScopeConfigInterface $scopeConfig Store configuration reader
*/
public function __construct(
private readonly ScopeConfigInterface $scopeConfig,
) {
}
/**
* Returns the minimum number of characters before the first autocomplete request fires.
*
* @return int
*/
public function getMinQueryLength(): int
{
return (int) $this->scopeConfig->getValue(
self::XML_PATH_MIN_QUERY_LENGTH,
ScopeInterface::SCOPE_STORE
) ?: 3;
}
/**
* Returns the debounce delay in milliseconds applied before firing a request.
*
* @return int
*/
public function getDebounceMs(): int
{
return (int) $this->scopeConfig->getValue(
self::XML_PATH_DEBOUNCE_MS,
ScopeInterface::SCOPE_STORE
) ?: 300;
}
/**
* Returns the maximum number of product suggestions rendered in the autocomplete dropdown.
*
* @return int
*/
public function getMaxSuggestions(): int
{
return (int) $this->scopeConfig->getValue(
self::XML_PATH_MAX_SUGGESTIONS,
ScopeInterface::SCOPE_STORE
) ?: 6;
}
}
3. GraphQL-Autocomplete-Query
Die eigentliche Such-Intelligenz kommt aus einer eigenen GraphQL-Query, die parallel zur Produktsuche auch Kategorie-Vorschläge abfragt. Der products-Query-Typ liefert über search, pageSize und Felder wie sku, name, small_image und price_range alles, was für eine kompakte Produktvorschau nötig ist. Für Live-Search-Autocomplete reicht es, pageSize auf einen kleinen Wert wie 5 oder 6 zu begrenzen, weil die Autocomplete-Liste keine vollständige Trefferliste, sondern nur eine Orientierungshilfe sein soll.
Zusätzlich zur Produktsuche fragt man categoryList mit einem name-Match-Filter ab, um passende Kategorien direkt in den Vorschlägen anzuzeigen, ein Muster, das bei Adobe Live Search und klassischer Elasticsearch-Integration gleichermaßen funktioniert, sofern das Schema categoryList unterstützt. Wichtig ist, dass die Query so schlank wie möglich bleibt: Jedes zusätzliche Feld erhöht die Antwortzeit, und bei einer Autocomplete-Komponente zählt jede Millisekunde, weil der Nutzer währenddessen weitertippt.
Bevor die Query überhaupt abgeschickt wird, muss das Debounce-Handling im Frontend greifen. Ohne Debounce würde jede Tasteneingabe einen eigenen GraphQL-Request auslösen, bei einem zehn Zeichen langen Suchbegriff also zehn parallele Anfragen, von denen neun überflüssig sind. Die Kombination aus Debounce im Alpine-Code und einer schlanken Query ist die Grundlage für eine performante Live-Search-Autocomplete im Hyvä-Header.
query LiveSearchAutocomplete($search: String!, $pageSize: Int!) {
products(search: $search, pageSize: $pageSize) {
total_count
items {
sku
name
small_image {
url
}
price_range {
minimum_price {
final_price {
value
currency
}
}
}
}
}
categoryList(filters: { name: { match: $search } }) {
uid
name
url_path
}
}
4. Alpine.js-Autocomplete-Komponente
Das Herzstück der Komponente ist eine Alpine.js-Funktion mit klar definiertem State: query für den aktuellen Suchbegriff, results für die Produktliste, loading für den Ladezustand und activeIndex für die per Tastatur markierte Position. Dieser State lebt vollständig im x-data-Objekt und wird nirgends global im window-Objekt gehalten, was mehrere unabhängige Suchfelder auf einer Seite ohne Konflikte ermöglicht.
Der kritische Teil ist die Request-Cancellation: Tippt ein Nutzer schnell weiter, während eine GraphQL-Antwort noch unterwegs ist, darf die ältere Antwort nicht die neuere überschreiben. Ein AbortController pro Komponente löst dieses Problem elegant, vor jedem neuen fetch()-Aufruf wird der vorherige Controller abgebrochen, sodass nur die zuletzt gestartete Anfrage tatsächlich results setzt. Ohne dieses Pattern flackert die Live-Search-Autocomplete-Liste bei schnellem Tippen zwischen alten und neuen Treffern.
Fehlerbehandlung gehört ebenfalls zum Komponenten-Code: Ein abgebrochener Request wirft einen AbortError, der explizit ignoriert werden muss, während echte Netzwerkfehler geloggt und dem Nutzer als klar kommunizierter Leerzustand angezeigt werden. Der folgende Code zeigt die vollständige Komponente inklusive Tastatur-Handling für Pfeiltasten und Enter.
// Alpine.js component: Live Search autocomplete with request cancellation
function hyvaLiveSearchAutocomplete(config) {
return {
query: '',
results: [],
categories: [],
loading: false,
open: false,
activeIndex: -1,
minChars: config.minChars,
debounceMs: config.debounceMs,
maxSuggestions: config.maxSuggestions,
abortController: null,
async fetchSuggestions() {
if (this.query.trim().length < this.minChars) {
this.results = [];
this.open = false;
return;
}
// Cancel the previous in-flight request before starting a new one
if (this.abortController) {
this.abortController.abort();
}
this.abortController = new AbortController();
this.loading = true;
this.activeIndex = -1;
try {
const response = await fetch('/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Store': hyva.storeCode },
signal: this.abortController.signal,
body: JSON.stringify({
query: window.liveSearchAutocompleteQuery,
variables: { search: this.query, pageSize: this.maxSuggestions }
})
});
const payload = await response.json();
this.results = payload.data.products.items;
this.categories = payload.data.categoryList;
this.open = true;
} catch (error) {
// Ignore aborts caused by newer keystrokes, log real failures
if (error.name !== 'AbortError') {
console.error('Live Search autocomplete request failed', error);
}
} finally {
this.loading = false;
}
},
moveActive(step) {
if (!this.open || this.results.length === 0) return;
const count = this.results.length;
this.activeIndex = (this.activeIndex + step + count) % count;
},
selectActive() {
if (this.activeIndex > -1) {
this.selectResult(this.results[this.activeIndex]);
}
},
selectResult(item) {
window.location.href = item.url ?? ('/catalog/product/view/sku/' + item.sku);
},
closeSuggestions() {
this.open = false;
this.activeIndex = -1;
}
};
}
5. Tastatur-Navigation und Barrierefreiheit
Eine Live-Search-Autocomplete-Komponente ohne Tastatur-Bedienung schließt einen erheblichen Teil der Nutzer aus, sowohl Screenreader-Nutzer als auch alle, die aus Gewohnheit mit der Tastatur navigieren. Die ARIA-Rolle combobox am Eingabefeld in Kombination mit role="listbox" auf dem Ergebnis-Container und role="option" auf jedem Eintrag ist das vom WAI-ARIA-Authoring-Pattern vorgegebene Grundgerüst für genau diesen Anwendungsfall.
Das Attribut aria-activedescendant zeigt Screenreadern, welcher Eintrag aktuell markiert ist, ohne den Fokus tatsächlich zu verschieben, der Fokus bleibt im Eingabefeld, während activeIndex per Pfeiltaste hoch- und runtergezählt wird. Escape schließt die Vorschlagsliste und setzt activeIndex zurück auf -1, Enter übernimmt den aktuell markierten Vorschlag oder löst, falls kein Eintrag markiert ist, die reguläre Volltextsuche aus.
Wichtig ist außerdem die aria-expanded-Bindung am Eingabefeld, die dynamisch zwischen true und false wechselt, sowie ein sr-only-Label, das den Zweck des Feldes auch ohne sichtbaren Text kommuniziert. Diese Details entscheiden bei einer Autocomplete im Hyvä-Header-Implementierung häufig über Erfolg oder Misserfolg im Rahmen einer Barrierefreiheitsprüfung nach WCAG 2.1 AA.
6. Rendering der Vorschläge
Jeder Vorschlag in der Liste zeigt üblicherweise ein kleines Produktbild, den Namen und den Preis, kompakt genug, um mehrere Treffer gleichzeitig darzustellen, aber informativ genug, um eine Kaufentscheidung vorzubereiten. Das small_image-Feld aus der GraphQL-Antwort liefert die Bild-URL, price_range.minimum_price.final_price liefert Wert und Währung, sodass der Preis ohne zusätzliche Formatierungslogik im Template direkt mit x-text ausgegeben werden kann.
Neben Produkten gehören Kategorie- und CMS-Vorschläge zu einer vollständigen Live-Search-Autocomplete im Hyvä-Header. Kategorien erscheinen typischerweise in einer eigenen Sektion oberhalb oder unterhalb der Produktliste, mit einem Icon oder Label, das die Ergebnisart kennzeichnet. Das verhindert, dass Nutzer eine Kategorie mit einem Produkt verwechseln.
Für den Fall, dass keine Treffer gefunden werden, braucht die Komponente einen expliziten Empty-State, einen kurzen Hinweistext statt einer leeren, aber sichtbaren Box. Ein guter Empty-State schlägt idealerweise auch alternative Aktionen vor, etwa einen Link zur vollständigen Suchergebnisseite oder zu beliebten Kategorien, damit der Nutzer bei einer erfolglosen Suche nicht ins Leere läuft.
{
"data": {
"products": {
"total_count": 42,
"items": [
{
"sku": "WT08-XS-Blue",
"name": "Zoltan Wool Sweater",
"small_image": { "url": "https://mironsoft.de/media/catalog/product/cache/wt08.jpg" },
"price_range": {
"minimum_price": {
"final_price": { "value": 59.9, "currency": "EUR" }
}
}
}
]
},
"categoryList": [
{ "uid": "Mg==", "name": "Pullover", "url_path": "damen/pullover" }
]
}
}
7. CSP-Konformität
Hyvä setzt aus gutem Grund auf eine strikte Content-Security-Policy, die Inline-Scripts nur zulässt, wenn sie explizit über das HyvaCsp-ViewModel registriert werden. Für die Live-Search-Autocomplete-Komponente bedeutet das: Jeder Inline-<script>-Block, der etwa die GraphQL-Query als Konstante bereitstellt oder Alpine-Komponenten registriert, muss unmittelbar von $hyvaCsp->registerInlineScript() gefolgt sein.
Externe Script-Quellen sind für diese Funktion nicht nötig und sollten es auch nicht sein, die komplette Autocomplete-Logik läuft über Alpine.js, das bereits Teil des Hyvä-Themes ist, plus eine schlanke, selbst geschriebene JavaScript-Funktion. Wer stattdessen ein externes Autocomplete-Skript von einem CDN einbindet, unterläuft die CSP-Konfiguration und schafft eine zusätzliche Angriffsfläche sowie eine externe Abhängigkeit, die bei einem Ausfall des CDN die gesamte Kopfzeilen-Suche lahmlegt.
In der Praxis reicht ein einziger Inline-Block im Layout, der die Alpine-Funktion global registriert, gefolgt vom passenden registerInlineScript()-Aufruf. Damit bleibt Live-Search-Autocomplete im Hyvä-Header vollständig CSP-konform, ohne dass zusätzliche Nonce- oder Hash-Ausnahmen in der CSP-Konfiguration des Shops nötig werden.
8. Performance
Debounce-Timing ist der wichtigste Stellhebel für die gefühlte Performance von Live-Search-Autocomplete. Ein Wert zwischen 250 und 350 Millisekunden hat sich in der Praxis bewährt: kurz genug, damit sich die Suche noch reaktionsschnell anfühlt, lang genug, um die Zahl unnötiger Requests bei schnellem Tippen spürbar zu reduzieren. Werte unter 150 Millisekunden bringen kaum wahrnehmbaren Geschwindigkeitsgewinn, erzeugen aber deutlich mehr Serverlast.
Genauso wichtig ist eine Mindestzeichenzahl vor dem ersten Request, typischerweise drei Zeichen. Ein einzelner Buchstabe liefert ohnehin zu viele, kaum relevante Treffer und würde bei Shops mit hohem Traffic unnötig viele GraphQL-Anfragen an die Suchengine schicken. Diese Schwelle lässt sich, wie im ViewModel-Beispiel gezeigt, direkt über die Systemkonfiguration steuern, ohne den Code anzufassen.
Für häufig wiederkehrende Suchanfragen lohnt sich eine Cache-Strategie auf HTTP-Ebene: Adobe Live Search cached Suggestion-Antworten serverseitig, bei einer klassischen Elasticsearch-Anbindung kann ein kurzlebiger Full-Page-Cache oder ein Edge-Cache vor dem GraphQL-Endpunkt ähnliche Effekte erzielen. Wichtig ist, dass der Cache-Schlüssel Suchbegriff, Store-View und Kundengruppe berücksichtigt, damit keine falschen Preise oder Sichtbarkeiten in den Vorschlägen von Live-Search-Autocomplete landen.
9. Tracking und Analytics
Jede abgeschlossene Live-Search-Autocomplete-Anfrage ist ein Signal für die Sortiments- und Suchoptimierung und sollte deshalb in den dataLayer gepusht werden. Ein Event wie search_suggestion_shown mit Suchbegriff, Trefferanzahl und Zeitstempel liefert die Datenbasis für spätere Auswertungen, welche Suchbegriffe häufig getippt, aber selten angeklickt werden.
Mindestens ebenso wertvoll ist das No-Result-Tracking: Sucht ein Nutzer nach einem Begriff, für den Live-Search-Autocomplete keine Produkte findet, ist das ein direkter Hinweis auf eine Sortimentslücke, ein fehlendes Synonym in der Suchengine oder einen Tippfehler, der sich durch eine Rechtschreibkorrektur beheben ließe. Ein Event wie search_no_results, gefüllt mit dem exakten Suchbegriff, macht diese Fälle im Analytics-Tool auswertbar.
Für die technische Umsetzung reicht ein window.dataLayer.push() direkt in der fetchSuggestions()-Methode der Alpine-Komponente, wieder eingebettet in einen registrierten Inline-Script-Block. Wichtig: Das Tracking darf niemals den eigentlichen Request blockieren oder verzögern, es läuft als Fire-and-Forget-Aufruf parallel zur Darstellung der Vorschläge.
Die folgenden Muster fassen die wichtigsten Unterschiede zwischen einer naiv umgesetzten und einer robusten Live-Search-Autocomplete im Hyvä-Header zusammen.
| Aufgabe | Naiver Ansatz | Empfohlenes Hyvä-Pattern | Nutzen |
|---|---|---|---|
| Anfrage bei Eingabe | Kein Debounce, ein Request pro Tastendruck | Debounced fetch (250-350 ms) | Weniger Serverlast, flüssigeres Tippen |
| Antworten bei schnellem Tippen | Keine Request-Cancellation | AbortController pro Komponente | Immer aktuelle, konsistente Vorschläge |
| Inline-Script im Header | Ungeprüfter <script>-Block | $hyvaCsp->registerInlineScript() | CSP-konform, kein Blockieren durch den Browser |
| Bedienung der Vorschlagsliste | Nur mit der Maus erreichbar | aria-activedescendant + Pfeiltasten | Barrierefreiheit nach WCAG 2.1 AA |
| Auswertung von Fehlsuchen | Kein No-Result-Tracking | dataLayer-Event search_no_results | Datenbasis für Sortimentsoptimierung |
In der Summe entscheidet die Kombination dieser Muster darüber, ob Live-Search-Autocomplete im Hyvä-Header als spürbare Verbesserung wahrgenommen wird oder als zusätzliche Fehlerquelle im Kopfbereich des Shops.
Mironsoft
Hyvä-Suche, GraphQL-Integration und Frontend-Performance
Live-Search-Autocomplete zuverlässig in euren Hyvä-Header integrieren?
Wir implementieren GraphQL-basierte Autocomplete-Komponenten, die barrierefrei, CSP-konform und performant sind, von der Suggestion-Query bis zur fertigen Alpine.js-Komponente im Header.
GraphQL-Integration
Suggestion-Queries für Produkte, Kategorien und CMS-Inhalte konzipieren und anbinden
Alpine-Komponenten
Debounce, AbortController und State-Handling für den Hyvä-Header umsetzen
Barrierefreiheit-Audit
Tastatur-Navigation und ARIA-Struktur nach WCAG 2.1 AA prüfen und nachrüsten
10. Zusammenfassung
Eine saubere Live-Search-Autocomplete-Integration im Hyvä-Header löst ein konkretes Problem: Die Standard-Quick-Search zeigt während der Eingabe keine Vorschläge, obwohl die Suchengine im Hintergrund längst relevanzsortierte Ergebnisse liefern könnte. Eine schlanke GraphQL-Query, eine Alpine.js-Komponente mit Debounce und AbortController sowie ein ARIA-konformes Markup verwandeln das einfache Eingabefeld in eine vollwertige, barrierefreie Suche direkt im Header.
Der größte Hebel liegt darin, alle Bausteine konsequent zusammenzuführen: CSP-konforme Script-Registrierung, ein durchdachtes Debounce-Timing, ein aussagekräftiger Empty-State und Tracking-Events für Treffer und Fehlsuchen. Wer diese Punkte von Anfang an mitdenkt, bekommt eine Autocomplete im Hyvä-Header, die nicht nur schnell wirkt, sondern auch messbar zur Sortiments- und Suchoptimierung beiträgt.
Live-Search-Autocomplete im Hyvä-Header, das Wichtigste auf einen Blick
GraphQL-Query
Schlanke Suggestion-Query mit products und categoryList, begrenzt auf wenige Felder und eine kleine pageSize.
Alpine-State & Cancellation
Debounce, query/results/loading/activeIndex und ein AbortController pro Komponente verhindern veraltete Vorschläge.
Barrierefreiheit & CSP
role="combobox", aria-activedescendant und Pfeiltasten-Navigation, jeder Inline-Script-Block mit registerInlineScript().
Performance & Tracking
Debounce 250-350 ms, Mindestzeichenzahl 3, Cache-Strategie pro Store-View, dataLayer-Events für Treffer und Fehlsuchen.