about summary refs log tree commit diff
path: root/src/librustdoc/html
diff options
context:
space:
mode:
authorJacob Hoffman-Andrews <github@hoffman-andrews.com>2021-05-09 12:56:21 -0700
committerJacob Hoffman-Andrews <github@hoffman-andrews.com>2021-05-13 12:49:14 -0700
commitb615c0c85469c94041a5e68b9d8b68dcf799f9f1 (patch)
tree4406ac781e0ba2b7b2fd0d37e06c341f4b2fe4d8 /src/librustdoc/html
parent952c5732c2d25a875f90e5cd5dd29a1a21c1d4a2 (diff)
downloadrust-b615c0c85469c94041a5e68b9d8b68dcf799f9f1.tar.gz
rust-b615c0c85469c94041a5e68b9d8b68dcf799f9f1.zip
rustdoc: use focus for search navigation
Rather than keeping track of highlighted element inside the JS, take
advantage of `.focus()` and the :focus CSS pseudo-class.

This required wrapping each row of results in one big <a> tag (because
anchors can be focused, but table rows cannot). That in turn required
moving from a table layout to a div layout with float.

This makes it so Ctrl+Enter opens links in new tabs, and using the arrow
keys to navigate off the bottom of the page scrolls the rest of the page
into view. It also simplifies the keyboard event handling. It eliminates
the need for click handlers on the search results, and for tracking
mouse movements.

This changes the UI treatment of mouse hovering. A hovered element now
gets a light grey background, but does not change the focused element.
It's possible to have two highlighted search results: one that is
focused (via keyboard) and one that is hovered (via mouse). Pressing
enter will activate the focused link; clicking will activate the hovered
link. This matches up with how Firefox and Chrome handle suggestions in
their URL bar, and avoids stray mouse movements changing the focus.

Selecting tabs is now done with left/right arrows while any search
result is focused. The visibility of results on each search tab is
controlled with the "active" class, rather than by setting display: none
directly. Note that the old code kept track of highlighted search
element when tabbing back and forth. The new code doesn't.
Diffstat (limited to 'src/librustdoc/html')
-rw-r--r--src/librustdoc/html/static/main.js9
-rw-r--r--src/librustdoc/html/static/rustdoc.css32
-rw-r--r--src/librustdoc/html/static/search.js186
-rw-r--r--src/librustdoc/html/static/themes/ayu.css50
-rw-r--r--src/librustdoc/html/static/themes/dark.css50
-rw-r--r--src/librustdoc/html/static/themes/light.css46
6 files changed, 178 insertions, 195 deletions
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index ebc9ada4451..fcb06bc796e 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -170,7 +170,8 @@ function hideThemeButtonState() {
         // 1 for "In Parameters"
         // 2 for "In Return Types"
         currentTab: 0,
-        mouseMovedAfterSearch: true,
+        // tab and back preserves the element that was focused.
+        focusedByTab: [null, null, null],
         clearInputTimeout: function() {
             if (searchState.timeout !== null) {
                 clearTimeout(searchState.timeout);
@@ -262,10 +263,6 @@ function hideThemeButtonState() {
                 search_input.placeholder = searchState.input.origPlaceholder;
             });
 
-            document.addEventListener("mousemove", function() {
-                searchState.mouseMovedAfterSearch = true;
-            });
-
             search_input.removeAttribute('disabled');
 
             // `crates{version}.js` should always be loaded before this script, so we can use it
@@ -1070,7 +1067,7 @@ function hideThemeButtonState() {
             ["T", "Focus the theme picker menu"],
             ["↑", "Move up in search results"],
             ["↓", "Move down in search results"],
-            ["ctrl + ↑ / ↓", "Switch result tab"],
+            ["← / →", "Switch result tab (when results focused)"],
             ["&#9166;", "Go to active search result"],
             ["+", "Expand all sections"],
             ["-", "Collapse all sections"],
diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css
index 1b7eff4604f..b03e657dce0 100644
--- a/src/librustdoc/html/static/rustdoc.css
+++ b/src/librustdoc/html/static/rustdoc.css
@@ -144,7 +144,7 @@ h4.type.trait-impl, h4.associatedconstant.trait-impl, h4.associatedtype.trait-im
 }
 
 h1, h2, h3, h4,
-.sidebar, a.source, .search-input, .content table td:first-child > a,
+.sidebar, a.source, .search-input, .search-results .result-name,
 div.item-list .out-of-band,
 #source-sidebar, #sidebar-toggle,
 details.rustdoc-toggle > summary::before,
@@ -748,6 +748,15 @@ a {
 	outline: 0;
 }
 
+.search-results {
+	display: none;
+	padding-bottom: 2em;
+}
+
+.search-results.active {
+	display: block;
+}
+
 .search-results .desc {
 	white-space: nowrap;
 	text-overflow: ellipsis;
@@ -756,22 +765,14 @@ a {
 }
 
 .search-results a {
+	/* A little margin ensures the browser's outlining of focused links has room to display. */
+	margin-left: 2px;
+	margin-right: 2px;
 	display: block;
 }
 
-.content .search-results td:first-child {
-	padding-right: 0;
+.result-name {
 	width: 50%;
-}
-.content .search-results td:first-child a {
-	padding-right: 10px;
-}
-.content .search-results td:first-child a:after {
-	clear: both;
-	content: "";
-	display: block;
-}
-.content .search-results td:first-child a span {
 	float: left;
 }
 
@@ -1134,6 +1135,11 @@ pre.rust {
 .search-failed {
 	text-align: center;
 	margin-top: 20px;
+	display: none;
+}
+
+.search-failed.active {
+	display: block;
 }
 
 .search-failed > ul {
diff --git a/src/librustdoc/html/static/search.js b/src/librustdoc/html/static/search.js
index a09d3eb1796..4de24d73283 100644
--- a/src/librustdoc/html/static/search.js
+++ b/src/librustdoc/html/static/search.js
@@ -48,9 +48,9 @@ function printTab(nb) {
     });
     onEachLazy(document.getElementById("results").childNodes, function(elem) {
         if (nb === 0) {
-            elem.style.display = "";
+            addClass(elem, "active");
         } else {
-            elem.style.display = "none";
+            removeClass(elem, "active");
         }
         nb -= 1;
     });
@@ -875,106 +875,22 @@ window.initSearch = function(rawSearchIndex) {
         };
     }
 
-    function initSearchNav() {
-        var hoverTimeout;
-
-        var click_func = function(e) {
-            var el = e.target;
-            // to retrieve the real "owner" of the event.
-            while (el.tagName !== "TR") {
-                el = el.parentNode;
-            }
-            var dst = e.target.getElementsByTagName("a");
-            if (dst.length < 1) {
-                return;
-            }
-            dst = dst[0];
-            if (window.location.pathname === dst.pathname) {
-                searchState.hideResults();
-                document.location.href = dst.href;
-            }
-        };
-        var mouseover_func = function(e) {
-            if (searchState.mouseMovedAfterSearch) {
-                var el = e.target;
-                // to retrieve the real "owner" of the event.
-                while (el.tagName !== "TR") {
-                    el = el.parentNode;
-                }
-                clearTimeout(hoverTimeout);
-                hoverTimeout = setTimeout(function() {
-                    onEachLazy(document.getElementsByClassName("search-results"), function(e) {
-                        onEachLazy(e.getElementsByClassName("result"), function(i_e) {
-                            removeClass(i_e, "highlighted");
-                        });
-                    });
-                    addClass(el, "highlighted");
-                }, 20);
-            }
-        };
-        onEachLazy(document.getElementsByClassName("search-results"), function(e) {
-            onEachLazy(e.getElementsByClassName("result"), function(i_e) {
-                i_e.onclick = click_func;
-                i_e.onmouseover = mouseover_func;
-            });
-        });
-
-        searchState.input.onkeydown = function(e) {
-            // "actives" references the currently highlighted item in each search tab.
-            // Each array in "actives" represents a tab.
-            var actives = [[], [], []];
-            // "current" is used to know which tab we're looking into.
-            var current = 0;
-            onEachLazy(document.getElementById("results").childNodes, function(e) {
-                onEachLazy(e.getElementsByClassName("highlighted"), function(h_e) {
-                    actives[current].push(h_e);
-                });
-                current += 1;
-            });
-            var SHIFT = 16;
-            var CTRL = 17;
-            var ALT = 18;
+    function nextTab(direction) {
+        var next = (searchState.currentTab + direction + 3) % searchState.focusedByTab.length;
+        searchState.focusedByTab[searchState.currentTab] = document.activeElement;
+        printTab(next);
+        focusSearchResult();
+    }
 
-            var currentTab = searchState.currentTab;
-            if (e.which === 38) { // up
-                if (e.ctrlKey) { // Going through result tabs.
-                    printTab(currentTab > 0 ? currentTab - 1 : 2);
-                } else {
-                    if (!actives[currentTab].length ||
-                        !actives[currentTab][0].previousElementSibling) {
-                        return;
-                    }
-                    addClass(actives[currentTab][0].previousElementSibling, "highlighted");
-                    removeClass(actives[currentTab][0], "highlighted");
-                }
-                e.preventDefault();
-            } else if (e.which === 40) { // down
-                if (e.ctrlKey) { // Going through result tabs.
-                    printTab(currentTab > 1 ? 0 : currentTab + 1);
-                } else if (!actives[currentTab].length) {
-                    var results = document.getElementById("results").childNodes;
-                    if (results.length > 0) {
-                        var res = results[currentTab].getElementsByClassName("result");
-                        if (res.length > 0) {
-                            addClass(res[0], "highlighted");
-                        }
-                    }
-                } else if (actives[currentTab][0].nextElementSibling) {
-                    addClass(actives[currentTab][0].nextElementSibling, "highlighted");
-                    removeClass(actives[currentTab][0], "highlighted");
-                }
-                e.preventDefault();
-            } else if (e.which === 13) { // return
-                if (actives[currentTab].length) {
-                    var elem = actives[currentTab][0].getElementsByTagName("a")[0];
-                    document.location.href = elem.href;
-                }
-            } else if ([SHIFT, CTRL, ALT].indexOf(e.which) !== -1) {
-                // Does nothing, it's just to avoid losing "focus" on the highlighted element.
-            } else if (actives[currentTab].length > 0) {
-                removeClass(actives[currentTab][0], "highlighted");
-            }
-        };
+    // focus the first search result on the active tab, or the result that
+    // was focused last time this tab was active.
+    function focusSearchResult() {
+        var target = searchState.focusedByTab[searchState.currentTab] ||
+          document.querySelectorAll(".search-results.active a").item(0) ||
+          document.querySelectorAll("#titles > button").item(searchState.currentTab);
+        if (target) {
+            target.focus();
+        }
     }
 
     function buildHrefAndPath(item) {
@@ -1044,16 +960,16 @@ window.initSearch = function(rawSearchIndex) {
     }
 
     function addTab(array, query, display) {
-        var extraStyle = "";
-        if (display === false) {
-            extraStyle = " style=\"display: none;\"";
+        var extraClass = "";
+        if (display === true) {
+            extraClass = " active";
         }
 
         var output = "";
         var duplicates = {};
         var length = 0;
         if (array.length > 0) {
-            output = "<table class=\"search-results\"" + extraStyle + ">";
+            output = "<div class=\"search-results " + extraClass + "\">";
 
             array.forEach(function(item) {
                 var name, type;
@@ -1069,20 +985,19 @@ window.initSearch = function(rawSearchIndex) {
                 }
                 length += 1;
 
-                output += "<tr class=\"" + type + " result\"><td>" +
-                          "<a href=\"" + item.href + "\">" +
+                output += "<a class=\"result-" + type + "\" href=\"" + item.href + "\">" +
+                          "<div><div class=\"result-name\">" +
                           (item.is_alias === true ?
                            ("<span class=\"alias\"><b>" + item.alias + " </b></span><span " +
                               "class=\"grey\"><i>&nbsp;- see&nbsp;</i></span>") : "") +
                           item.displayPath + "<span class=\"" + type + "\">" +
-                          name + "</span></a></td><td>" +
-                          "<a href=\"" + item.href + "\">" +
+                          name + "</span></div><div>" +
                           "<span class=\"desc\">" + item.desc +
-                          "&nbsp;</span></a></td></tr>";
+                          "&nbsp;</span></div></div></a>";
             });
-            output += "</table>";
+            output += "</div>";
         } else {
-            output = "<div class=\"search-failed\"" + extraStyle + ">No results :(<br/>" +
+            output = "<div class=\"search-failed\"" + extraClass + ">No results :(<br/>" +
                 "Try on <a href=\"https://duckduckgo.com/?q=" +
                 encodeURIComponent("rust " + query.query) +
                 "\">DuckDuckGo</a>?<br/><br/>" +
@@ -1118,7 +1033,7 @@ window.initSearch = function(rawSearchIndex) {
         {
             var elem = document.createElement("a");
             elem.href = results.others[0].href;
-            elem.style.display = "none";
+            removeClass(elem, "active");
             // For firefox, we need the element to be in the DOM so it can be clicked.
             document.body.appendChild(elem);
             elem.click();
@@ -1159,7 +1074,6 @@ window.initSearch = function(rawSearchIndex) {
 
         search.innerHTML = output;
         searchState.showResults(search);
-        initSearchNav();
         var elems = document.getElementById("titles").childNodes;
         elems[0].onclick = function() { printTab(0); };
         elems[1].onclick = function() { printTab(1); };
@@ -1437,6 +1351,50 @@ window.initSearch = function(rawSearchIndex) {
         };
         searchState.input.onpaste = searchState.input.onchange;
 
+        searchState.outputElement().addEventListener("keydown", function(e) {
+            // We only handle unmodified keystrokes here. We don't want to interfere with,
+            // for instance, alt-left and alt-right for history navigation.
+            if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
+                return;
+            }
+            // up and down arrow select next/previous search result, or the
+            // search box if we're already at the top.
+            if (e.which === 38) { // up
+                var previous = document.activeElement.previousElementSibling;
+                if (previous) {
+                    console.log("previousElementSibling", previous);
+                    previous.focus();
+                } else {
+                    searchState.focus();
+                }
+                e.preventDefault();
+            } else if (e.which === 40) { // down
+                var next = document.activeElement.nextElementSibling;
+                if (next) {
+                    next.focus();
+                }
+                var rect = document.activeElement.getBoundingClientRect();
+                if (window.innerHeight - rect.bottom < rect.height) {
+                    window.scrollBy(0, rect.height);
+                }
+                e.preventDefault();
+            } else if (e.which === 37) { // left
+                nextTab(-1);
+                e.preventDefault();
+            } else if (e.which === 39) { // right
+                nextTab(1);
+                e.preventDefault();
+            }
+        });
+
+        searchState.input.addEventListener("keydown", function(e) {
+            if (e.which === 40) { // down
+                focusSearchResult();
+                e.preventDefault();
+            }
+        });
+
+
         var selectCrate = document.getElementById("crate-search");
         if (selectCrate) {
             selectCrate.onchange = function() {
diff --git a/src/librustdoc/html/static/themes/ayu.css b/src/librustdoc/html/static/themes/ayu.css
index 13d1a475e4b..cf3b4b4b936 100644
--- a/src/librustdoc/html/static/themes/ayu.css
+++ b/src/librustdoc/html/static/themes/ayu.css
@@ -151,13 +151,16 @@ pre, .rustdoc.source .example-wrap {
 	color: #c5c5c5;
 }
 
-.content .highlighted {
+.content a:hover {
+	background-color: #777;
+}
+
+.content a:focus {
 	color: #000 !important;
 	background-color: #c6afb3;
 }
-.content .highlighted a, .content .highlighted span { color: #000 !important; }
-.content .highlighted {
-	background-color: #c6afb3;
+.content a:focus {
+	color: #000 !important;
 }
 .search-results a {
 	color: #0096cf;
@@ -432,31 +435,21 @@ individually rather than as a group) */
 /* FIXME: these rules should be at the bottom of the file but currently must be
 above the `@media (max-width: 700px)` rules due to a bug in the css checker */
 /* see https://github.com/rust-lang/rust/pull/71237#issuecomment-618170143 */
-.content .highlighted.mod, .content .highlighted.externcrate {}
 .search-input:focus {}
 .content span.attr,.content a.attr,.block a.current.attr,.content span.derive,.content a.derive,
 .block a.current.derive,.content span.macro,.content a.macro,.block a.current.macro {}
-.content .highlighted.trait {}
 .content span.struct,.content a.struct,.block a.current.struct {}
 #titles>button:hover,#titles>button.selected {}
-.content .highlighted.traitalias {}
 .content span.type,.content a.type,.block a.current.type {}
 .content span.union,.content a.union,.block a.current.union {}
-.content .highlighted.foreigntype {}
 pre.rust .lifetime {}
-.content .highlighted.primitive {}
-.content .highlighted.constant,.content .highlighted.static {}
 .stab.unstable {}
-.content .highlighted.fn,.content .highlighted.method,.content .highlighted.tymethod {}
 h2,h3:not(.impl):not(.method):not(.type):not(.tymethod),h4:not(.method):not(.type):not(.tymethod) {}
 .content span.enum,.content a.enum,.block a.current.enum {}
 .content span.constant,.content a.constant,.block a.current.constant,.content span.static,
-.content a.static,.block a.current.static {}
+.content a.static, .block a.current.static {}
 .content span.keyword,.content a.keyword,.block a.current.keyword {}
 pre.rust .comment {}
-.content .highlighted.enum {}
-.content .highlighted.struct {}
-.content .highlighted.keyword {}
 .content span.traitalias,.content a.traitalias,.block a.current.traitalias {}
 .content span.fn,.content a.fn,.block a.current.fn,.content span.method,.content a.method,
 .block a.current.method,.content span.tymethod,.content a.tymethod,.block a.current.tymethod,
@@ -467,15 +460,36 @@ pre.rust .attribute .ident {}
 .content span.foreigntype,.content a.foreigntype,.block a.current.foreigntype {}
 pre.rust .doccomment {}
 .stab.deprecated {}
-.content .highlighted.attr,.content .highlighted.derive,.content .highlighted.macro {}
+.content a.attr,.content a.derive,.content a.macro {}
 .stab.portability {}
-.content .highlighted.union {}
 .content span.primitive,.content a.primitive,.block a.current.primitive {}
 .content span.externcrate,.content span.mod,.content a.mod,.block a.current.mod {}
-.content .highlighted.type {}
 pre.rust .kw-2,pre.rust .prelude-ty {}
 .content span.trait,.content a.trait,.block a.current.trait {}
 
+.search-results a:focus span {}
+a.result-trait:focus {}
+a.result-traitalias:focus {}
+a.result-mod:focus,
+a.result-externcrate:focus {}
+a.result-mod:focus {}
+a.result-externcrate:focus {}
+a.result-enum:focus {}
+a.result-struct:focus {}
+a.result-union:focus {}
+a.result-fn:focus,
+a.result-method:focus,
+a.result-tymethod:focus {}
+a.result-type:focus {}
+a.result-foreigntype:focus {}
+a.result-attr:focus,
+a.result-derive:focus,
+a.result-macro:focus {}
+a.result-constant:focus,
+a.result-static:focus {}
+a.result-primitive:focus {}
+a.result-keyword:focus {}
+
 @media (max-width: 700px) {
 	.sidebar-menu {
 		background-color: #14191f;
diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css
index 945ca9b252e..f66f9bbbc89 100644
--- a/src/librustdoc/html/static/themes/dark.css
+++ b/src/librustdoc/html/static/themes/dark.css
@@ -109,32 +109,36 @@ pre, .rustdoc.source .example-wrap {
 	color: #ddd;
 }
 
-.content .highlighted {
+.content a:hover {
+	background-color: #777;
+}
+
+.content a:focus {
 	color: #eee !important;
 	background-color: #616161;
 }
-.content .highlighted a, .content .highlighted span { color: #eee !important; }
-.content .highlighted.trait { background-color: #013191; }
-.content .highlighted.traitalias { background-color: #013191; }
-.content .highlighted.mod,
-.content .highlighted.externcrate { background-color: #afc6e4; }
-.content .highlighted.mod { background-color: #803a1b; }
-.content .highlighted.externcrate { background-color: #396bac; }
-.content .highlighted.enum { background-color: #5b4e68; }
-.content .highlighted.struct { background-color: #194e9f; }
-.content .highlighted.union { background-color: #b7bd49; }
-.content .highlighted.fn,
-.content .highlighted.method,
-.content .highlighted.tymethod { background-color: #4950ed; }
-.content .highlighted.type { background-color: #38902c; }
-.content .highlighted.foreigntype { background-color: #b200d6; }
-.content .highlighted.attr,
-.content .highlighted.derive,
-.content .highlighted.macro { background-color: #217d1c; }
-.content .highlighted.constant,
-.content .highlighted.static { background-color: #0063cc; }
-.content .highlighted.primitive { background-color: #00708a; }
-.content .highlighted.keyword { background-color: #884719; }
+.search-results a:focus span { color: #eee !important; }
+a.result-trait:focus { background-color: #013191; }
+a.result-traitalias:focus { background-color: #013191; }
+a.result-mod:focus,
+a.result-externcrate:focus { background-color: #afc6e4; }
+a.result-mod:focus { background-color: #803a1b; }
+a.result-externcrate:focus { background-color: #396bac; }
+a.result-enum:focus { background-color: #5b4e68; }
+a.result-struct:focus { background-color: #194e9f; }
+a.result-union:focus { background-color: #b7bd49; }
+a.result-fn:focus,
+a.result-method:focus,
+a.result-tymethod:focus { background-color: #4950ed; }
+a.result-type:focus { background-color: #38902c; }
+a.result-foreigntype:focus { background-color: #b200d6; }
+a.result-attr:focus,
+a.result-derive:focus,
+a.result-macro:focus { background-color: #217d1c; }
+a.result-constant:focus,
+a.result-static:focus { background-color: #0063cc; }
+a.result-primitive:focus { background-color: #00708a; }
+a.result-keyword:focus { background-color: #884719; }
 
 .content .item-info::before { color: #ccc; }
 
diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css
index 2ed638bdd4f..872160863b2 100644
--- a/src/librustdoc/html/static/themes/light.css
+++ b/src/librustdoc/html/static/themes/light.css
@@ -109,30 +109,34 @@ pre, .rustdoc.source .example-wrap {
 	color: #4E4C4C;
 }
 
-.content .highlighted {
+.content a:hover {
+	background-color: #ddd;
+}
+
+.content a:focus {
 	color: #000 !important;
 	background-color: #ccc;
 }
-.content .highlighted a, .content .highlighted span { color: #000 !important; }
-.content .highlighted.trait { background-color: #c7b6ff; }
-.content .highlighted.traitalias { background-color: #c7b6ff; }
-.content .highlighted.mod,
-.content .highlighted.externcrate { background-color: #afc6e4; }
-.content .highlighted.enum { background-color: #b4d1b9; }
-.content .highlighted.struct { background-color: #e7b1a0; }
-.content .highlighted.union { background-color: #b7bd49; }
-.content .highlighted.fn,
-.content .highlighted.method,
-.content .highlighted.tymethod { background-color: #c6afb3; }
-.content .highlighted.type { background-color: #ffc891; }
-.content .highlighted.foreigntype { background-color: #f5c4ff; }
-.content .highlighted.attr,
-.content .highlighted.derive,
-.content .highlighted.macro { background-color: #8ce488; }
-.content .highlighted.constant,
-.content .highlighted.static { background-color: #c3e0ff; }
-.content .highlighted.primitive { background-color: #9aecff; }
-.content .highlighted.keyword { background-color: #f99650; }
+.search-results a:focus span { color: #000 !important; }
+a.result-trait:focus { background-color: #c7b6ff; }
+a.result-traitalias:focus { background-color: #c7b6ff; }
+a.result-mod:focus,
+a.result-externcrate:focus { background-color: #afc6e4; }
+a.result-enum:focus { background-color: #b4d1b9; }
+a.result-struct:focus { background-color: #e7b1a0; }
+a.result-union:focus { background-color: #b7bd49; }
+a.result-fn:focus,
+a.result-method:focus,
+a.result-tymethod:focus { background-color: #c6afb3; }
+a.result-type:focus { background-color: #ffc891; }
+a.result-foreigntype:focus { background-color: #f5c4ff; }
+a.result-attr:focus,
+a.result-derive:focus,
+a.result-macro:focus { background-color: #8ce488; }
+a.result-constant:focus,
+a.result-static:focus { background-color: #c3e0ff; }
+a.result-primitive:focus { background-color: #9aecff; }
+a.result-keyword:focus { background-color: #f99650; }
 
 .content .item-info::before { color: #ccc; }