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

@@ -4,6 +4,8 @@ WORKDIR /app/site
ARG PUBLIC_ENABLE_SW=true ARG PUBLIC_ENABLE_SW=true
ENV PUBLIC_ENABLE_SW=$PUBLIC_ENABLE_SW ENV PUBLIC_ENABLE_SW=$PUBLIC_ENABLE_SW
ARG PUBLIC_ENABLE_NAV_HOVER_LINE=true
ENV PUBLIC_ENABLE_NAV_HOVER_LINE=$PUBLIC_ENABLE_NAV_HOVER_LINE
ARG PUBLIC_ASSET_VERSION ARG PUBLIC_ASSET_VERSION
ENV PUBLIC_ASSET_VERSION=$PUBLIC_ASSET_VERSION ENV PUBLIC_ASSET_VERSION=$PUBLIC_ASSET_VERSION

View File

@@ -7,6 +7,7 @@ services:
args: args:
# Build-time toggle for service worker registration in the generated static HTML. # Build-time toggle for service worker registration in the generated static HTML.
PUBLIC_ENABLE_SW: ${PUBLIC_ENABLE_SW:-true} PUBLIC_ENABLE_SW: ${PUBLIC_ENABLE_SW:-true}
PUBLIC_ENABLE_NAV_HOVER_LINE: ${PUBLIC_ENABLE_NAV_HOVER_LINE:-true}
PUBLIC_ASSET_VERSION: ${PUBLIC_ASSET_VERSION:-} PUBLIC_ASSET_VERSION: ${PUBLIC_ASSET_VERSION:-}
# Public, build-time config baked into the HTML. # Public, build-time config baked into the HTML.
PUBLIC_SITE_URL: ${PUBLIC_SITE_URL:-} PUBLIC_SITE_URL: ${PUBLIC_SITE_URL:-}

BIN
header-desktop-1366x768.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

BIN
header-hover-videos.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

BIN
header-mobile-390x844.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-02-11

View File

@@ -0,0 +1,82 @@
## Context
The primary header navigation is currently laid out as a simple left brand + right nav flex row. This makes the header feel left-heavy and de-emphasizes the brand. The site already uses a cohesive visual system (tokenized colors, glow accents, motion preferences) and has existing accessibility constraints (focus-visible, reduced motion).
Relevant implementation notes:
- Header markup is in `site/src/layouts/BaseLayout.astro`.
- Styling is in `site/src/styles/global.css`.
- Public feature flags are expressed via `PUBLIC_*` env vars and commonly use an opt-out string comparison (`!== "false"`) similar to `PUBLIC_ENABLE_SW`.
This change is purely UI-level: layout and hover presentation. No routing, data, or backend behavior changes are required.
## Goals / Non-Goals
**Goals:**
- Add a left-side logo in the header using the same asset as the favicon, sized appropriately and aligned with the header rhythm.
- Center the site brand label ("SanthoshJ") in the primary header layout without breaking the mobile hamburger behavior.
- Introduce a novel animated hover-line treatment that appears only on hover:
- primary header title links (brand + Videos, Podcast, Blog)
- section/module titles for Videos, Podcast, Blog (titles only)
- Gate the hover-line treatment behind a public env flag so it can be turned off to preserve the current behavior.
- Preserve accessibility:
- no layout shift due to hover effects
- keyboard focus remains visible and not confused with hover-only affordances
- reduced motion mode substantially reduces/disables the animation
**Non-Goals:**
- Redesigning the overall header/nav visual language beyond layout + hover line.
- Changing navigation destinations, labels, or analytics tracking attributes.
- Introducing new font families or major typography changes.
- Adding new build dependencies.
## Decisions
### 1) Header layout strategy: CSS Grid for centering
Decision: implement the header container as a 3-column grid (`left / center / right`) and place:
- left: logo link
- center: brand text link
- right: nav links (desktop) and hamburger toggle (mobile)
Rationale: grid makes true center alignment robust even when left/right columns have different widths, and avoids brittle absolute positioning.
Alternative: flex with spacer elements. Rejected due to center drift as content changes.
### 2) Logo asset and sizing
Decision: reuse `site/public/favicon.png` as the logo source initially, styled to a fixed square size with pixel-snapped rendering.
Rationale: avoids introducing new assets. If needed later, replace with a dedicated logo asset (same path/size contract).
### 3) Hover-line implementation: pseudo-element with no layout impact
Decision: implement the hover line using `::after` (or `background-size`) so it does not affect layout metrics.
- visible only on `:hover`
- reduced motion: animation disabled/reduced
- focus-visible: keep existing focus ring; optionally show a static line for focus (not animated)
Rationale: avoids CLS and keeps the effect decorative.
### 4) Feature flag: public opt-out env var
Decision: add `PUBLIC_ENABLE_NAV_HOVER_LINE` (string) and treat `"false"` as disabled; all other values (including undefined) keep current behavior.
Rationale: matches existing public env patterns and ensures default behavior remains unchanged.
### 5) Scope of "titles"
Decision: apply the hover-line to primary header title links (center brand + Videos/Podcast/Blog nav) and to explicit title elements for the three primary content surfaces (Videos, Podcast, Blog) where they are presented as headings; avoid applying to card titles and other headings.
Rationale: keeps the effect intentional and avoids visual noise.
## Risks / Trade-offs
- [Mobile header regressions] Grid changes could break the hamburger layout → Mitigation: keep mobile nav toggle and panel behavior unchanged; add targeted CSS at the existing breakpoint.
- [Accessibility confusion] Hover-only affordance might be mistaken for focus indicator → Mitigation: do not remove focus-visible styles; keep hover line hover-only and optionally static on focus.
- [Theme contrast] Line color may be low-contrast in some themes → Mitigation: use existing accent tokens and test across dark/light/high-contrast.
- [Over-application] Accidentally applying effect to card titles → Mitigation: apply only to selectors scoped to nav links + specific page/module title classes.

View File

@@ -0,0 +1,33 @@
## Why
The site header can feel visually left-weighted. A tighter, more intentional navbar layout and hover treatment will improve polish and brand presentation.
## What Changes
- Move the site branding text "SanthoshJ" to the center of the navbar (desktop and mobile-safe).
- Add a left-side logo using the existing favicon asset, sized appropriately for the header.
- Add a novel/creative animated hover line treatment that appears only on hover:
- primary header title links: brand + Videos, Podcast, Blog
- section/module titles for Videos, Podcast, Blog (titles only)
- Add a public env flag to enable/disable the hover line effect:
- default behavior remains unchanged unless explicitly enabled/disabled
- when disabled, revert to current hover behavior
- Ensure all the above changes are WCAG compliant.
## Capabilities
### New Capabilities
- `navbar-branding`: Add a left logo and centered brand layout in the primary header.
- `nav-hover-line`: Provide an animated hover-line treatment for primary header title links and key section titles, gated by a public env flag.
### Modified Capabilities
- (none)
## Impact
- `site/src/layouts/BaseLayout.astro` (header markup: logo placement, centered brand, nav link structure)
- `site/src/styles/global.css` (navbar layout + hover line animation styles)
- `site/public/favicon.*` (reuse as navbar logo asset)
- `site/src/env.d.ts` + `site/src/lib/config.ts` (new `PUBLIC_*` env flag wiring)

View File

@@ -0,0 +1,47 @@
## ADDED Requirements
### Requirement: Hover line appears only on hover for primary header titles
The site MUST render a decorative animated line that appears only on hover for the primary header title links:
- SanthoshJ (center brand link)
- Videos
- Podcast
- Blog
The hover line MUST NOT cause layout shift.
#### Scenario: Hover line is hidden by default
- **WHEN** the header renders
- **THEN** the hover line is not visible on primary header title links until hover
#### Scenario: Hover line appears on hover
- **WHEN** a pointer user hovers a primary header title link
- **THEN** an animated line appears as a hover affordance for that title
### Requirement: Hover line applies to section/module titles for key surfaces
The site MUST apply the same hover-line treatment to the titles for the Videos, Podcast, and Blog surfaces, but only on titles (not on card titles).
#### Scenario: Titles use hover line
- **WHEN** a pointer user hovers the Videos/Podcast/Blog surface title element
- **THEN** the hover line appears only for that title
#### Scenario: Card titles are unaffected
- **WHEN** a pointer user hovers a content card title
- **THEN** no hover line effect is applied (existing behavior remains)
### Requirement: Hover line is gated by a public env flag
The hover-line effect MUST be controllable via a public environment variable `PUBLIC_ENABLE_NAV_HOVER_LINE`.
If `PUBLIC_ENABLE_NAV_HOVER_LINE` is set to the string `"false"`, the hover-line effect MUST be disabled and the current behavior MUST continue.
#### Scenario: Flag disables hover line
- **WHEN** `PUBLIC_ENABLE_NAV_HOVER_LINE` is set to `"false"`
- **THEN** hovering primary header title links and surface titles does not render the hover line effect
### Requirement: Reduced motion disables or substantially reduces animation
If `prefers-reduced-motion: reduce` is set, the hover-line animation MUST be disabled or substantially reduced.
#### Scenario: Reduced motion disables noticeable hover animation
- **WHEN** `prefers-reduced-motion: reduce` is set and a user hovers a nav title
- **THEN** the hover line does not animate noticeably

View File

@@ -0,0 +1,25 @@
## ADDED Requirements
### Requirement: Primary header shows logo and centered brand
The site MUST render a primary header that includes:
- a left-side logo that links to `/`
- a centered brand label "SanthoshJ" that links to `/`
- the primary navigation links (Videos, Podcast, Blog)
The logo MUST use the same visual asset as the site favicon.
#### Scenario: Desktop header layout
- **WHEN** a user loads the home page on a desktop viewport
- **THEN** the header shows the logo on the left, the brand label centered, and the nav links on the right
#### Scenario: Mobile header layout
- **WHEN** a user loads the home page on a mobile viewport
- **THEN** the header still shows the logo on the left and the brand label centered without overlapping the nav toggle
### Requirement: Logo size is visually aligned
The header logo MUST be sized to match the header rhythm and MUST not cause layout shift.
#### Scenario: Logo has fixed dimensions
- **WHEN** the header renders
- **THEN** the logo element has explicit width and height and does not change size on load

View File

@@ -0,0 +1,20 @@
## 1. Header Branding Layout
- [x] 1.1 Update `site/src/layouts/BaseLayout.astro` header markup to add a left logo link (favicon asset) and move "SanthoshJ" brand link to the center
- [x] 1.2 Update `site/src/styles/global.css` header layout to center brand text robustly (grid-based layout) while preserving mobile hamburger behavior
- [x] 1.3 Ensure logo has correct sizing and explicit dimensions (no layout shift)
## 2. Hover Line Effect (Gated)
- [x] 2.1 Add `PUBLIC_ENABLE_NAV_HOVER_LINE` to `site/src/env.d.ts`
- [x] 2.2 Add config wiring in `site/src/lib/config.ts` (or direct `import.meta.env` usage) with opt-out semantics (`"false"` disables)
- [x] 2.3 Implement hover-line CSS for primary header title links (brand + Videos/Podcast/Blog), hover-only, no layout shift
- [x] 2.4 Implement hover-line CSS for the Videos/Podcast/Blog surface title elements only (not card titles)
- [x] 2.5 Add reduced-motion handling so the effect is disabled/substantially reduced under `prefers-reduced-motion: reduce`
- [x] 2.6 Ensure focus-visible styles remain clear and are not replaced by hover-only affordances
## 3. Verification
- [x] 3.1 Verify layout on desktop + mobile widths (no overlap with nav toggle)
- [x] 3.2 Verify hover-line is enabled by default and fully disabled when `PUBLIC_ENABLE_NAV_HOVER_LINE="false"`
- [x] 3.3 Run `npm -C site run build` and ensure no new typecheck regressions

View File

@@ -0,0 +1,51 @@
# nav-hover-line Specification
## Purpose
TBD - created by archiving change final-touches. Update Purpose after archive.
## Requirements
### Requirement: Hover line appears only on hover for primary header titles
The site MUST render a decorative animated line that appears only on hover for the primary header title links:
- SanthoshJ (center brand link)
- Videos
- Podcast
- Blog
The hover line MUST NOT cause layout shift.
#### Scenario: Hover line is hidden by default
- **WHEN** the header renders
- **THEN** the hover line is not visible on primary header title links until hover
#### Scenario: Hover line appears on hover
- **WHEN** a pointer user hovers a primary header title link
- **THEN** an animated line appears as a hover affordance for that title
### Requirement: Hover line applies to section/module titles for key surfaces
The site MUST apply the same hover-line treatment to the titles for the Videos, Podcast, and Blog surfaces, but only on titles (not on card titles).
#### Scenario: Titles use hover line
- **WHEN** a pointer user hovers the Videos/Podcast/Blog surface title element
- **THEN** the hover line appears only for that title
#### Scenario: Card titles are unaffected
- **WHEN** a pointer user hovers a content card title
- **THEN** no hover line effect is applied (existing behavior remains)
### Requirement: Hover line is gated by a public env flag
The hover-line effect MUST be controllable via a public environment variable `PUBLIC_ENABLE_NAV_HOVER_LINE`.
If `PUBLIC_ENABLE_NAV_HOVER_LINE` is set to the string `"false"`, the hover-line effect MUST be disabled and the current behavior MUST continue.
#### Scenario: Flag disables hover line
- **WHEN** `PUBLIC_ENABLE_NAV_HOVER_LINE` is set to `"false"`
- **THEN** hovering primary header title links and surface titles does not render the hover line effect
### Requirement: Reduced motion disables or substantially reduces animation
If `prefers-reduced-motion: reduce` is set, the hover-line animation MUST be disabled or substantially reduced.
#### Scenario: Reduced motion disables noticeable hover animation
- **WHEN** `prefers-reduced-motion: reduce` is set and a user hovers a nav title
- **THEN** the hover line does not animate noticeably

View File

@@ -0,0 +1,29 @@
# navbar-branding Specification
## Purpose
TBD - created by archiving change final-touches. Update Purpose after archive.
## Requirements
### Requirement: Primary header shows logo and centered brand
The site MUST render a primary header that includes:
- a left-side logo that links to `/`
- a centered brand label "SanthoshJ" that links to `/`
- the primary navigation links (Videos, Podcast, Blog)
The logo MUST use the same visual asset as the site favicon.
#### Scenario: Desktop header layout
- **WHEN** a user loads the home page on a desktop viewport
- **THEN** the header shows the logo on the left, the brand label centered, and the nav links on the right
#### Scenario: Mobile header layout
- **WHEN** a user loads the home page on a mobile viewport
- **THEN** the header still shows the logo on the left and the brand label centered without overlapping the nav toggle
### Requirement: Logo size is visually aligned
The header logo MUST be sized to match the header rhythm and MUST not cause layout shift.
#### Scenario: Logo has fixed dimensions
- **WHEN** the header renders
- **THEN** the logo element has explicit width and height and does not change size on load

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". # - In `npm run build`/`preview`, SW is registered only if this is "true".
PUBLIC_ENABLE_SW=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) # Content ingestion configuration (used by scripts)
YOUTUBE_CHANNEL_ID=UCxxxxxxxxxxxxxxxxxxxxxx YOUTUBE_CHANNEL_ID=UCxxxxxxxxxxxxxxxxxxxxxx
YOUTUBE_API_KEY= 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_UMAMI_WEBSITE_ID?: string;
readonly PUBLIC_ENABLE_SW?: string; readonly PUBLIC_ENABLE_SW?: string;
readonly PUBLIC_ASSET_VERSION?: string; readonly PUBLIC_ASSET_VERSION?: string;
readonly PUBLIC_ENABLE_NAV_HOVER_LINE?: string;
} }
interface ImportMeta { interface ImportMeta {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -210,18 +210,37 @@ textarea:focus-visible {
background: var(--surface-0); background: var(--surface-0);
border-bottom: 1px solid var(--stroke-weak); border-bottom: 1px solid var(--stroke-weak);
padding: 14px 24px; padding: 14px 24px;
display: flex; display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
align-items: center; 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 { .brand {
grid-column: 2;
justify-self: center;
font-weight: 800; font-weight: 800;
letter-spacing: -0.02em; letter-spacing: -0.02em;
font-size: 18px; font-size: 18px;
} }
.nav { .nav {
grid-column: 3;
justify-self: end;
display: flex; display: flex;
gap: 16px; gap: 16px;
font-weight: 600; font-weight: 600;
@@ -229,6 +248,8 @@ textarea:focus-visible {
} }
.nav a { .nav a {
display: inline-flex;
align-items: center;
padding: 10px 12px; padding: 10px 12px;
border-radius: 999px; border-radius: 999px;
} }
@@ -237,6 +258,54 @@ textarea:focus-visible {
color: var(--fg); 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 { .nav-toggle {
display: none; display: none;
align-items: center; align-items: center;
@@ -279,9 +348,23 @@ textarea:focus-visible {
@media (max-width: 760px) { @media (max-width: 760px) {
.site-header { .site-header {
position: sticky; 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 { .nav-toggle {
grid-column: 3;
justify-self: end;
display: inline-flex; display: inline-flex;
} }
@@ -557,6 +640,11 @@ html[data-theme-transition="on"] .theme-notch-option {
animation-duration: 0.001ms !important; animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important; animation-iteration-count: 1 !important;
} }
.title-hover-line::after {
transition: none !important;
filter: none;
}
} }
.subnav { .subnav {