Add client-side search with / shortcut
continuous-integration/drone/push Build encountered an error
continuous-integration/drone Build is failing

Lazy-loads /index.json on first keystroke, fuzzy-matches titles and
descriptions, keyboard-navigable results (arrows, Enter, Esc).
Based on https://gist.github.com/cmod/5410eae147e4318164258742dd053993

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-30 07:58:35 +02:00
parent 561c0fa320
commit 318c62c289
6 changed files with 355 additions and 2 deletions
+95 -1
View File
@@ -266,7 +266,7 @@ nav {
justify-content: flex-end;
flex-flow: row wrap;
overflow: hidden;
margin: auto 2% .6em auto;
margin: 0 2% 0 auto;
padding-left: 2em;
}
@@ -342,6 +342,7 @@ nav li:hover {
text-align: center;
display: flex;
flex-flow: row wrap;
align-items: center;
/* $mq-mini or smaller: */ margin: 0; width: 100%; font-size: $base-font-size * 0.8;
@media #{$mq-small} { margin: 0 5%; width: 90%; font-size: $base-font-size * 0.9; }
@@ -779,3 +780,96 @@ table {
td {
}
}
// Search — sits in the header, replaces nav when active
#fastSearch {
display: none; // toggled to flex by JS
flex: 1;
align-items: center;
align-self: center; // vertically align with the header text
position: relative; // anchor for the results dropdown
margin: 0;
input#searchInput {
flex: 1;
width: 100%;
padding: .25em .63em;
font-size: 1.1em; // matches nav a font-size
color: $text-dark;
background-color: transparent;
border: none; // header border-bottom already provides the bottom line
outline: none;
box-sizing: border-box;
&::placeholder { color: $text-very-light; font-style: italic; }
}
ul#searchResults {
position: absolute;
top: 100%;
right: 0;
left: 0;
list-style: none;
margin: 0;
padding: 0;
background-color: #f7f7f7; // matches header bg
border: 1px solid $tab-border-color;
border-top: 2px solid $ipng-orange;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 200;
&:empty { display: none; }
li {
border-bottom: 1px solid $tab-border-color;
&:last-child { border-bottom: none; }
&.search-message {
padding: .5em .63em;
color: $text-normal;
font-style: italic;
font-size: .9em;
}
a {
display: block;
padding: .4em .63em;
text-decoration: none;
color: $text-dark;
&:hover, &:focus {
outline: none;
background-color: $ipng-orange;
color: #fff;
.meta, .desc { color: rgba(255,255,255,0.85); }
}
.title {
display: block;
font-size: 1em;
font-weight: bold;
}
.meta {
display: block;
font-size: .72em;
color: $text-normal;
text-transform: uppercase;
letter-spacing: .04em;
margin: .1em 0;
}
.desc {
display: block;
font-size: .85em;
color: $text-normal;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
}
@@ -7,5 +7,6 @@
{{- block "main" . }}{{- end }}
</div>
{{- partial "footer.html" . -}}
<script src="/js/fastsearch.js"></script>
</body>
</html>
@@ -3,7 +3,7 @@
<div class="myname">
<h2><a href="{{ default .Site.Home .Site.BaseURL }}">{{ default .Site.Params.Author .Site.Params.siteHeading }}</a></h2>
</div>
<nav>
<nav id="mainNav">
<ul class="navbar">
{{- /* info about current page */ -}}
{{- $currentPage := . -}}
@@ -67,4 +67,8 @@
{{- end }}
</ul>
</nav>
<div id="fastSearch">
<input id="searchInput" tabindex="0" placeholder="Search…" aria-label="Search" autocomplete="off">
<ul id="searchResults"></ul>
</div>
</header>