]> git.zndr.dk Git - zndr-11ty.git/commitdiff
Search bar
authorJannik ZANDER <jzander@grundfos.com>
Tue, 9 Sep 2025 18:29:16 +0000 (20:29 +0200)
committerJannik ZANDER <jzander@grundfos.com>
Tue, 9 Sep 2025 18:29:16 +0000 (20:29 +0200)
site/_includes/layouts/base.njk
site/index.njk

index 973f5595a308c1d099c15ff3b48a3890d9bc8d41..b25725db2dc3caafbe94f23e3d4cec1b29d75c80 100644 (file)
@@ -46,7 +46,7 @@
     {% endblock %}
 
     <footer>
-      <div>© 2025 Jannik • Tange</div>
+      <div>© {{ '' | year }} Jannik • Tange Sø</div>
       <div class="foot-links">
         <a href="/pgp.txt">PGP</a>
         <a href="/keys.txt">SSH Keys</a>
index 332812dce97cdacce175fd59335af3073991c839..064beb57b6963e59ca25ee3b9254b00f8b8c6032 100644 (file)
@@ -5,6 +5,10 @@ title: zndr.dk — Services
 
 <h2 style="margin:0 0 .5rem 0;">Services</h2>
 
+<div class="search" role="search">
+  <input id="filter" placeholder="Filter … (press / to focus)" autocomplete="off" aria-label="Filter services" />
+</div>
+
 <main class="grid" id="grid">
 {% for s in services %}
   <a
@@ -20,6 +24,60 @@ title: zndr.dk — Services
 {% endfor %}
 </main>
 
+<script>
+(() => {
+  const input = document.getElementById('filter');
+  const cards = Array.from(document.querySelectorAll('.grid .card'));
+  const empty = document.createElement('p');
+  empty.textContent = 'No matches.';
+  empty.style.display = 'none';
+  empty.style.color = 'var(--muted)';
+  const grid = document.querySelector('.grid');
+  grid.parentNode.insertBefore(empty, grid.nextSibling);
+
+  // Read initial query from ?q=
+  const params = new URLSearchParams(location.search);
+  const initial = params.get('q') || '';
+  if (initial) input.value = initial;
+
+  const norm = s => (s || '').toLowerCase().trim();
+  const matches = (el, q) => {
+    if (!q) return true;
+    // search name, description, tag, and any data-* attributes
+    const name = el.querySelector('h3')?.textContent || '';
+    const desc = el.querySelector('p')?.textContent || '';
+    const tag  = el.getAttribute('data-tag') || '';
+    const hay  = `${name}\n${desc}\n${tag}\n${el.textContent}`;
+    return norm(hay).includes(q);
+  };
+
+  let t;
+  const apply = () => {
+    const q = norm(input.value);
+    let shown = 0;
+    cards.forEach(card => {
+      const ok = matches(card, q);
+      card.style.display = ok ? '' : 'none';
+      if (ok) shown++;
+    });
+    empty.style.display = shown ? 'none' : '';
+    // reflect in URL (so refresh/bookmark keeps query)
+    const url = new URL(location);
+    if (q) { url.searchParams.set('q', q); }
+    else   { url.searchParams.delete('q'); }
+    history.replaceState(null, '', url);
+  };
+
+  input.addEventListener('input', () => {
+    clearTimeout(t);
+    t = setTimeout(apply, 80); // tiny debounce
+  });
+
+  // run once on load
+  apply();
+})();
+</script>
+
 <script>
 (function () {
   const PING_URL = "https://lan.zndr.dk/ping";   // 200 only on LAN