patx/mongokv
Improve navigation and styling in index.html
Commit 8c0065f · patx · 2025-12-28T21:46:04-05:00
Improve navigation and styling in index.html Enhance navigation and styling for better user experience.
Comments
No comments yet.
Diff
diff --git a/docs/index.html b/docs/index.html
index 5081abc..4eb3c8a 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -4,26 +4,34 @@
<meta charset="UTF-8">
<title>mongoKV – Tiny Redis-style Key-Value Store for MongoDB (Python)</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
+
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Pacifico&display=swap" rel="stylesheet">
+
<!-- highlight.js -->
<link rel="stylesheet"
- href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css">
+ href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github.min.css">
<script defer
- src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"></script>
+ src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/highlight.min.js"></script>
<script defer
- src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/python.min.js"></script>
+ src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/python.min.js"></script>
<script defer
- src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/bash.min.js"></script>
+ src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/bash.min.js"></script>
<script defer
- src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/json.min.js"></script>
+ src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/languages/json.min.js"></script>
+
+ <script defer>
+ document.addEventListener("DOMContentLoaded", () => {
+ document.querySelectorAll("pre code").forEach(el => hljs.highlightElement(el));
+
+ const nav = document.getElementById("topnav");
+ const onScroll = () => nav.classList.toggle("scrolled", window.scrollY > 2);
+ onScroll();
+ window.addEventListener("scroll", onScroll, { passive: true });
+ });
+ </script>
-<script defer>
- document.addEventListener("DOMContentLoaded", () => {
- document.querySelectorAll("pre code").forEach(el => hljs.highlightElement(el));
- });
-</script>
<style>
:root {
--bg: #f5f5f5;
@@ -37,6 +45,8 @@
--sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
+ html { scroll-behavior: smooth; }
+
body {
margin: 0;
padding: 0;
@@ -67,13 +77,13 @@
font-size: 4rem;
margin: 0 0 4px;
}
-
+
.logo {
- font-family: 'Pacifico', cursive;
- color: var(--accent);
- letter-spacing: 3px;
+ font-family: 'Pacifico', cursive;
+ color: var(--accent);
+ letter-spacing: 3px;
}
-
+
.subtitle {
color: var(--muted);
margin: 0 0 12px;
@@ -86,18 +96,10 @@
white-space: nowrap;
}
- a {
- color: var(--accent);
- text-decoration: none;
- }
-
- a:hover {
- text-decoration: underline;
- }
+ a { color: var(--accent); text-decoration: none; }
+ a:hover { text-decoration: underline; }
- p {
- margin: 0 0 1rem;
- }
+ p { margin: 0 0 1rem; }
ul, ol {
margin: 0 0 1rem 1.5rem;
@@ -165,28 +167,167 @@
color: var(--accent);
}
- .callout-list li {
- margin-bottom: 0.25rem;
+ .callout-list li { margin-bottom: 0.25rem; }
+
+ .header-content {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ flex-wrap: wrap;
+ gap: 24px;
+ }
+
+ .header-content nav ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ gap: 24px;
+ }
+
+ .header-content nav a {
+ color: var(--fg);
+ text-decoration: none;
+ font-weight: 500;
+ }
+
+ .header-content nav a:hover {
+ color: var(--accent);
+ text-decoration: underline;
+ }
+
+ .navbar{
+ position: sticky;
+ top: 0;
+ z-index: 9999;
+
+ background: rgba(245,245,245,.82);
+ backdrop-filter: blur(10px);
+ -webkit-backdrop-filter: blur(10px);
+
+ border-bottom: 1px solid var(--border);
+ }
+
+ .nav-inner{
+ max-width: 840px;
+ margin: 0 auto;
+ padding: 10px 16px;
+
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 14px;
+ }
+
+ .brand{
+ display: inline-flex;
+ align-items: baseline;
+ gap: 10px;
+ text-decoration: none;
+ }
+
+ .brand-logo{
+ font-family: 'Pacifico', cursive;
+ color: var(--accent);
+ letter-spacing: 2px;
+ font-size: 1.25rem;
+ line-height: 1;
+ }
+
+ .nav-links{
+ list-style: none;
+ margin: 0;
+ padding: 0;
+
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+ }
+
+ .nav-links a{
+ display: inline-flex;
+ align-items: center;
+ height: 36px;
+ padding: 0 12px;
+
+ border-radius: 999px;
+ color: var(--fg);
+ text-decoration: none;
+ font-weight: 600;
+ font-size: 0.95rem;
+
+ transition: transform .15s ease, background .15s ease, color .15s ease, box-shadow .15s ease;
+ }
+
+ .nav-links a:hover{
+ background: rgba(36,102,60,.10);
+ color: var(--accent);
+ box-shadow: 0 4px 14px rgba(0,0,0,.06);
+ transform: translateY(-1px);
+ }
+
+ /* NEW: active section highlight */
+ .nav-links a.active{
+ background: rgba(36,102,60,.16);
+ color: var(--accent);
+ box-shadow: 0 8px 18px rgba(0,0,0,.08);
+ }
+
+ .navbar.scrolled{
+ box-shadow: 0 10px 24px rgba(0,0,0,.08);
+ }
+ /* Hide brand logo until scrolled past header */
+ .brand-logo {
+ opacity: 0;
+ transform: translateY(-4px);
+ pointer-events: none;
+ transition: opacity .2s ease, transform .2s ease;
+ }
+
+ /* Show brand logo after scrolling */
+ .navbar.show-logo .brand-logo {
+ opacity: 1;
+ transform: translateY(0);
+ pointer-events: auto;
}
</style>
</head>
+
<body>
- <div class="page">
+ <nav class="navbar" id="topnav">
+ <div class="nav-inner">
+ <a class="brand" href="#top">
+ <span class="brand-logo">mongoKV</span>
+ </a>
+
+ <ul class="nav-links">
+ <li><a href="#guide" data-spy="guide">Guide</a></li>
+ <li><a href="#api" data-spy="api">API</a></li>
+ <li><a href="https://github.com/patx/mongokv" target="_blank" rel="noreferrer">GitHub</a></li>
+ <li><a href="https://pypi.org/project/mongokv/" target="_blank" rel="noreferrer">PyPI</a></li>
+ </ul>
+ </div>
+ </nav>
+
+ <div class="page" id="top">
<header>
- <h1><span class="logo">mongoKV</span> <span class="pronounce">/ˈmɑːŋɡoʊ kiː ˈvæljuː/</span></h1>
- <p class="subtitle">Tiny async/sync key–value store on top of PyMongo.</p>
- <p class="tagline">
- <a href="https://github.com/patx/mongokv" target="_blank" rel="noopener noreferrer">GitHub</a>
- ·
- <a href="https://pypi.org/project/mongokv/" target="_blank" rel="noopener noreferrer">PyPI</a>
- ·
- <a href="https://harrisonerd.com/" target="_blank" rel="noopener noreferrer">Author</a>
+ <div class="header-content">
+ <div>
+ <h1><span class="logo">mongoKV</span> <span class="pronounce">/ˈmɑːŋɡoʊ kiː ˈvæljuː/</span></h1>
+ <p class="subtitle">Tiny async/sync key–value store on top of PyMongo.</p>
+ <p class="tagline">
+ <a href="https://harrisonerd.com/" target="_blank" rel="noopener noreferrer">Harrison Erd</a>
· BSD 3-Clause
- </p>
+ </p>
+ </div>
+
+ </div>
</header>
<section class="section" style="border: 0px;">
- <h2>User Guide</h2>
+ <h2 id="guide">User Guide</h2>
<p><code>mongoKV</code> is a tiny key–value store wrapper around MongoDB using PyMongo.</p>
<ul class="callout-list">
<li>One collection per instance, one document per key</li>
@@ -302,7 +443,7 @@ asyncio.run(main())</code></pre>
</section>
<section class="section api-heading" style="border: 0px;">
- <h2>API Reference</h2>
+ <h2 id="api">API Reference</h2>
</section>
<section class="section">
@@ -503,6 +644,9 @@ print(keys) # -> ["user:1", "note:abc", ...]</code></pre>
</ul>
</li>
</ul>
+
+ <h4>Returns</h4>
+ <p><code>None</code></p>
<h4>Examples (sync)</h4>
<pre><code class="language-python">db.close()</code></pre>
@@ -547,6 +691,98 @@ except PyMongoError as e:
</section>
</div>
+
+<script defer>
+ document.addEventListener("DOMContentLoaded", () => {
+ document.querySelectorAll("pre code").forEach(el => hljs.highlightElement(el));
+
+ const nav = document.getElementById("topnav");
+ const header = document.querySelector("header");
+
+ const guideEl = document.getElementById("guide");
+ const apiEl = document.getElementById("api");
+
+ const guideLink = document.querySelector('.nav-links a[href="#guide"]');
+ const apiLink = document.querySelector('.nav-links a[href="#api"]');
+
+ const clearActive = () => {
+ [guideLink, apiLink].forEach(a => a && a.classList.remove("active"));
+ };
+
+ const setActive = (which) => {
+ clearActive();
+ if (which === "guide" && guideLink) guideLink.classList.add("active");
+ if (which === "api" && apiLink) apiLink.classList.add("active");
+ };
+
+ // Decide which section should be active RIGHT NOW (when spy is enabled)
+ const computeActiveSection = () => {
+ if (!guideEl || !apiEl) return null;
+
+ const y = 90; // "reading line" just below sticky nav
+ const g = guideEl.getBoundingClientRect();
+ const a = apiEl.getBoundingClientRect();
+
+ const guidePassed = g.top <= y;
+ const apiPassed = a.top <= y;
+
+ if (apiPassed) return "api";
+
+ if (guidePassed) return "guide";
+
+ return null;
+ };
+
+ let spyEnabled = false;
+
+ const syncNavState = () => {
+ const headerBottom = header.getBoundingClientRect().bottom;
+ const shouldEnable = headerBottom <= 0;
+
+ nav.classList.toggle("scrolled", window.scrollY > 2);
+ nav.classList.toggle("show-logo", shouldEnable);
+
+ if (!shouldEnable) {
+ spyEnabled = false;
+ clearActive();
+ return;
+ }
+
+ spyEnabled = true;
+
+ const which = computeActiveSection();
+ if (which) setActive(which);
+ else clearActive();
+ };
+
+ syncNavState();
+ window.addEventListener("scroll", syncNavState, { passive: true });
+ window.addEventListener("resize", syncNavState, { passive: true });
+
+ const onNavClick = (which) => (e) => {
+ if (!spyEnabled) return;
+ setActive(which);
+ };
+ if (guideLink) guideLink.addEventListener("click", onNavClick("guide"));
+ if (apiLink) apiLink.addEventListener("click", onNavClick("api"));
+
+ if ("IntersectionObserver" in window && guideEl && apiEl) {
+ const observer = new IntersectionObserver(() => {
+ if (!spyEnabled) return;
+ const which = computeActiveSection();
+ if (which) setActive(which);
+ else clearActive();
+ }, {
+ root: null,
+ rootMargin: "-72px 0px -55% 0px",
+ threshold: [0, 0.1, 0.25, 0.5]
+ });
+
+ observer.observe(guideEl);
+ observer.observe(apiEl);
+ }
+ });
+</script>
+
</body>
</html>
-