Typeahead
Câmp cu sugestii ancorate. Refolosește .input pentru câmp și .popover pentru listă — nu are chrome propriu, deliberat.
Markup canonic · components/typeahead.json
sursa = JSON · generat de scripts/build-catalog.py
Câmpul E .input. Neschimbat.
Regula care ține componenta onestă: typeahead-ul nu are câmp propriu. Nicio înălțime proprie, nicio bordură proprie, niciun radius propriu — folosește
.input exact așa cum e. Tot ce adaugă e ancorarea listei și starea „niciun rezultat". Un al doilea input, oricât de bine copiat, derivă de primul la prima schimbare de token..typeahead position: relative + gap 4px (ca .field) .typeahead > .popover top: calc(100% + 6px), left/right 0, max-height 280px .typeahead-match substringul potrivit — --c-accent-deep, weight 600 .typeahead-meta rândul secundar — 12px/16px, --c-fog .typeahead-empty starea fără rezultate — 14px, --c-fog, centrat
Combobox · .input.has-chevron
typeahead = text liber care filtrează o listă deschisă. combobox = alegere dintr-o listă închisă, deci arată ca .select — dar deschide un .popover, nu meniul nativ. Chevronul e SVG-ul copiat literal din .select: dacă se schimbă o săgeată, se schimbă amândouă în același commit.
Fără rezultate
Niciun client pentru „qwerty"
Accesibilitate
input role="combobox" aria-expanded aria-controls aria-activedescendant
autocomplete="off"
lista role="listbox"
rândul role="option"
ales .is-selected + aria-selected="true" + bifa .popover-check
sub săgeată .is-active ← aria-activedescendant arată aici, NU aria-selected
tastatură ↑ ↓ navighează · Enter alege · Esc închide și întoarce focusul în câmp
Focusul rămâne în .input tot timpul; rândul de sub săgeată se marchează cu .is-active plus aria-activedescendant, nu prin mutarea focusului — altfel utilizatorul nu mai poate scrie. Cele două atribute nu stau pe același rând: aria-selected spune care e valoarea, aria-activedescendant spune unde a ajuns săgeata.