去重「我的介绍」:页脚改放站点信息卡片,关于页入口只留在侧栏

「我的介绍」重复
- 页脚原来那栏和侧栏作者卡片是同一份内容(同头像、同站名、同一句签名),
  手机端首页从头刷到尾会看到两次
- 页脚那栏整栏删除,只保留侧栏作者卡片
- 空出来的左侧位置给站点信息卡片

站点信息改成卡片
- 从原来横跨全宽的裸信息带,改成和侧栏作者卡片/分类卡片同一套语言:
  bg-card + border-border/50 + rounded-xl + p-5,标题带那道红色短竖线
- 内部 2 列 × 3 行:建站时间 / 已运行 / 文章 / 偶得 / 总字数 / 最近更新
- 桌面页脚高 529 -> 476px;手机 822 -> 760px(相比最初的 957px 共省 197px)

关于页入口
- 只留在侧栏作者卡片:站名和签名两行包在同一个 <a href="/about/"> 里
- 按要求去掉所有 hover 装饰(原来有头像描边转主色、站名转主色、箭头位移),
  也不再放「关于我 →」提示,两行就是安静的文字链接
- 头像不参与:下面社交图标本身是 <a>,<a> 不能嵌套 <a>

验证(headless Chrome 131,禁用缓存)
- 页脚站点信息卡片计算样式:bg oklch(0.98 0.011 84)、border 1px …/0.5、
  radius 12px、padding 20px,与侧栏卡片一致
- 页脚内已无「时之刃的小站 / 只是将博客作为新的爱好的普通人」字样
- 侧栏 <a> href=/about/,链接文本为「时之刃只是将博客作为新的爱好的普通人!」,
  className 为空(无 hover 类);点名字跳转 /about/ 正常
- a a 嵌套数 0,无横向溢出;桌面/手机截图正常
This commit is contained in:
dsh
2026-09-15 10:05:55 +08:00
parent 0cfcbd060e
commit 056b5faf98
3 changed files with 41 additions and 64 deletions
+5 -6
View File
@@ -241,18 +241,17 @@ prose-p:leading-[1.78] md:prose-p:leading-[1.85]
| 区块 | 内容 | 显示范围 | | 区块 | 内容 | 显示范围 |
|---|---|---| |---|---|---|
| 我的介绍 | 头像 + 站名 + 签名,**整块可点去 `/about/`** | 全部 | | 站点信息卡片 | 建站时间 / 已运行 / 文章 / 偶得 / 总字数 / 最近更新,卡片样式沿用侧栏作者卡片 | 全部 |
| 最新文章 | 最近 4 篇(按发布时间倒序,不把置顶提前) | 桌面 | | 最新文章 | 最近 4 篇(按发布时间倒序,不把置顶提前) | 桌面 |
| 联系 | 社交图标 + 订阅 RSS | 全部 | | 联系 | 社交图标 + 订阅 RSS | 全部 |
| 站点信息 | 建站时间 / 已运行 / 文章 / 偶得 / 总字数 / 最近更新 | 全部(想只在桌面显示就给它加 `hidden md:grid`) |
| 底栏 | 版权 / 备案 / 主题署名 | 全部 | | 底栏 | 版权 / 备案 / 主题署名 | 全部 |
**原来的「导航」栏已删除** —— 它是顶部导航的逐项复制(9 条名字和目标完全一样),手机上白占 312px。桌面端改用「最新文章」,把重复变成发现。不影响 SEO:顶部导航和移动端菜单里的链接在每页 HTML 里都还在。 两处**刻意不放**的东西:
两点需要注意: - **「导航」栏已删除** —— 它是顶部导航的逐项复制(9 条名字和目标完全一样),手机上白占 312px。桌面端改用「最新文章」,把重复变成发现。不影响 SEO:顶部导航和移动端菜单里的链接在每页 HTML 里都还在。
- **页脚不再放「我的介绍」** —— 侧栏作者卡片已经有了,再放一份是重复。页脚那份的视觉位置让给了站点信息卡片。
- **「我的介绍」整块包 `<a>` 是合法的**,因为这一块里没有别的链接。但**侧栏的作者卡片不行** —— 它下面有社交图标(本身是 `<a>`),`<a>` 不能嵌套。所以那里只包到签名那一段。 「我的介绍 → 关于页」在**侧栏作者卡片**里(`sidebar.tsx`):站名和签名那两行包在同一个 `<a href="/about/">` 里,不做 hover 装饰。只能包这两行——下面的社交图标本身是 `<a>`,`<a>` 不能嵌套 `<a>`。`aria-label` 是必须的,否则读屏会把「站名 + 签名」念成链接名。
- 整块链接要给 `aria-label`,否则读屏会把「头像 alt + 站名 + 签名」整段念成链接名。
统计数据在 `src/lib/site-stats.ts`:文章数/偶得数/分类数/标签数/总字数在构建时算,「已运行 N 天」和「最近更新」由页脚里的内联脚本按**真实时间**刷新(站点久不重建也不会停在构建那天),服务端会先渲染一份构建时兜底值,关 JS 也不空。加减统计项改 `footer.astro` 里的 `statCells` 数组即可。 统计数据在 `src/lib/site-stats.ts`:文章数/偶得数/分类数/标签数/总字数在构建时算,「已运行 N 天」和「最近更新」由页脚里的内联脚本按**真实时间**刷新(站点久不重建也不会停在构建那天),服务端会先渲染一份构建时兜底值,关 JS 也不空。加减统计项改 `footer.astro` 里的 `statCells` 数组即可。
+20 -36
View File
@@ -1,6 +1,6 @@
--- ---
import type { ComponentType, SVGProps } from "react" import type { ComponentType, SVGProps } from "react"
import { ArrowRight, Github, Mail, Music, Rss } from "lucide-react" import { Github, Mail, Music, Rss } from "lucide-react"
import { QqIcon } from "@/components/icons/qq-icon" import { QqIcon } from "@/components/icons/qq-icon"
import { profile, type ExternalProfileLink, type ExternalProfileLinkType } from "@/data/profile" import { profile, type ExternalProfileLink, type ExternalProfileLinkType } from "@/data/profile"
import { site } from "@/data/site" import { site } from "@/data/site"
@@ -32,30 +32,27 @@ const statCells = [
<div class="max-w-6xl mx-auto px-6 py-16"> <div class="max-w-6xl mx-auto px-6 py-16">
<div class="grid gap-12 md:grid-cols-3"> <div class="grid gap-12 md:grid-cols-3">
{/* {/*
我的介绍:整块可点去关于页(这里没有嵌套链接,所以整块包 <a> 是合法的)。 站点信息卡片:沿用侧栏作者卡片/分类卡片的语言
aria-label 是为了不让读屏把头像 alt + 站名 + 签名全念成链接名。 (bg-card + border/50 + rounded-xl + 红色短竖线标题)。
「我的介绍」不在这里重复展示 —— 侧栏作者卡片已经有了。
*/} */}
<a href="/about/" class="group block" aria-label={`关于 ${profile.name}`}> <div class="bg-card border border-border/50 rounded-xl p-5">
<div class="flex items-center gap-3 mb-4"> <div class="flex items-center gap-2 mb-4">
<div class="w-8 h-8 rounded-full overflow-hidden border border-primary/20 bg-secondary transition-colors duration-200 group-hover:border-primary/60"> <div class="w-1 h-4 bg-primary rounded-full" />
<img <h4 class="font-medium text-foreground">站点信息</h4>
src={profile.avatar} </div>
alt={profile.name} <div class="grid grid-cols-2 gap-x-4 gap-y-4">
loading="lazy" {statCells.map((cell) => (
decoding="async" <div>
class="w-full h-full object-cover" <div class="text-xs text-muted-foreground mb-1">{cell.label}</div>
/> <div class="text-sm text-foreground font-medium tabular-nums">
<span data-stat={cell.live}>{cell.value}</span>
{cell.unit && <span class="text-xs text-muted-foreground ml-0.5">{cell.unit}</span>}
</div>
</div>
))}
</div> </div>
<span class="text-foreground font-medium transition-colors duration-200 group-hover:text-primary">
{site.name}
</span>
</div> </div>
<p class="text-muted-foreground text-sm leading-relaxed">{profile.bio}</p>
<span class="mt-3 inline-flex items-center gap-1 text-xs text-muted-foreground transition-colors duration-200 group-hover:text-primary">
关于我
<ArrowRight class="w-3 h-3 transition-transform duration-200 group-hover:translate-x-0.5" />
</span>
</a>
{/* {/*
最新文章:手机端上面就是文章列表,再列一遍没意义,所以只在 md 以上显示。 最新文章:手机端上面就是文章列表,再列一遍没意义,所以只在 md 以上显示。
@@ -109,21 +106,8 @@ const statCells = [
</div> </div>
</div> </div>
{/* 站点信息:桌面 6 列一行,手机 3 列两行 */}
<div class="mt-14 pt-8 border-t border-border/50 grid grid-cols-3 gap-x-4 gap-y-6 md:grid-cols-6">
{statCells.map((cell) => (
<div>
<div class="text-xs text-muted-foreground mb-1.5">{cell.label}</div>
<div class="text-foreground font-medium tabular-nums">
<span data-stat={cell.live}>{cell.value}</span>
{cell.unit && <span class="text-xs text-muted-foreground ml-0.5">{cell.unit}</span>}
</div>
</div>
))}
</div>
{/* Bottom */} {/* Bottom */}
<div class="mt-10 pt-8 border-t border-border/50 grid gap-4 md:grid-cols-3"> <div class="mt-14 pt-8 border-t border-border/50 grid gap-4 md:grid-cols-3">
<p class="text-muted-foreground text-sm"> <p class="text-muted-foreground text-sm">
© {site.startDate.slice(0, 4)} {profile.name}. All rights reserved. © {site.startDate.slice(0, 4)} {profile.name}. All rights reserved.
</p> </p>
+8 -14
View File
@@ -2,7 +2,7 @@
import { useState, type ReactNode } from "react" import { useState, type ReactNode } from "react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { ArrowRight, Calendar } from "lucide-react" import { Calendar } from "lucide-react"
import { profile } from "@/data/profile" import { profile } from "@/data/profile"
import { ProfileContactLink } from "@/components/profile-contact-link" import { ProfileContactLink } from "@/components/profile-contact-link"
@@ -39,29 +39,23 @@ export function Sidebar({
return ( return (
<aside className="w-full lg:w-72 shrink-0 space-y-8"> <aside className="w-full lg:w-72 shrink-0 space-y-8">
{/* Author Card —— 头像/站名/签名整块可点,跳关于页。 {/* Author Card —— 站名和签名两行各自可点去关于页,不做 hover 装饰。
注意只能包到这里:下面的社交图标本身是 <a>,<a> 不能嵌套 <a>。 头像不参与:整块包 <a> 会把下面的社交图标(本身是 <a>)套进去,
aria-label 是为了不让读屏把头像 alt + 站名 + 签名整段念成链接名。 */} <a> 不能嵌套 <a>,所以只包名字 + 分隔线 + 签名这一小段。 */}
<div className="bg-card border border-border/50 rounded-xl p-6 text-center"> <div className="bg-card border border-border/50 rounded-xl p-6 text-center">
<a href="/about/" className="group block" aria-label={`关于 ${profile.name}`}> <div className="w-24 h-24 mx-auto mb-4 rounded-full overflow-hidden border-2 border-primary/20">
<div className="w-24 h-24 mx-auto mb-4 rounded-full overflow-hidden border-2 border-primary/20 transition-colors duration-200 group-hover:border-primary/60">
<img <img
src={profile.avatar} src={profile.avatar}
alt={profile.name} alt={profile.name}
loading="lazy" loading="lazy"
decoding="async" decoding="async"
className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105" className="w-full h-full object-cover"
/> />
</div> </div>
<h3 className="font-serif text-xl font-bold text-foreground mb-1 transition-colors duration-200 group-hover:text-primary"> <a href="/about/" aria-label={`关于 ${profile.name}`}>
{profile.name} <h3 className="font-serif text-xl font-bold text-foreground mb-1">{profile.name}</h3>
</h3>
<div className="w-8 h-0.5 bg-primary mx-auto mb-3" /> <div className="w-8 h-0.5 bg-primary mx-auto mb-3" />
<p className="text-sm text-muted-foreground leading-relaxed">{profile.bio}</p> <p className="text-sm text-muted-foreground leading-relaxed">{profile.bio}</p>
<span className="mt-2 inline-flex items-center gap-1 text-xs text-muted-foreground transition-colors duration-200 group-hover:text-primary">
关于我
<ArrowRight className="w-3 h-3 transition-transform duration-200 group-hover:translate-x-0.5" />
</span>
</a> </a>
{/* Social Links */} {/* Social Links */}