Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions public/combobox-autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,15 +198,18 @@ ComboboxAutocomplete.prototype.setCurrentOptionStyle = function (option) {
var opt = this.filteredOptions[i];
if (opt === option) {
opt.setAttribute('aria-selected', 'true');
// Keep the visible highlight ('active') on the option the combobox marks current;
// the legacy Swiftype handler otherwise drifts out of sync at the list boundaries.
opt.classList.add('active');
if ((this.listboxNode.scrollTop + this.listboxNode.offsetHeight) < (opt.offsetTop + opt.offsetHeight)) {
this.listboxNode.scrollTop = opt.offsetTop + opt.offsetHeight - this.listboxNode.offsetHeight;
}
else if (this.listboxNode.scrollTop > (opt.offsetTop + 2)) {
this.listboxNode.scrollTop = opt.offsetTop;
}
// Focus stays on the input (aria-activedescendant), and the listbox is not an
// internal scroll container, so the browser will not auto-scroll to the active
// option. Scroll it into view so it is never obscured (WCAG 2.4.11).
// The listbox is capped and scrolls internally (see #search-results in
// autocomplete.css), so the scrollTop above keeps the focused suggestion visible
// (WCAG 2.4.11). scrollIntoView is a no-op-when-visible fallback.
if (typeof opt.scrollIntoView === 'function') {
try {
opt.scrollIntoView({ block: 'nearest', inline: 'nearest' });
Expand All @@ -219,6 +222,7 @@ ComboboxAutocomplete.prototype.setCurrentOptionStyle = function (option) {
}
else {
opt.removeAttribute('aria-selected');
opt.classList.remove('active');
}
}
};
Expand Down
10 changes: 10 additions & 0 deletions public/css/autocomplete.css
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ form input.st-search-input {
text-align: left;
}

/* WCAG 2.4.11 Focus Not Obscured: cap the suggestion list to the viewport and scroll it
internally instead of scrolling the whole page, so the keyboard-focused suggestion stays
visible. 'position: relative' makes each option's offsetTop relative to this list so
setCurrentOptionStyle's listboxNode.scrollTop can reach it. No scrollbar when all fit. */
#search-results {
position: relative;
max-height: calc(100vh - 60px);
overflow-y: auto;
}

.swiftype-widget .autocomplete li > a:hover {
text-decoration: none;
}
Expand Down