UI touch ups
Some checks failed
ci / site (push) Has been cancelled
publish-image / publish (push) Has been cancelled

This commit is contained in:
2026-02-10 23:06:52 -05:00
parent 07d8787972
commit 439b886a1b
23 changed files with 412 additions and 9 deletions

View File

@@ -10,6 +10,10 @@ PUBLIC_UMAMI_WEBSITE_ID=00000000-0000-0000-0000-000000000000
# - In `npm run build`/`preview`, SW is registered only if this is "true".
PUBLIC_ENABLE_SW=true
# Nav/title hover-line effect toggle (public, build-time).
# Set to "false" to disable the decorative hover-line treatment.
PUBLIC_ENABLE_NAV_HOVER_LINE=true
# Content ingestion configuration (used by scripts)
YOUTUBE_CHANNEL_ID=UCxxxxxxxxxxxxxxxxxxxxxx
YOUTUBE_API_KEY=

1
site/src/env.d.ts vendored
View File

@@ -6,6 +6,7 @@ interface ImportMetaEnv {
readonly PUBLIC_UMAMI_WEBSITE_ID?: string;
readonly PUBLIC_ENABLE_SW?: string;
readonly PUBLIC_ASSET_VERSION?: string;
readonly PUBLIC_ENABLE_NAV_HOVER_LINE?: string;
}
interface ImportMeta {

View File

@@ -16,6 +16,7 @@ const siteUrl = (cfg.siteUrl || "http://localhost:4321").replace(/\/$/, "");
const canonicalUrl = `${siteUrl}${canonicalPath.startsWith("/") ? canonicalPath : `/${canonicalPath}`}`;
const assetSuffix = cfg.assetVersion ? `?v=${encodeURIComponent(cfg.assetVersion)}` : "";
const bodyClass = cfg.enableNavHoverLine ? "nav-hover-line-enabled" : "";
---
<!doctype html>
@@ -131,11 +132,22 @@ const assetSuffix = cfg.assetVersion ? `?v=${encodeURIComponent(cfg.assetVersion
) : null
}
</head>
<body>
<body class={bodyClass}>
<a class="skip-link" href="#main-content">Skip to content</a>
<header class="site-header">
<a
class="brand"
class="brand-logo-link"
href="/"
aria-label="Home"
data-umami-event="click"
data-umami-event-target_id="nav.logo"
data-umami-event-placement="nav"
data-umami-event-target_url="/"
>
<img class="brand-logo" src="/favicon.png" alt="" width="28" height="28" decoding="async" />
</a>
<a
class="brand title-hover-line"
href="/"
data-umami-event="click"
data-umami-event-target_id="nav.brand"
@@ -157,6 +169,7 @@ const assetSuffix = cfg.assetVersion ? `?v=${encodeURIComponent(cfg.assetVersion
<nav class="nav" id="primary-nav" data-open="false">
<a
class="title-hover-line"
href="/videos"
data-umami-event="click"
data-umami-event-target_id="nav.videos"
@@ -166,6 +179,7 @@ const assetSuffix = cfg.assetVersion ? `?v=${encodeURIComponent(cfg.assetVersion
Videos
</a>
<a
class="title-hover-line"
href="/podcast"
data-umami-event="click"
data-umami-event-target_id="nav.podcast"
@@ -175,6 +189,7 @@ const assetSuffix = cfg.assetVersion ? `?v=${encodeURIComponent(cfg.assetVersion
Podcast
</a>
<a
class="title-hover-line"
href="/blog"
data-umami-event="click"
data-umami-event-target_id="nav.blog"

View File

@@ -1,6 +1,7 @@
type PublicConfig = {
siteUrl?: string;
assetVersion?: string;
enableNavHoverLine: boolean;
umami?: {
scriptUrl: string;
websiteId: string;
@@ -25,12 +26,14 @@ type IngestConfig = {
export function getPublicConfig(): PublicConfig {
const siteUrl = import.meta.env.PUBLIC_SITE_URL;
const assetVersion = import.meta.env.PUBLIC_ASSET_VERSION;
const enableNavHoverLine = import.meta.env.PUBLIC_ENABLE_NAV_HOVER_LINE !== "false";
const scriptUrl = import.meta.env.PUBLIC_UMAMI_SCRIPT_URL;
const websiteId = import.meta.env.PUBLIC_UMAMI_WEBSITE_ID;
return {
siteUrl,
assetVersion,
enableNavHoverLine,
umami: scriptUrl && websiteId ? { scriptUrl, websiteId } : undefined,
};
}

View File

@@ -36,7 +36,7 @@ const blogJsonLd = {
<script type="application/ld+json" set:html={JSON.stringify(blogJsonLd)} />
<section class="section">
<div class="section-header">
<h2>Latest posts</h2>
<h2 class="title-hover-line">Latest posts</h2>
<span class="muted">{posts.length} post{posts.length === 1 ? "" : "s"}</span>
</div>

View File

@@ -136,7 +136,7 @@ const homeJsonLd = {
<section class="section">
<div class="section-header">
<h2>High-performing videos</h2>
<h2 class="title-hover-line">High-performing videos</h2>
<a
class="muted"
href="/videos"
@@ -215,7 +215,7 @@ const homeJsonLd = {
<section class="section">
<div class="section-header">
<h2>Podcast: Irregular Mind</h2>
<h2 class="title-hover-line">Podcast: Irregular Mind</h2>
<a
class="muted"
href="/podcast"

View File

@@ -44,7 +44,7 @@ const podcastJsonLd = {
<script type="application/ld+json" set:html={JSON.stringify(podcastJsonLd)} />
<section class="section">
<div class="section-header">
<h2>Irregular Mind</h2>
<h2 class="title-hover-line">Irregular Mind</h2>
<span class="muted">{episodes.length} episodes</span>
</div>
{

View File

@@ -44,7 +44,7 @@ const videosJsonLd = {
<script type="application/ld+json" set:html={JSON.stringify(videosJsonLd)} />
<section class="section">
<div class="section-header">
<h2>Videos</h2>
<h2 class="title-hover-line">Videos</h2>
<span class="muted">{videos.length} items</span>
</div>
{

View File

@@ -210,18 +210,37 @@ textarea:focus-visible {
background: var(--surface-0);
border-bottom: 1px solid var(--stroke-weak);
padding: 14px 24px;
display: flex;
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.brand-logo-link {
display: inline-flex;
align-items: center;
justify-self: start;
border-radius: 10px;
}
.brand-logo {
width: 28px;
height: 28px;
display: block;
object-fit: contain;
}
.brand {
grid-column: 2;
justify-self: center;
font-weight: 800;
letter-spacing: -0.02em;
font-size: 18px;
}
.nav {
grid-column: 3;
justify-self: end;
display: flex;
gap: 16px;
font-weight: 600;
@@ -229,6 +248,8 @@ textarea:focus-visible {
}
.nav a {
display: inline-flex;
align-items: center;
padding: 10px 12px;
border-radius: 999px;
}
@@ -237,6 +258,54 @@ textarea:focus-visible {
color: var(--fg);
}
.title-hover-line {
position: relative;
--hover-line-left: 0;
--hover-line-right: 0;
--hover-line-bottom: -2px;
}
.title-hover-line::after {
content: "";
position: absolute;
left: var(--hover-line-left);
right: var(--hover-line-right);
bottom: var(--hover-line-bottom);
height: 2px;
border-radius: 999px;
background: linear-gradient(
90deg,
color-mix(in srgb, var(--accent2) 72%, transparent),
color-mix(in srgb, var(--accent) 65%, transparent)
);
opacity: 0;
transform: scaleX(0.35);
transform-origin: center;
transition:
transform 240ms cubic-bezier(0.22, 1, 0.36, 1),
opacity 180ms ease,
filter 200ms ease;
filter: drop-shadow(0 0 7px color-mix(in srgb, var(--accent2) 40%, transparent));
pointer-events: none;
}
.nav a.title-hover-line {
--hover-line-left: 12px;
--hover-line-right: 12px;
--hover-line-bottom: 7px;
}
.nav-hover-line-enabled .title-hover-line:hover::after {
opacity: 1;
transform: scaleX(1);
}
.nav-hover-line-enabled .title-hover-line:focus-visible::after {
opacity: 0.9;
transform: scaleX(1);
transition: none;
}
.nav-toggle {
display: none;
align-items: center;
@@ -279,9 +348,23 @@ textarea:focus-visible {
@media (max-width: 760px) {
.site-header {
position: sticky;
grid-template-columns: auto 1fr auto;
padding: 12px 14px;
gap: 10px;
}
.brand {
font-size: 17px;
}
.brand-logo {
width: 24px;
height: 24px;
}
.nav-toggle {
grid-column: 3;
justify-self: end;
display: inline-flex;
}
@@ -557,6 +640,11 @@ html[data-theme-transition="on"] .theme-notch-option {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
}
.title-hover-line::after {
transition: none !important;
filter: none;
}
}
.subnav {