Skip to content

Commit 0075b10

Browse files
committed
Refine UI styles and navigation
1 parent b2dd3cc commit 0075b10

File tree

11 files changed

+331
-141
lines changed

11 files changed

+331
-141
lines changed

src/components/astro/ContactSection.astro

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
---
2+
import { Image } from 'astro:assets';
23
import { getI18n, type Lang } from '../../i18n';
34
45
interface Props {
@@ -40,16 +41,109 @@ const descText = t('contact.desc').replace('{brand}', '<strong>RushDB</strong>')
4041
<div class="contact-qrcodes">
4142
<div class="contact-wechat">
4243
<div class="contact-wechat-title">{t('contact.wechatTitle')}</div>
43-
<img src="/Wechat.png" alt="RushDB 官方公众号二维码" class="contact-wechat-img" loading="lazy" decoding="async" />
44+
<button
45+
type="button"
46+
class="contact-qrcode-trigger"
47+
data-full-src="/Wechat.png"
48+
data-alt="RushDB 官方公众号二维码"
49+
aria-label="查看 RushDB 官方公众号二维码大图"
50+
>
51+
<Image
52+
src="/Wechat.png"
53+
alt="RushDB 官方公众号二维码"
54+
class="contact-wechat-img"
55+
loading="lazy"
56+
decoding="async"
57+
width={430}
58+
height={430}
59+
/>
60+
</button>
4461
<div class="contact-wechat-hint">{t('contact.qrHint')}</div>
4562
</div>
4663
<div class="contact-redbook">
4764
<div class="contact-redbook-title">{t('contact.redbookTitle')}</div>
48-
<img src="/Redbook.jpg" alt="RushDB 官方小红书二维码" class="contact-redbook-img" loading="lazy" decoding="async" />
65+
<button
66+
type="button"
67+
class="contact-qrcode-trigger"
68+
data-full-src="/Redbook.jpg"
69+
data-alt="RushDB 官方小红书二维码"
70+
aria-label="查看 RushDB 官方小红书二维码大图"
71+
>
72+
<Image
73+
src="/Redbook.jpg"
74+
alt="RushDB 官方小红书二维码"
75+
class="contact-redbook-img"
76+
loading="lazy"
77+
decoding="async"
78+
width={1002}
79+
height={1688}
80+
/>
81+
</button>
4982
<div class="contact-redbook-hint">{t('contact.qrHint')}</div>
5083
</div>
5184
</div>
5285
</div>
5386
</div>
5487
</div>
88+
89+
<div class="lightbox" id="qrcode-lightbox" aria-hidden="true" role="dialog" aria-modal="true">
90+
<div class="lightbox-backdrop" data-lightbox-close></div>
91+
<div class="lightbox-content" role="document">
92+
<button type="button" class="lightbox-close" data-lightbox-close aria-label="关闭预览">×</button>
93+
<img class="lightbox-image" alt="" />
94+
</div>
95+
</div>
5596
</section>
97+
98+
<script is:inline>
99+
(() => {
100+
const lightbox = document.getElementById('qrcode-lightbox');
101+
if (!lightbox) return;
102+
103+
const image = lightbox.querySelector('.lightbox-image');
104+
const triggers = document.querySelectorAll('.contact-qrcode-trigger');
105+
const closeTargets = lightbox.querySelectorAll('[data-lightbox-close]');
106+
let lastActive = null;
107+
108+
if (lightbox.parentElement !== document.body) {
109+
document.body.appendChild(lightbox);
110+
}
111+
112+
const openLightbox = (src, alt) => {
113+
if (!image) return;
114+
image.src = src;
115+
image.alt = alt || '';
116+
image.decoding = 'async';
117+
lastActive = document.activeElement;
118+
lightbox.classList.add('is-open');
119+
lightbox.setAttribute('aria-hidden', 'false');
120+
document.body.style.overflow = 'hidden';
121+
};
122+
123+
const closeLightbox = () => {
124+
lightbox.classList.remove('is-open');
125+
lightbox.setAttribute('aria-hidden', 'true');
126+
document.body.style.overflow = '';
127+
if (image) image.src = '';
128+
if (lastActive && typeof lastActive.focus === 'function') {
129+
lastActive.focus();
130+
}
131+
};
132+
133+
triggers.forEach((trigger) => {
134+
trigger.addEventListener('click', () => {
135+
const src = trigger.getAttribute('data-full-src');
136+
const alt = trigger.getAttribute('data-alt') || trigger.getAttribute('aria-label') || '';
137+
if (src) openLightbox(src, alt);
138+
});
139+
});
140+
141+
closeTargets.forEach((el) => el.addEventListener('click', closeLightbox));
142+
143+
document.addEventListener('keydown', (event) => {
144+
if (event.key === 'Escape' && lightbox.classList.contains('is-open')) {
145+
closeLightbox();
146+
}
147+
});
148+
})();
149+
</script>

src/components/astro/HeroSection.astro

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
---
2+
import { Image } from 'astro:assets';
23
import { getI18n, type Lang } from '../../i18n';
34
45
interface Props {
@@ -15,7 +16,7 @@ const { t } = getI18n(lang);
1516
<div class="hero-content">
1617
<div class="logo-container" aria-hidden="true">
1718
<div class="logo">
18-
<img src="/RushDB.png" alt="RushDB Logo" />
19+
<Image src="/RushDB.png" alt="RushDB Logo" width={697} height={697} priority />
1920
</div>
2021
</div>
2122
<h1 class="hero-title">

src/components/astro/MembersSection.astro

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
---
2+
import { Image } from 'astro:assets';
23
import { getI18n, type Lang } from '../../i18n';
34
45
interface Props {
@@ -15,7 +16,7 @@ const { t } = getI18n(lang);
1516
<a href="https://mmmttt000045.github.io" class="card-link" target="_blank" rel="noopener noreferrer">
1617
<div class="member-card">
1718
<div class="member-avatar">
18-
<img src="/MingTai.png" alt="明泰" class="avatar-img" loading="lazy" decoding="async" />
19+
<Image src="/MingTai.png" alt="明泰" class="avatar-img" loading="lazy" decoding="async" width={331} height={490} />
1920
</div>
2021
<div class="member-name">{t('members.mingTai.name')}</div>
2122
<div class="member-info">
@@ -42,7 +43,7 @@ const { t } = getI18n(lang);
4243
<a href="https://huxin0817.github.io/" class="card-link" target="_blank" rel="noopener noreferrer">
4344
<div class="member-card">
4445
<div class="member-avatar">
45-
<img src="/HuXin.png" alt="胡鑫" class="avatar-img" loading="lazy" decoding="async" />
46+
<Image src="/HuXin.png" alt="胡鑫" class="avatar-img" loading="lazy" decoding="async" width={354} height={523} />
4647
</div>
4748
<div class="member-name">{t('members.huXin.name')}</div>
4849
<div class="member-info">
@@ -69,7 +70,7 @@ const { t } = getI18n(lang);
6970
<a href="https://koschei.top/" class="card-link" target="_blank" rel="noopener noreferrer">
7071
<div class="member-card">
7172
<div class="member-avatar">
72-
<img src="/WuYiMin.png" alt="吴奕民" class="avatar-img" loading="lazy" decoding="async" />
73+
<Image src="/WuYiMin.png" alt="吴奕民" class="avatar-img" loading="lazy" decoding="async" width={347} height={485} />
7374
</div>
7475
<div class="member-name">{t('members.wuYiMin.name')}</div>
7576
<div class="member-info">

src/components/astro/ProjectsSection.astro

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
---
2+
import { Image } from 'astro:assets';
23
import { getI18n, type Lang } from '../../i18n';
34
45
interface Props {
@@ -21,7 +22,7 @@ const { t } = getI18n(lang);
2122
<div class="project-item featured-project">
2223
<div class="project-header">
2324
<div class="project-icon">
24-
<img src="/miniob.png" alt="MiniOB" />
25+
<Image src="/miniob.png" alt="MiniOB" loading="lazy" decoding="async" width={48} height={48} />
2526
</div>
2627
<div class="project-meta">
2728
<div class="project-title">{t('projects.miniob.title')}</div>

src/components/blog/PostCard.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const heroImage = post.data.heroImage;
3535
<a href={`/${lang}/blog/${slug}/`} class="post-card-link">
3636
{heroImage && (
3737
<div class="post-card-image">
38-
<img src={heroImage} alt={post.data.title} loading="lazy" />
38+
<img src={heroImage} alt={post.data.title} loading="lazy" data-image-component="true" />
3939
<div class="post-card-image-overlay"></div>
4040
</div>
4141
)}

src/components/vue/NavBar.vue

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,32 @@ const languages: Record<Lang, string> = {
3434
ja: '🇯🇵 日本語',
3535
};
3636
37+
function normalizePath(path: string) {
38+
if (!path) return '/';
39+
return path.endsWith('/') ? path : `${path}/`;
40+
}
41+
42+
const homePath = computed(() => `/${props.lang}/`);
43+
const isHome = computed(() => normalizePath(props.currentPath) === homePath.value);
44+
const scrollTargetKey = 'rushdb-scroll-target';
45+
46+
function sectionHref(id: string) {
47+
return isHome.value ? `#${id}` : homePath.value;
48+
}
49+
50+
function navigateToSection(id: string) {
51+
if (isHome.value) {
52+
const targetEl = document.getElementById(id);
53+
if (targetEl) {
54+
targetEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
55+
}
56+
return;
57+
}
58+
59+
sessionStorage.setItem(scrollTargetKey, id);
60+
window.location.href = homePath.value;
61+
}
62+
3763
let navHighlightObserver: IntersectionObserver | null = null;
3864
3965
function toggleLanguageSwitcher() {
@@ -73,6 +99,12 @@ function scrollToTop() {
7399
window.scrollTo({ top: 0, behavior: 'smooth' });
74100
}
75101
102+
function onLogoClick(event: MouseEvent) {
103+
if (!isHome.value) return;
104+
event.preventDefault();
105+
scrollToTop();
106+
}
107+
76108
function updateNavbarBg() {
77109
navbarBg.value = window.scrollY > 100 ? 'rgba(5, 7, 18, 0.82)' : 'rgba(5, 7, 18, 0.72)';
78110
}
@@ -93,6 +125,17 @@ function onDocumentClick(event: MouseEvent) {
93125
closeNavMenu();
94126
}
95127
128+
const sectionAnchor = target.closest<HTMLAnchorElement>('a[data-section]');
129+
if (sectionAnchor) {
130+
const sectionId = sectionAnchor.dataset.section;
131+
if (!sectionId) return;
132+
133+
event.preventDefault();
134+
closeNavMenu();
135+
navigateToSection(sectionId);
136+
return;
137+
}
138+
96139
const anchor = target.closest<HTMLAnchorElement>('a[href^="#"]:not(.nav-logo)');
97140
if (!anchor) return;
98141
@@ -150,7 +193,7 @@ onUnmounted(() => {
150193
<template>
151194
<nav class="navbar" :class="{ 'menu-open': isMenuOpen }" :style="{ background: navbarBg }" role="navigation" :aria-label="t('a11y.mainNavigation')">
152195
<div class="nav-content">
153-
<a href="#" class="nav-logo" @click.prevent="scrollToTop">
196+
<a :href="homePath" class="nav-logo" @click="onLogoClick">
154197
<img src="/RushDB.png" alt="RushDB Logo" class="nav-logo-img" />
155198
<span>&ensp;RushDB</span>
156199
</a>
@@ -159,16 +202,22 @@ onUnmounted(() => {
159202
<div class="nav-actions nav-desktop">
160203
<ul class="nav-links">
161204
<li>
162-
<a href="#about" :class="{ active: activeSection === 'about' }">{{ t('nav.about') }}</a>
205+
<a :href="sectionHref('about')" data-section="about" :class="{ active: activeSection === 'about' }">{{ t('nav.about') }}</a>
163206
</li>
164207
<li>
165-
<a href="#achievements" :class="{ active: activeSection === 'achievements' }">{{ t('nav.achievements') }}</a>
208+
<a
209+
:href="sectionHref('achievements')"
210+
data-section="achievements"
211+
:class="{ active: activeSection === 'achievements' }"
212+
>
213+
{{ t('nav.achievements') }}
214+
</a>
166215
</li>
167216
<li>
168-
<a href="#projects" :class="{ active: activeSection === 'projects' }">{{ t('nav.projects') }}</a>
217+
<a :href="sectionHref('projects')" data-section="projects" :class="{ active: activeSection === 'projects' }">{{ t('nav.projects') }}</a>
169218
</li>
170219
<li>
171-
<a href="#members" :class="{ active: activeSection === 'members' }">{{ t('nav.members') }}</a>
220+
<a :href="sectionHref('members')" data-section="members" :class="{ active: activeSection === 'members' }">{{ t('nav.members') }}</a>
172221
</li>
173222
<li>
174223
<a :href="`/${lang}/blog/`" class="nav-blog-link">{{ t('nav.blog') }}</a>
@@ -204,16 +253,29 @@ onUnmounted(() => {
204253
<div class="nav-mobile-menu" :class="{ open: isMenuOpen }">
205254
<ul class="nav-mobile-links">
206255
<li>
207-
<a href="#about" :class="{ active: activeSection === 'about' }" @click="closeNavMenu">{{ t('nav.about') }}</a>
256+
<a :href="sectionHref('about')" data-section="about" :class="{ active: activeSection === 'about' }" @click="closeNavMenu">
257+
{{ t('nav.about') }}
258+
</a>
208259
</li>
209260
<li>
210-
<a href="#achievements" :class="{ active: activeSection === 'achievements' }" @click="closeNavMenu">{{ t('nav.achievements') }}</a>
261+
<a
262+
:href="sectionHref('achievements')"
263+
data-section="achievements"
264+
:class="{ active: activeSection === 'achievements' }"
265+
@click="closeNavMenu"
266+
>
267+
{{ t('nav.achievements') }}
268+
</a>
211269
</li>
212270
<li>
213-
<a href="#projects" :class="{ active: activeSection === 'projects' }" @click="closeNavMenu">{{ t('nav.projects') }}</a>
271+
<a :href="sectionHref('projects')" data-section="projects" :class="{ active: activeSection === 'projects' }" @click="closeNavMenu">
272+
{{ t('nav.projects') }}
273+
</a>
214274
</li>
215275
<li>
216-
<a href="#members" :class="{ active: activeSection === 'members' }" @click="closeNavMenu">{{ t('nav.members') }}</a>
276+
<a :href="sectionHref('members')" data-section="members" :class="{ active: activeSection === 'members' }" @click="closeNavMenu">
277+
{{ t('nav.members') }}
278+
</a>
217279
</li>
218280
<li>
219281
<a :href="`/${lang}/blog/`" class="nav-blog-link">{{ t('nav.blog') }}</a>

src/i18n/ui.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const ui = {
2222
openMenu: '打开菜单',
2323
},
2424
hero: {
25-
title: 'RushDB无限进步',
25+
title: 'RushDB 无限进步',
2626
subtitle: '专注数据库技术的本科生竞赛团队',
2727
actions: {
2828
learnMore: '了解更多',

src/layouts/BaseLayout.astro

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ const metaDescription = description || descriptions[lang];
5959
<link rel="shortcut icon" type="image/png" href="/RushDB.png" />
6060
<link rel="apple-touch-icon" href="/RushDB.png" />
6161
<link
62-
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
62+
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Source+Serif+4:ital,opsz,wght@0,8..60,200..900;1,8..60,200..900&display=swap"
6363
rel="stylesheet"
6464
/>
6565

@@ -149,6 +149,24 @@ const metaDescription = description || descriptions[lang];
149149
document.body.appendChild(script);
150150
}
151151

152+
// Scroll to section requested from another page (keeps URL clean)
153+
function handlePendingSectionScroll() {
154+
const key = 'rushdb-scroll-target';
155+
const targetId = sessionStorage.getItem(key);
156+
if (!targetId) return;
157+
158+
const targetEl = document.getElementById(targetId);
159+
if (!targetEl) {
160+
sessionStorage.removeItem(key);
161+
return;
162+
}
163+
164+
requestAnimationFrame(() => {
165+
targetEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
166+
sessionStorage.removeItem(key);
167+
});
168+
}
169+
152170
// Initialize
153171
let scrollRaf = null;
154172
let pointerRaf = null;
@@ -174,6 +192,7 @@ const metaDescription = description || descriptions[lang];
174192
setupSectionObserver();
175193
setupMemberCardHover();
176194
loadBusuanzi();
195+
handlePendingSectionScroll();
177196
});
178197
</script>
179198
</body>

src/pages/[lang]/blog/[slug].astro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ const formattedDate = post.data.pubDate.toLocaleDateString(
7878
.post-main {
7979
max-width: 1100px;
8080
margin: 0 auto;
81+
font-family: "Source Serif 4", "Times New Roman", serif;
82+
font-optical-sizing: auto;
8183
}
8284

8385
.back-link {

src/pages/[lang]/blog/index.astro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ const titles: Record<Lang, string> = {
5656
.blog-main {
5757
max-width: 900px;
5858
margin: 0 auto;
59+
font-family: "Source Serif 4", "Times New Roman", serif;
60+
font-optical-sizing: auto;
5961
}
6062

6163
.blog-title {

0 commit comments

Comments
 (0)