feat(design): single-page consolidation, drop typeface picker

Merge projects + homelab + timeline into one scrollable page.
Remove typeface picker (sans/serif/mono), keep theme toggle.
Nav simplified to name + toggle. Hero gains anchor links for
in-page navigation (#projects, #journey, #homelab). Old pages
become meta-refresh redirects. Timeline redesigned as two-column
grid layout — date left, content right — cutting vertical space
~50%. Focus states added for keyboard nav. Tags dropped from
timeline entries.
This commit is contained in:
2026-05-24 19:36:19 -04:00
parent de74019e48
commit 141d66d7bb
9 changed files with 275 additions and 376 deletions
+6
View File
@@ -42,4 +42,10 @@
Email
</a>
</nav>
<nav class="flex flex-wrap items-center gap-x-2ch gap-y-half-lh text-[var(--color-text-dim)] mt-half-lh">
<a href="#projects" class="underline hover:text-[var(--color-text)]">Projects</a>
<a href="#journey" class="underline hover:text-[var(--color-text)]">Journey</a>
<a href="#homelab" class="underline hover:text-[var(--color-text)]">Homelab</a>
</nav>
</section>
+8 -74
View File
@@ -1,53 +1,14 @@
---
const pathname = Astro.url.pathname;
const links = [
{ href: "/", label: "tyler" },
{ href: "/homelab/", label: "homelab" },
{ href: "/projects/", label: "projects" },
];
---
<header class="sticky top-0 z-50 bg-[var(--color-bg)] border-b border-[var(--color-border)]">
<nav class="max-w-[740px] mx-auto px-4ch h-11 flex items-center justify-between">
<ul class="flex items-center gap-2ch">
{links.map(({ href, label }) => {
const active = pathname === href || pathname === href.replace(/\/$/, "");
return (
<li>
<a
href={href}
aria-current={active ? "page" : undefined}
class:list={[
active
? "text-[var(--color-text)]"
: "text-[var(--color-text-label)] hover:text-[var(--color-text)]",
]}
>
{label}
</a>
</li>
);
})}
</ul>
<a href="/" class="font-semibold">Tyler Koenig</a>
<div class="flex items-center gap-2ch">
<div role="group" aria-label="Typeface" class="flex items-center gap-[0.5ch] text-[var(--color-text-dim)]">
<button data-typeface-btn="sans" class="hover:text-[var(--color-text)] cursor-pointer">sans</button>
<span aria-hidden="true">/</span>
<button data-typeface-btn="serif" class="hover:text-[var(--color-text)] cursor-pointer">serif</button>
<span aria-hidden="true">/</span>
<button data-typeface-btn="mono" class="hover:text-[var(--color-text)] cursor-pointer">mono</button>
</div>
<button
data-theme-toggle
aria-label="Switch to light mode"
class="text-[var(--color-text-label)] hover:text-[var(--color-text)] cursor-pointer"
>
light
</button>
</div>
<button
data-theme-toggle
aria-label="Switch to light mode"
class="text-[var(--color-text-label)] hover:text-[var(--color-text)] cursor-pointer"
>
light
</button>
</nav>
</header>
@@ -71,31 +32,4 @@ const links = [
});
updateTheme();
const tfBtns = document.querySelectorAll("[data-typeface-btn]");
function updateTypeface() {
const current = document.documentElement.dataset.typeface || "sans";
tfBtns.forEach((b) => {
const val = b.getAttribute("data-typeface-btn");
if (val === current) {
b.classList.add("font-bold", "text-[var(--color-text)]");
b.classList.remove("text-[var(--color-text-dim)]");
} else {
b.classList.remove("font-bold", "text-[var(--color-text)]");
b.classList.add("text-[var(--color-text-dim)]");
}
});
}
tfBtns.forEach((b) => {
b.addEventListener("click", () => {
const val = b.getAttribute("data-typeface-btn")!;
document.documentElement.dataset.typeface = val;
localStorage.setItem("lerko96-typeface", val);
updateTypeface();
});
});
updateTypeface();
</script>
+12 -17
View File
@@ -14,29 +14,24 @@ const typeLabel: Record<TimelineType, string> = {
---
<Widget title="Journey">
<ol class="flex flex-col gap-0">
<ol class="flex flex-col">
{timeline.map((entry) => (
<li class="pb-2lh last:pb-0">
<p class="text-[var(--color-text-dim)] mb-qtr-lh">
<li class="grid grid-cols-[10ch_1fr] gap-2ch py-half-lh border-b border-[var(--color-border)] last:border-b-0">
<div class="text-[var(--color-text-dim)] pt-[0.1em]">
{isDate(entry.date)
? <time datetime={entry.date}>{entry.date}</time>
: <span>{entry.date}</span>
}
<span class="mx-[0.5ch]">·</span>
<em>{typeLabel[entry.type]}</em>
</p>
<h3 class="font-semibold mb-half-lh">{entry.title}</h3>
<p class="text-[var(--color-text-label)] leading-relaxed mb-half-lh">
{entry.description}
</p>
{entry.tags && entry.tags.length > 0 && (
<p class="text-[var(--color-text-dim)]">
{entry.tags.join(" · ")}
</div>
<div>
<h3 class="font-semibold">
{entry.title}
<span class="font-normal text-[var(--color-text-dim)]"> · {typeLabel[entry.type]}</span>
</h3>
<p class="text-[var(--color-text-label)] leading-relaxed">
{entry.description}
</p>
)}
</div>
</li>
))}
</ol>