Improve suggestions accessibility and search privacy

This commit is contained in:
Ted Pier 2026-07-11 15:27:36 -07:00
parent abe4c6e7bc
commit 6f9242d20e
3 changed files with 21 additions and 12 deletions

View File

@ -541,7 +541,7 @@ span > select {
.light-theme .pure-button-primary:focus, .light-theme .pure-button-primary:focus,
.light-theme .pure-button-secondary:hover, .light-theme .pure-button-secondary:hover,
.light-theme .pure-button-secondary:focus, .light-theme .pure-button-secondary:focus,
.light-theme .active { .light-theme #suggestions .active {
color: #fff !important; color: #fff !important;
border-color: rgba(0, 182, 240, 0.75) !important; border-color: rgba(0, 182, 240, 0.75) !important;
background-color: rgba(0, 182, 240, 0.75) !important; background-color: rgba(0, 182, 240, 0.75) !important;
@ -589,7 +589,7 @@ span > select {
.no-theme .pure-button-primary:focus, .no-theme .pure-button-primary:focus,
.no-theme .pure-button-secondary:hover, .no-theme .pure-button-secondary:hover,
.no-theme .pure-button-secondary:focus, .no-theme .pure-button-secondary:focus,
.no-theme .active { .no-theme #suggestions .active {
color: #fff !important; color: #fff !important;
border-color: rgba(0, 182, 240, 0.75) !important; border-color: rgba(0, 182, 240, 0.75) !important;
background-color: rgba(0, 182, 240, 0.75) !important; background-color: rgba(0, 182, 240, 0.75) !important;
@ -650,7 +650,7 @@ span > select {
.dark-theme .pure-button-primary:focus, .dark-theme .pure-button-primary:focus,
.dark-theme .pure-button-secondary:hover, .dark-theme .pure-button-secondary:hover,
.dark-theme .pure-button-secondary:focus, .dark-theme .pure-button-secondary:focus,
.dark-theme .active { .dark-theme #suggestions .active {
color: #fff !important; color: #fff !important;
border-color: rgb(0, 182, 240) !important; border-color: rgb(0, 182, 240) !important;
background-color: rgba(0, 182, 240, 1) !important; background-color: rgba(0, 182, 240, 1) !important;
@ -713,7 +713,7 @@ body.dark-theme {
.no-theme .pure-button-primary:focus, .no-theme .pure-button-primary:focus,
.no-theme .pure-button-secondary:hover, .no-theme .pure-button-secondary:hover,
.no-theme .pure-button-secondary:focus, .no-theme .pure-button-secondary:focus,
.no-theme .active { .no-theme #suggestions .active {
color: #fff !important; color: #fff !important;
border-color: rgb(0, 182, 240) !important; border-color: rgb(0, 182, 240) !important;
background-color: rgba(0, 182, 240, 1) !important; background-color: rgba(0, 182, 240, 1) !important;
@ -943,6 +943,7 @@ h1, h2, h3, h4, h5, p,
border: 1px solid #a0a0a0; border: 1px solid #a0a0a0;
z-index: 200; z-index: 200;
padding: .25em 0; padding: .25em 0;
margin: 0;
display: none; display: none;
} }

View File

@ -50,19 +50,24 @@ searchbox?.addEventListener("input", function () {
); );
const body = await resp.json(); const body = await resp.json();
// As a sanity check, ensure the
// suggestions match the query.
if (body.query != searchbox.value) {
return;
}
// Put the results into DOM. // Put the results into DOM.
resetSuggestions(); resetSuggestions();
const results = body.suggestions; const results = body.suggestions;
for (let i = 0; i < results.length; i++) { for (let i = 0; i < results.length; i++) {
const s = document.createElement("a"); const s = document.createElement("a");
s.href = `/search?q=${encodeURIComponent(results[i])}`; s.href = "/search";
s.innerText = results[i]; s.innerText = results[i];
s.setAttribute("role", "option");
s.setAttribute("aria-selected", "false");
s.addEventListener("click", function (e) {
searchbox.value = results[i];
if (searchbox.form?.requestSubmit) {
searchbox.form.requestSubmit();
} else {
searchbox.form?.submit();
}
e.preventDefault();
});
suggestions.appendChild(s); suggestions.appendChild(s);
} }
} catch (err) { } catch (err) {
@ -109,8 +114,10 @@ searchbox?.addEventListener("keydown", function (e) {
for (let i = 0; i < currentSuggestions.length; i++) { for (let i = 0; i < currentSuggestions.length; i++) {
if (i == selectedSuggestion) { if (i == selectedSuggestion) {
currentSuggestions[i].classList.add("active"); currentSuggestions[i].classList.add("active");
currentSuggestions[i].setAttribute("aria-selected", "true");
} else if (currentSuggestions[i].classList.contains("active")) { } else if (currentSuggestions[i].classList.contains("active")) {
currentSuggestions[i].classList.remove("active"); currentSuggestions[i].classList.remove("active");
currentSuggestions[i].setAttribute("aria-selected", "false");
} }
} }
}); });

View File

@ -9,11 +9,12 @@
<% end %> <% end %>
<fieldset id="search-container"> <fieldset id="search-container">
<input type="search" id="searchbox" autocorrect="off" <input type="search" id="searchbox" autocorrect="off"
aria-autocomplete="list" aria-controls="suggestions"
autocapitalize="none" spellcheck="false" <% if autofocus %>autofocus<% end %> autocapitalize="none" spellcheck="false" <% if autofocus %>autofocus<% end %>
name="q" placeholder="<%= I18n.translate(locale, "search") %>" name="q" placeholder="<%= I18n.translate(locale, "search") %>"
title="<%= I18n.translate(locale, "search") %>" title="<%= I18n.translate(locale, "search") %>"
value="<%= env.get?("search").try {|x| HTML.escape(x.as(String)) } %>"> value="<%= env.get?("search").try {|x| HTML.escape(x.as(String)) } %>">
<div id="suggestions"></div> <ul id="suggestions"></ul>
</fieldset> </fieldset>
<button type="submit" id="searchbutton" aria-label="<%= I18n.translate(locale, "search") %>"> <button type="submit" id="searchbutton" aria-label="<%= I18n.translate(locale, "search") %>">
<i class="icon ion-ios-search"></i> <i class="icon ion-ios-search"></i>