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:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user