update overal styling

This commit is contained in:
ClovertaTheTrilobita 2026-09-17 12:42:39 +08:00
parent 6e4cd96114
commit 566c5fa24a
4 changed files with 156 additions and 82 deletions

View file

@ -10,6 +10,7 @@ const t = getTranslations(lang);
<div class="header-slot"> <div class="header-slot">
<header class="site-header"> <header class="site-header">
<div class="header-content">
<div class="theme-icon-wrap"> <div class="theme-icon-wrap">
<ThemeIcon /> <ThemeIcon />
</div> </div>
@ -27,6 +28,7 @@ const t = getTranslations(lang);
</div> </div>
<Navigation /> <Navigation />
</nav> </nav>
</div>
<div class="divider"> <div class="divider">
<Divider /> <Divider />
</div> </div>
@ -40,6 +42,10 @@ const t = getTranslations(lang);
padding-top: 0.5rem; padding-top: 0.5rem;
} }
.header-content {
position: relative;
}
.banner-link { .banner-link {
display: block; display: block;
} }
@ -103,21 +109,23 @@ const t = getTranslations(lang);
.site-header { .site-header {
pointer-events: auto; pointer-events: auto;
width: var(--header-width, 100%); width: 100%;
max-width: none; max-width: 90ch;
margin-left: var(--header-offset, 0); margin-inline: auto;
margin-right: var(--header-offset, 0); padding: 0.5rem 0 0;
padding: 0.5rem var(--header-inline-padding, 0) 0;
background-color: color-mix( background-color: color-mix(
in srgb, in srgb,
var(--background-color) var(--header-surface-weight, 0%), var(--background-color) var(--header-surface-weight, 0%),
transparent transparent
); );
backdrop-filter: blur(var(--header-blur, 0));
transform: translateY(0); transform: translateY(0);
transition: transform 0.3s ease; transition: transform 0.3s ease;
} }
.header-content {
width: 100%;
}
.header-slot.is-scroll-hidden .site-header, .header-slot.is-scroll-hidden .site-header,
.header-slot.is-footer-hidden .site-header { .header-slot.is-footer-hidden .site-header {
transform: translateY(calc(-100% - 0.5rem)); transform: translateY(calc(-100% - 0.5rem));
@ -133,7 +141,7 @@ const t = getTranslations(lang);
.theme-icon-wrap { .theme-icon-wrap {
top: var(--theme-top, 0); top: var(--theme-top, 0);
right: var(--theme-right, 0); right: 0;
} }
.header-nav { .header-nav {
@ -168,13 +176,11 @@ const t = getTranslations(lang);
} }
.site-header :global(.site-nav a) { .site-header :global(.site-nav a) {
font-size: var(--site-nav-font-size, 1.2rem); font-size: var(--site-nav-font-size, 1.05rem);
} }
.site-header > .divider { .site-header > .divider {
width: var(--divider-width, 100%); width: 100%;
margin-left: var(--divider-offset, 0);
margin-right: var(--divider-offset, 0);
margin-top: var(--divider-margin-top, 0.2rem); margin-top: var(--divider-margin-top, 0.2rem);
margin-bottom: var(--divider-margin-bottom, 2rem); margin-bottom: var(--divider-margin-bottom, 2rem);
transform: translateY(var(--divider-y, 0)); transform: translateY(var(--divider-y, 0));
@ -209,13 +215,8 @@ const t = getTranslations(lang);
const HEADER_SCROLL_DISTANCE = 120; const HEADER_SCROLL_DISTANCE = 120;
const DIRECTION_TRIGGER_DISTANCE = 6; const DIRECTION_TRIGGER_DISTANCE = 6;
const headerStyleProperties = [ const headerStyleProperties = [
"--header-width",
"--header-offset",
"--header-inline-padding",
"--header-surface-weight", "--header-surface-weight",
"--header-blur",
"--theme-top", "--theme-top",
"--theme-right",
"--header-gap", "--header-gap",
"--header-nav-height", "--header-nav-height",
"--header-nav-y", "--header-nav-y",
@ -231,8 +232,6 @@ const t = getTranslations(lang);
"--divider-margin-top", "--divider-margin-top",
"--divider-margin-bottom", "--divider-margin-bottom",
"--divider-inner-margin-bottom", "--divider-inner-margin-bottom",
"--divider-width",
"--divider-offset",
"--divider-y", "--divider-y",
]; ];
@ -248,6 +247,7 @@ const t = getTranslations(lang);
let headerFrame = 0; let headerFrame = 0;
let headerMetrics: HeaderMetrics | null = null; let headerMetrics: HeaderMetrics | null = null;
let footerDividerObserver: IntersectionObserver | null = null; let footerDividerObserver: IntersectionObserver | null = null;
let isFooterDividerVisible = false;
let lastScrollY = window.scrollY; let lastScrollY = window.scrollY;
let scrollDirection = 0; let scrollDirection = 0;
let directionDistance = 0; let directionDistance = 0;
@ -297,6 +297,7 @@ const t = getTranslations(lang);
if (!header) return; if (!header) return;
updateScrollVisibility(header.parentElement, window.scrollY); updateScrollVisibility(header.parentElement, window.scrollY);
updateFooterVisibility(header.parentElement, window.scrollY);
if (!desktopHeaderQuery.matches) { if (!desktopHeaderQuery.matches) {
clearHeaderStyles(header); clearHeaderStyles(header);
@ -328,12 +329,8 @@ const t = getTranslations(lang);
Math.max(9 * rootFontSize, viewportWidth * 0.22), Math.max(9 * rootFontSize, viewportWidth * 0.22),
12 * rootFontSize, 12 * rootFontSize,
); );
const headerWidth = lerp(expandedWidth, viewportWidth, progress);
const headerOffset = -(headerWidth - expandedWidth) / 2;
const inlinePadding = lerp(0, rootFontSize, progress);
const compactSiteNavWidth = Math.max( const compactSiteNavWidth = Math.max(
viewportWidth - expandedWidth -
2 * rootFontSize -
compactLogoWidth - compactLogoWidth -
7.5 * rootFontSize, 7.5 * rootFontSize,
0, 0,
@ -344,13 +341,8 @@ const t = getTranslations(lang);
); );
const values: Record<string, string> = { const values: Record<string, string> = {
"--header-width": `${headerWidth}px`, "--header-surface-weight": `${lerp(0, 98, progress)}%`,
"--header-offset": `${headerOffset}px`,
"--header-inline-padding": `${inlinePadding}px`,
"--header-surface-weight": `${lerp(0, 94, progress)}%`,
"--header-blur": `${lerp(0, 10, progress)}px`,
"--theme-top": `${lerp(0, 0.85 * rootFontSize, progress)}px`, "--theme-top": `${lerp(0, 0.85 * rootFontSize, progress)}px`,
"--theme-right": `${inlinePadding}px`,
"--header-gap": `${lerp(rootFontSize, 0, progress)}px`, "--header-gap": `${lerp(rootFontSize, 0, progress)}px`,
"--header-nav-height": `${lerp(expandedNavHeight, 3 * rootFontSize, progress)}px`, "--header-nav-height": `${lerp(expandedNavHeight, 3 * rootFontSize, progress)}px`,
"--header-nav-y": `${lerp(0, 0.5 * rootFontSize, progress)}px`, "--header-nav-y": `${lerp(0, 0.5 * rootFontSize, progress)}px`,
@ -362,12 +354,10 @@ const t = getTranslations(lang);
"--site-nav-x": `${lerp(0, compactLogoWidth + 1.5 * rootFontSize, progress)}px`, "--site-nav-x": `${lerp(0, compactLogoWidth + 1.5 * rootFontSize, progress)}px`,
"--site-nav-y": `${lerp(0, -36, progress)}px`, "--site-nav-y": `${lerp(0, -36, progress)}px`,
"--site-nav-gap": `${lerp(1.5 * rootFontSize, compactNavGap, progress)}px`, "--site-nav-gap": `${lerp(1.5 * rootFontSize, compactNavGap, progress)}px`,
"--site-nav-font-size": `${lerp(1.2 * rootFontSize, 1.05 * rootFontSize, progress)}px`, "--site-nav-font-size": `${lerp(1.05 * rootFontSize, 0.95 * rootFontSize, progress)}px`,
"--divider-margin-top": `${lerp(0.2 * rootFontSize, 0.45 * rootFontSize, progress)}px`, "--divider-margin-top": `${lerp(0.2 * rootFontSize, 0.45 * rootFontSize, progress)}px`,
"--divider-margin-bottom": `${lerp(2 * rootFontSize, 0, progress)}px`, "--divider-margin-bottom": `${lerp(2 * rootFontSize, 0, progress)}px`,
"--divider-inner-margin-bottom": `${lerp(rootFontSize, 0, progress)}px`, "--divider-inner-margin-bottom": `${lerp(rootFontSize, 0, progress)}px`,
"--divider-width": `${headerWidth}px`,
"--divider-offset": `${-inlinePadding}px`,
"--divider-y": `${lerp(0, -0.1 * rootFontSize, progress)}px`, "--divider-y": `${lerp(0, -0.1 * rootFontSize, progress)}px`,
}; };
@ -410,6 +400,23 @@ const t = getTranslations(lang);
directionDistance = 0; directionDistance = 0;
} }
function updateFooterVisibility(
slot: HTMLElement | null,
currentScrollY: number,
) {
if (!slot) return;
const headerIsFullyExpanded =
currentScrollY >= HEADER_SCROLL_DISTANCE;
slot.classList.toggle(
"is-footer-hidden",
desktopHeaderQuery.matches &&
headerIsFullyExpanded &&
isFooterDividerVisible,
);
}
function requestHeaderUpdate() { function requestHeaderUpdate() {
if (headerFrame) return; if (headerFrame) return;
@ -422,6 +429,7 @@ const t = getTranslations(lang);
function bindFooterDividerObserver() { function bindFooterDividerObserver() {
footerDividerObserver?.disconnect(); footerDividerObserver?.disconnect();
footerDividerObserver = null; footerDividerObserver = null;
isFooterDividerVisible = false;
const slot = document.querySelector<HTMLElement>(".header-slot"); const slot = document.querySelector<HTMLElement>(".header-slot");
const footerDivider = document.querySelector<HTMLElement>( const footerDivider = document.querySelector<HTMLElement>(
@ -435,7 +443,8 @@ const t = getTranslations(lang);
if (!desktopHeaderQuery.matches || !footerDivider) return; if (!desktopHeaderQuery.matches || !footerDivider) return;
footerDividerObserver = new IntersectionObserver(([entry]) => { footerDividerObserver = new IntersectionObserver(([entry]) => {
slot.classList.toggle("is-footer-hidden", entry.isIntersecting); isFooterDividerVisible = entry.isIntersecting;
updateFooterVisibility(slot, window.scrollY);
}); });
footerDividerObserver.observe(footerDivider); footerDividerObserver.observe(footerDivider);
} }
@ -454,6 +463,7 @@ const t = getTranslations(lang);
lastScrollY = window.scrollY; lastScrollY = window.scrollY;
scrollDirection = 0; scrollDirection = 0;
directionDistance = 0; directionDistance = 0;
isFooterDividerVisible = false;
document document
.querySelector<HTMLElement>(".header-slot") .querySelector<HTMLElement>(".header-slot")
?.classList.remove("is-scroll-hidden"); ?.classList.remove("is-scroll-hidden");

View file

@ -3,25 +3,48 @@ import { getLangFromUrl, getTranslations } from "@/i18n";
const lang = getLangFromUrl(Astro.url); const lang = getLangFromUrl(Astro.url);
const t = getTranslations(lang); const t = getTranslations(lang);
const pathname = Astro.url.pathname;
const items = [
{ href: `/${lang}/`, label: t.nav.home, exact: true },
{ href: `/${lang}/about/`, label: t.nav.about },
{ href: `/${lang}/tags/`, label: t.nav.tags },
{ href: `/${lang}/timeline/`, label: t.nav.timeline },
{ href: `/${lang}/friends/`, label: t.nav.friends },
];
const isCurrent = (href: string, exact = false) =>
exact
? pathname === href || pathname === href.slice(0, -1)
: pathname.startsWith(href);
--- ---
<nav class="site-nav" aria-label="Site navigation"> <nav class="site-nav" aria-label="Site navigation">
<div class="site-nav-desktop"> <div class="site-nav-desktop">
<a href={`/${lang}/`}>{t.nav.home}</a> {
<a href={`/${lang}/about/`}>{t.nav.about}</a> items.map(({ href, label, exact }) => (
<a href={`/${lang}/tags/`}>{t.nav.tags}</a> <a
<a href={`/${lang}/timeline/`}>{t.nav.timeline}</a> href={href}
<a href={`/${lang}/friends/`}>{t.nav.friends}</a> aria-current={isCurrent(href, exact) ? "page" : undefined}
>
{label}
</a>
))
}
</div> </div>
<details class="site-nav-mobile"> <details class="site-nav-mobile">
<summary>☰ {lang === "zh" ? "导航栏" : "Menu"}</summary> <summary>☰ {lang === "zh" ? "导航栏" : "Menu"}</summary>
<div class="dropdown-menu"> <div class="dropdown-menu">
<a href={`/${lang}/`}>{t.nav.home}</a> {
<a href={`/${lang}/about/`}>{t.nav.about}</a> items.map(({ href, label, exact }) => (
<a href={`/${lang}/tags/`}>{t.nav.tags}</a> <a
<a href={`/${lang}/timeline/`}>{t.nav.timeline}</a> href={href}
<a href={`/${lang}/friends/`}>{t.nav.friends}</a> aria-current={isCurrent(href, exact) ? "page" : undefined}
>
{label}
</a>
))
}
</div> </div>
</details> </details>
</nav> </nav>
@ -54,22 +77,45 @@ const t = getTranslations(lang);
} }
.site-nav a { .site-nav a {
color: black; position: relative;
text-decoration: underline; color: var(--text-color);
font-weight: 700; text-decoration: none;
font-size: 1.2rem; font-weight: 500;
font-size: 1.05rem;
line-height: 1.4;
} }
.site-nav a:hover { .site-nav-desktop a::after {
text-decoration: underline; content: "";
position: absolute;
left: 50%;
bottom: -0.28rem;
width: 100%;
height: 2px;
background: var(--deep-red);
transform: translateX(-50%) scaleX(0);
transform-origin: center;
transition: transform 0.18s ease;
}
.site-nav-desktop a:hover::after,
.site-nav-desktop a:focus-visible::after,
.site-nav-desktop a[aria-current="page"]::after {
transform: translateX(-50%) scaleX(1);
}
.site-nav a:focus-visible {
outline: 2px solid var(--url-color);
outline-offset: 0.35rem;
text-decoration: none;
} }
:global(.dark) .site-nav a { :global(.dark) .site-nav a {
color: white; color: var(--text-color-dark);
} }
:global(.dark) .site-nav a:hover { :global(.dark) .site-nav a:hover {
color: #ddd; color: #fff;
} }
.site-nav-mobile { .site-nav-mobile {
@ -81,10 +127,10 @@ const t = getTranslations(lang);
list-style: none; list-style: none;
cursor: pointer; cursor: pointer;
display: inline-block; display: inline-block;
font-weight: 700; font-weight: 500;
font-size: 1.1rem; font-size: 1rem;
color: black; color: var(--text-color);
text-decoration: underline; text-decoration: none;
user-select: none; user-select: none;
} }
@ -121,6 +167,11 @@ const t = getTranslations(lang);
text-decoration: underline; text-decoration: underline;
} }
.dropdown-menu a[aria-current="page"] {
color: var(--deep-red);
font-weight: 600;
}
:global(.dark) .dropdown-menu { :global(.dark) .dropdown-menu {
background: #1f1b18; background: #1f1b18;
border-color: #c8a98a; border-color: #c8a98a;

View file

@ -8,6 +8,7 @@ import "katex/dist/katex.min.css";
const lang = getLangFromUrl(Astro.url); const lang = getLangFromUrl(Astro.url);
const htmlLang = lang === "zh" ? "zh-CN" : "en-US"; const htmlLang = lang === "zh" ? "zh-CN" : "en-US";
const isHomePage = new RegExp(`^/${lang}/?$`).test(Astro.url.pathname);
import SEO from "@/components/SEO.astro"; import SEO from "@/components/SEO.astro";
import FloatingActions from "@/components/FloatingActions.astro"; import FloatingActions from "@/components/FloatingActions.astro";
import Spinner from "@/components/Spinner.astro"; import Spinner from "@/components/Spinner.astro";
@ -115,7 +116,7 @@ const {
document.addEventListener("astro:page-load", addCodeCopyButtons); document.addEventListener("astro:page-load", addCodeCopyButtons);
</script> </script>
<body> <body class:list={{ "is-home": isHomePage }}>
<Header /> <Header />
<Spinner /> <Spinner />
<main <main

View file

@ -50,18 +50,32 @@ body::after {
position: fixed; position: fixed;
right: 0; right: 0;
bottom: 0; bottom: 0;
width: 400px; width: clamp(300px, 31vw, 400px);
/* 按需调整大小 */ height: clamp(300px, 31vw, 400px);
height: 400px;
/* 按需调整大小 */
background-image: url("https://files.seeusercontent.com/2026/04/16/1sXb/touhou___kirisame_marisa__2__by_.webp"); background-image: url("https://files.seeusercontent.com/2026/04/16/1sXb/touhou___kirisame_marisa__2__by_.webp");
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: right bottom; background-position: right bottom;
background-size: contain; background-size: contain;
opacity: 0.35; opacity: 0;
-webkit-mask-image: radial-gradient(
ellipse at right bottom,
#000 0%,
#000 38%,
transparent 78%
);
mask-image: radial-gradient(
ellipse at right bottom,
#000 0%,
#000 38%,
transparent 78%
);
pointer-events: none; pointer-events: none;
z-index: -1; z-index: -1;
transition: opacity 0.25s ease;
}
body.is-home::after {
opacity: 0.16;
} }
html.dark body::after { html.dark body::after {
@ -69,7 +83,7 @@ html.dark body::after {
} }
@media (max-width: 900px) { @media (max-width: 900px) {
body::after { body.is-home::after {
opacity: 0; opacity: 0;
} }
} }
@ -98,7 +112,7 @@ h1 {
a { a {
color: var(--url-color); color: var(--url-color);
font-weight: 700; font-weight: inherit;
text-decoration: none; text-decoration: none;
} }
@ -119,7 +133,6 @@ html.dark {
.dark .nav-links a { .dark .nav-links a {
color: #9ecbff; color: #9ecbff;
font-weight: 700;
} }
.dark .nav-links a:hover, .dark .nav-links a:hover,
@ -129,7 +142,6 @@ html.dark {
.dark a { .dark a {
color: #9ecbff; color: #9ecbff;
font-weight: 700;
} }
.dark a:hover, .dark a:hover,