wip: DRY up things

This commit is contained in:
2026-01-11 02:16:23 +00:00
parent 9f1af55602
commit d2efcb01ba
23 changed files with 326 additions and 471 deletions

View File

@@ -1,6 +1,7 @@
---
import VersionSelector from "./VersionSelector.astro";
import ThemeToggle from "./ThemeToggle.astro";
import GitHubIcon from "./icons/GitHubIcon.astro";
import { config } from "../config";
interface Props {
@@ -8,6 +9,12 @@ interface Props {
}
const { version } = Astro.props;
const navItems = [
{ id: "about", label: "About" },
{ id: "spec", label: "Spec" },
{ id: "faq", label: "FAQ" },
];
---
<header
@@ -41,42 +48,22 @@ const { version } = Astro.props;
<!-- Desktop Navigation -->
<nav class="hidden md:flex items-center gap-1">
<a
href="#about"
class="nav-link inline-flex items-center px-4 py-2 text-sm font-medium
rounded-lg transition-colors cursor-pointer
text-gray-600 dark:text-neutral-400
hover:bg-gray-100 hover:text-gray-950
dark:hover:bg-neutral-800 dark:hover:text-neutral-50"
data-nav-link
data-section-id="about"
>
About
</a>
<a
href="#spec"
class="nav-link inline-flex items-center px-4 py-2 text-sm font-medium
rounded-lg transition-colors cursor-pointer
text-gray-600 dark:text-neutral-400
hover:bg-gray-100 hover:text-gray-950
dark:hover:bg-neutral-800 dark:hover:text-neutral-50"
data-nav-link
data-section-id="spec"
>
Spec
</a>
<a
href="#faq"
class="nav-link inline-flex items-center px-4 py-2 text-sm font-medium
rounded-lg transition-colors cursor-pointer
text-gray-600 dark:text-neutral-400
hover:bg-gray-100 hover:text-gray-950
dark:hover:bg-neutral-800 dark:hover:text-neutral-50"
data-nav-link
data-section-id="faq"
>
FAQ
</a>
{
navItems.map((item) => (
<a
href={`#${item.id}`}
class="nav-link inline-flex items-center px-4 py-2 text-sm font-medium
rounded-lg transition-colors cursor-pointer
text-gray-600 dark:text-neutral-400
hover:bg-gray-100 hover:text-gray-950
dark:hover:bg-neutral-800 dark:hover:text-neutral-50"
data-nav-link
data-section-id={item.id}
>
{item.label}
</a>
))
}
</nav>
<!-- Right side: Theme, GitHub -->
@@ -93,12 +80,7 @@ const { version } = Astro.props;
hover:bg-gray-100 dark:hover:bg-neutral-800"
aria-label="View on GitHub"
>
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path
fill-rule="evenodd"
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
clip-rule="evenodd"></path>
</svg>
<GitHubIcon />
</a>
<!-- Mobile menu button -->
@@ -137,38 +119,26 @@ const { version } = Astro.props;
versions={Array.from(config.versions)}
/>
</div>
<a
href="#about"
class="nav-link block py-2 text-gray-600 dark:text-neutral-400
hover:text-sky-600"
data-nav-link
data-section-id="about"
>
About
</a>
<a
href="#spec"
class="nav-link block py-2 text-gray-600 dark:text-neutral-400
hover:text-sky-600"
data-nav-link
data-section-id="spec"
>
Spec
</a>
<a
href="#faq"
class="nav-link block py-2 text-gray-600 dark:text-neutral-400
hover:text-sky-600"
data-nav-link
data-section-id="faq"
>
FAQ
</a>
{
navItems.map((item) => (
<a
href={`#${item.id}`}
class="nav-link block py-2 text-gray-600 dark:text-neutral-400
hover:text-sky-600"
data-nav-link
data-section-id={item.id}
>
{item.label}
</a>
))
}
</div>
</nav>
</header>
<script>
import { initActiveSectionTracker } from "../scripts/activeSectionTracker";
function initHeader() {
const header = document.getElementById("site-header");
const hero = document.getElementById("hero");
@@ -212,51 +182,10 @@ const { version } = Astro.props;
}
// Active section tracking for nav links
const navLinks = document.querySelectorAll("[data-nav-link]");
const sections: { id: string; element: Element }[] = [];
// Collect unique section IDs
const seenIds = new Set<string>();
navLinks.forEach((link) => {
const id = link.getAttribute("data-section-id");
if (id && !seenIds.has(id)) {
seenIds.add(id);
const section = document.getElementById(id);
if (section) {
sections.push({ id, element: section });
}
}
initActiveSectionTracker({
linkSelector: "[data-nav-link]",
defaultToFirst: false,
});
function updateActiveNavSection() {
const headerOffset = 100;
let activeId: string | null = null;
for (const { id, element } of sections) {
const rect = element.getBoundingClientRect();
if (rect.top <= headerOffset) {
activeId = id;
}
}
navLinks.forEach((link) => {
const linkId = link.getAttribute("data-section-id");
link.classList.toggle("active", linkId === activeId);
});
}
let ticking = false;
window.addEventListener("scroll", () => {
if (!ticking) {
requestAnimationFrame(() => {
updateActiveNavSection();
ticking = false;
});
ticking = true;
}
});
updateActiveNavSection();
}
// Initialize on load